Flexbox
Normal flow stacks blocks down the page, but a lot of interface runs across it: a navigation bar, a row of buttons, a card's footer with a date on the left and a link on the right. For years these took fragile tricks. Flexbox, short for the flexible box layout, is the CSS tool built for them: it arranges a group of elements in one direction, a row or a column, and handles the spacing and alignment between them.
Books on a shelf are a good model. You decide which end of the shelf they lean towards, how far apart they stand, and whether the ones that do not fit go onto a second shelf. Flexbox gives you exactly those decisions for boxes, and the rest of this page uses its real terms.
Container and Items
Flexbox always involves two levels. You write display: flex on a parent element, which becomes the flex container. Its direct children become flex items, and they are the things that get arranged. Grandchildren are not affected; they carry on in normal flow inside their own item.
<div class="row"> <div class="box">One</div> <div class="box">Two</div> <div class="box">Three</div> </div>
.row {
display: flex;
}
.box {
padding: 1rem;
border: 1px solid black;
}
Without the display: flex line, the three boxes are ordinary blocks: three full-width bars stacked down the page. With it, they sit side by side in a row at the left, each only as wide as its word, and all three the same height. Add flex-direction: column to the .row rule and they stack again, each stretched to the full width. It looks like normal flow, but it is a flex column, and the alignment tools below now work on it.
Two Axes
A flex container has two directions that matter. The main axis is the direction the items are laid out in: left to right in a row, top to bottom in a column. The cross axis runs at right angles to it. Two properties align the items, one per axis.
justify-content places the items along the main axis. Its values include flex-start, center, flex-end, and space-between, which puts the first item at the start, the last at the end, and shares the leftover space between them. align-items places the items across the cross axis: center, flex-start, flex-end, or the default, stretch, which is why the boxes in a row all came out the same height.
This is the simplified picture for English pages. In a language written right to left, a row runs right to left, and "start" means the right-hand side. The properties keep working; only the direction turns round.
Centring something both ways, a famous old headache, takes two alignment lines. The container needs some height of its own, or there is no room to centre in.
.stage {
display: flex;
justify-content: center;
align-items: center;
min-height: 20rem;
border: 1px dashed gray;
}
Put one of the boxes from before inside an element with the class stage. The browser shows a dashed area at least 20 rem tall with the box sitting exactly in its middle, horizontally and vertically. Shrink the window and it stays in the middle.
Spacing and Wrapping
The gap property puts space between the items and nowhere else: not before the first, not after the last. Margins can do the same job, but they add space at the edges too and need a special case for the last item. gap has no edge cases.
By default a flex row never breaks onto a second line. If the items do not fit, they shrink, and once they cannot shrink any further they spill out of the container. flex-wrap: wrap lets the row continue on the next line instead, like the second shelf.
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
list-style: none;
padding: 0;
}
.tags li {
padding: 0.25rem 0.75rem;
border: 1px solid black;
}
Apply this to a list with the class tags and six short items, such as HTML, CSS and JavaScript. The two lines about the list remove its bullets and its default indent. On a wide screen the six tags sit in one row with half a rem between them. Narrow the window and the tags that no longer fit move down to a second row, with the same gap between the rows as between the tags.
Sharing the Space
Items can also grow to fill the room. flex: 1 on an item tells it to take a share of the row's space, and when every item has it, they get equal shares: three buttons with flex: 1 become three buttons of the same width across the whole row, as long as their text fits.
The opposite job, pushing one item away from the others, uses a margin set to auto. In a flex row, an automatic margin swallows all the free space on its side.
.toolbar {
display: flex;
gap: 0.5rem;
}
.toolbar .help {
margin-left: auto;
}
Give a toolbar three buttons, Bold, Italic and Help, with the class help on the last. Bold and Italic sit together at the left, and Help moves all the way to the right-hand end, because its left margin takes up everything in between.
The Site's Header
Now the site. First, on each of your three pages, add your name at the start of the header, as a link to the Home page, just before the nav element. The header then holds two things: your name and the nav element with its list of three links. The goal is the name on the left, the links in a row on the right, and on a narrow phone the links dropping to a line of their own rather than being squeezed.
<header class="site-header"> <a href="index.html">Your Name</a> <nav class="site-nav"> <ul> <li><a href="index.html">Home</a></li> <li><a href="projects.html">Projects</a></li> <li><a href="contact.html">Contact</a></li> </ul> </nav> </header>
.site-header {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
gap: var(--space-m);
padding: var(--space-m);
}
.site-nav ul {
display: flex;
flex-wrap: wrap;
gap: var(--space-m);
list-style: none;
margin: 0;
padding: 0;
}
There are two flex containers here. The header arranges its two children, the name and the nav, with space-between pushing them to opposite ends and align-items: center lining them up vertically. The list inside the nav is a second container that puts the three links in a row, with the spacing token from Chapter 3 between them. Add both rules to css/style.css and all three pages change at once, because they share the stylesheet.
Narrow the window and watch. When the name and the links no longer fit on one line, the nav wraps below the name and, alone on its line, lines up at the left. In DevTools, the Elements panel shows a small "flex" badge next to each flex container; click it and the page draws the items and the gaps between them.
- "
display: flexgoes on the things I want in a row." It goes on their parent. The parent becomes the container, and its direct children become the items that line up. - "
justify-contentalways means horizontal." It follows the main axis. In a row that is horizontal; in a column it is vertical, andalign-itemstakes over the horizontal job. - "Margins are the way to space out items."
gapputs space only between items, with no extra space at the edges and no special rule for the last one.
- Flexbox is the everyday tool for the small parts of an interface: bars, button groups, rows of controls, anything laid out in a line.
- A wrapping row adapts to narrow screens on its own. The site's header now works on a phone without a single rule written for phones.
Knowledge Check
You want three links in a row. Where does display: flex go?
- On each of the three links, one rule per link
- On the element that contains the three links
- On the body element, so the whole page is flex
- On the first link only; the others follow it
In a container with flex-direction: column, what does justify-content: center do?
- Centres the items from left to right
- Puts the middle item first in the order
- Centres the items from top to bottom
- Nothing, because it only works in rows
What does gap do in a flex container?
- Adds space around the outside edge of the container
- Makes every item the same width as the others
- Replaces the margins inside every item with padding
- Puts space between neighbouring items, and nowhere else
A flex row of tags is wider than a phone screen. What does flex-wrap: wrap change?
- The tags that no longer fit move down onto a new line
- The tags shrink until they all fit on one line
- The tags that do not fit are hidden from view entirely
- The row spills out past the edge of the screen
You got correct