The two-axis model
How many times have you fought to center something vertically or evenly space a row of elements? Flexbox exists for exactly that: aligning and distributing anything without the struggle. And vertical centering, that old headache, becomes a couple of lines.
When you apply display: flex to an element, it becomes a
flex container and its direct children become flex items. What sets
flexbox apart from everything before it is that it reasons in terms of two
axes, not absolute rows and columns:
- Main axis: the direction in which the items are placed.
- Cross axis: the one perpendicular to the main axis.
The key concept: justify-content aligns along the main axis and
align-items aligns along the cross axis. Which axis is which is decided by
flex-direction.
flex-direction: who is the main axis
.container {
display: flex;
flex-direction: row; /* default: horizontal main axis → */
}
With row the main axis goes from left to right, so
justify-content distributes horizontally and align-items aligns
vertically. With column the roles are swapped:
.container {
display: flex;
flex-direction: column; /* vertical main axis ↓ */
}
Now justify-content distributes vertically and align-items
horizontally. ⚠️ CLASSIC TRAP: mixing up the two axes is the number one
flexbox mistake. Don't memorize "justify = horizontal", memorize "justify = main
axis" and let flex-direction decide which axis that is.
There are also
row-reverseandcolumn-reverse, which invert the visual order of the items without touching the HTML.
justify-content: distribute the main axis
justify-content decides what to do with the leftover space on the main
axis:
.bar {
display: flex;
justify-content: space-between; /* ends pushed apart, gap in the middle */
}
| Value | Effect |
|---|---|
flex-start |
Items at the start (default). |
center |
Items grouped in the center. |
flex-end |
Items at the end. |
space-between |
First and last pushed to the edges; equal gaps. |
space-around |
Equal gap around each item (half at the edges). |
space-evenly |
Exactly equal gaps, including at the edges. |
align-items: align the cross axis
.container {
display: flex;
align-items: center; /* centers the items on the cross axis */
}
Its default value is stretch: the items are stretched to fill the whole
height of the container (in a row). That's why, if you set display: flex on a
row of cards with different content, they all end up with the same height "for
free", with no need to match them up by hand. Other values: flex-start,
flex-end, baseline.
flex-wrap: allow multiple lines
By default the items don't wrap: they shrink to fit. With
flex-wrap: wrap you allow them to move to a new line when they don't fit:
.gallery {
display: flex;
flex-wrap: wrap; /* nowrap (default) | wrap | wrap-reverse */
}
gap: spacing without margins
gap defines the space between items without adding it at the edges,
avoiding the old margin hacks with "the last one without margin":
.container {
display: flex;
gap: 16px; /* same horizontal and vertical gap */
gap: 8px 24px; /* row column: 8px between rows, 24px between columns */
}
With these six container properties you already solve most of your day-to-day layouts. The fine control lives in the items, and that's exactly what comes next.