Lay out a navigation bar and a row of cards with Flexbox: direction, alignment, gap and wrapping.
Why this matters
Before Flexbox, putting three boxes side by side and centring them vertically took floats, clearfix hacks and guesswork. Flexbox makes one-dimensional layout (a row or a column of items) simple and predictable. Navigation bars, toolbars, card rows, button groups and "centre this thing" are all Flexbox jobs you will meet every week.
The core idea: a container and its items
You turn an element into a flex container with one declaration. Its direct children become flex items:
.nav-list {
display: flex;
}
<ul class="nav-list">
<li><a href="#routes">Routes</a></li>
<li><a href="#events">Events</a></li>
<li><a href="#join">Join</a></li>
</ul>
The three list items now sit in a row. Only the direct children are affected; the links inside the items are laid out normally.
Two axes
A flex container has a main axis and a cross axis.
flex-direction: row(the default) makes the main axis horizontal.flex-direction: columnmakes the main axis vertical.
Alignment properties are named after the axes, not after left/right or top/bottom, which is why they swap meaning when you change direction:
| Property | Set on | Works along | Common values |
|---|---|---|---|
justify-content | container | main axis | flex-start, center, space-between, flex-end |
align-items | container | cross axis | stretch (default), center, flex-start, flex-end |
gap | container | between items | 1rem, 0.5rem 1rem |
flex-wrap | container | lines | nowrap (default), wrap |
Worked example 1: a header bar
Logo on the left, navigation on the right, both centred vertically:
.site-header {
display: flex;
justify-content: space-between; /* push the two children apart */
align-items: center; /* centre them vertically */
}
.nav-list {
display: flex;
gap: 1rem; /* space between links, none at the ends */
list-style: none; /* remove the bullets */
margin: 0;
padding: 0;
}
Flexbox containers can be nested: the header is a flex container whose second child (the nav's list) is a flex container itself.
gap is the modern way to space items. It puts space only between items, so you no longer need margins that leave an awkward extra gap at the end.
Worked example 2: cards that wrap
.cards {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.card {
flex: 1 1 200px;
}
flex: 1 1 200px is shorthand for three item properties:
flex-grow: 1: if there is spare space, take a share of it.flex-shrink: 1: if space is tight, you may shrink.flex-basis: 200px: start from a width of about 200px before growing or shrinking.
With flex-wrap: wrap, cards that do not fit move to the next line, and each line's cards grow to fill it. On a wide screen you get three cards in a row; on a phone, one per row. No media query needed.
Centring anything
The classic "centre a box both ways" problem becomes:
.hero {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
}
Ordering, and why to be careful
order and flex-direction: row-reverse change the visual order without changing the HTML. Keyboard focus and screen readers still follow the HTML order, so a visual order that differs from the source confuses keyboard users. Change the HTML order instead whenever you can.
Common mistakes
- Putting
display: flexon the item instead of the parent. - Expecting grandchildren to be flex items. Only direct children are.
- Mixing up the axes after switching to
flex-direction: column(nowjustify-contentis vertical). - Forgetting
flex-wrap: wrap, so items squash or overflow instead of wrapping. - Using margins for spacing where
gapwould be simpler.
Quick recap
display: flexon the parent; direct children become flex items.justify-contentaligns along the main axis,align-itemsacross it;gapspaces items.flex-wrap: wrapplusflex: 1 1 <basis>gives responsive card rows.- Keep the HTML order and the visual order the same.
Try it
Open the Do tab and use Flexbox to turn a stacked header and cards into a proper nav bar and a wrapping card row.