Flexbox
One dimension, one line of CSS: lay things in a row or a column, spread them out, and space them evenly.
A row, declared
Blocks stack by default, each taking the full width. display: flex on a parent turns its children into a row — and that is the whole trick: the container decides the direction, the children just line up.
From there, three properties cover most layouts. flex-direction: column stacks them instead. justify-content spreads them along the row — space-between pushes the first and last to the edges. align-items: center lines them up across it, which is what finally removes the old vertical-align guesswork.
Turning on flex changes more than direction. The children stop being ordinary blocks: their margins no longer collapse, their widths become a starting point rather than a rule, and they stretch to the height of the tallest item by default. That last default is why a row of cards with different amounts of text ends up the same height without any extra rule.
A toolbar in three lines
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}Gap, not margins
Before gap, spacing a row meant margins on the children and a rule to remove the last one. gap says the distance between items once, and the ends stay flush — which is exactly what a row of buttons wants.
Gap works in flexbox, where it was added later, and in grid, where it was there from the start. Either way it is the spacing that belongs to the container rather than to each child.
Growing and shrinking
Children can be told how to share the space: flex: 1 on one of them takes what is left over after the others have their size. That is how a search field fills the gap between a logo and a button, and how a card’s foot keeps its button at one end.
Reach for flexbox when the layout is a line — a navbar, a button row, a card where one part grows. When it is a true two-dimensional grid, reach for grid instead.
The flex shorthand, spelled out
flex: 1 is shorthand for three properties: flex-grow, flex-shrink and flex-basis. The first says how much of the leftover space this item should take, the second says whether it may shrink when space is short, and the third is the size it starts from before either happens. flex: 1 means “start at zero, grow into the spare space, and shrink if you must”, which is why it fills a row so reliably.
The long form is worth knowing because the shorthand can surprise you. flex: 1 sets the basis to 0, not auto, so an item with a long word still has that word as its minimum unless you lower it with min-width: 0. And flex: none means “do not grow, do not shrink, use my size” — the right choice for an icon that should keep its dimensions while the field beside it takes the room.
Two axes, two sets of properties
A flex container has a main axis and a cross axis. flex-direction: row makes the main axis horizontal; column makes it vertical. justify-content always works along the main axis, and align-items always works across it — so switching direction swaps what each property does.
That is why justify-content: center centres a row but does not centre a column’s height in the way people expect. Decide the direction first, then read the properties as “along” and “across”, and the layout stops being a memory test.
Wrapping and the minimum size
A flex row does not wrap unless you say flex-wrap: wrap. Without it, the children shrink until they cannot, and then they overflow the container. With it, the row breaks into lines and align-content starts to matter for the space between those lines.
The shrink floor is the trap. A flex item’s automatic minimum is its content, so a long word or an input refuses to get narrower than its text no matter what flex-shrink says. min-width: 0 on the item — or overflow-wrap: anywhere — lets it give way.
Three flexbox mistakes: putting justify-content on the children instead of the container; expecting flex: 1 to shrink a child below a long unbreakable word, when its automatic min-width is the real floor; and adding gap and child margins together, which doubles the space between items.
Try these next
- Make a card a flex column with the button pushed to the bottom, first with
margin-top: auto, then with a growing body. - Put a long URL in a flex child and fix the overflow with
min-width: 0. - Change
flex-wrapon a row of tags and compare the two layouts at a narrow width.
Line up the toolbar
The words and the two buttons are stacked blocks. Make the toolbar a single row with the words at one end and the buttons at the other, with an even gap between everything.
3 left to do
- The toolbar is a flex row
- The ends are pushed apart
- The children have an even gap
display: flex; on .toolbar, then align-items: center;, justify-content: space-between; and gap: 1rem;.
A card whose foot stays down
Make each card a column: heading and text at the top, the button at the bottom, however short the text. The two cards in the row should still end up the same height.
2 left to do
- Each card lays its children out vertically
- The paragraph takes the spare height
display: flex; flex-direction: column; on .card, then flex: 1; on the paragraph so it takes the spare height and pushes the button down.
If the items sit side by side but refuse to spread, check that justify-content is on the container — the parent holds the layout, not the children.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does display: flex on an element do?
