One Dimension, Solved
Flexbox exists because centring a box vertically used to require a genuine trick. Set four properties, watch a row become a column and back, and learn which axis each property is actually talking about.
Worth reading first: Block, Inline, and the Normal Flow
Flexbox exists because centring a box vertically used to require a genuine trick. Set four properties, watch a row become a column and back, and learn which axis each property is actually talking about.
display: flex changes the children, not the parent
One declaration on a container, and every direct child becomes a flex item — laid out in a row, side by side, in a way normal flow never would.
.nav { display: flex;}The container itself is still a block: full width, on its own line. What changed is how it arranges its children.
Grandchildren are unaffected — they are laid out by their own parent. Flex is one level deep, always.
Including inline elements. A span inside a flex container stops being inline and starts respecting width and height.
By default. Even if they are all block elements that would otherwise stack.
Rather than overflowing. This is flexbox's namesake behaviour and it is why a nav bar does not break the layout.
.container { display: flex; flex-direction: row; justify-content: flex-start; align-items: stretch; gap: 12px; }
Right now justify-content moves things horizontally and align-items moves them vertically. Switch direction and they swap — the properties did not change meaning, the axes did. That is the entire mental model, and it is why memorising “justify is across, align is down” fails the first time you build a column.
The main axis is whichever direction you chose
This is the concept the whole system rests on, and it is where most confusion comes from. Flexbox has two axes, and which is which depends on flex-direction.
flex-direction: row; /* default — main axis is horizontal */flex-direction: column; /* main axis is now VERTICAL */flex-direction: row-reverse; /* right to left */flex-direction: column-reverse; /* bottom to top */flex-direction: row
Main axis horizontal, cross axis vertical. justify-content moves things left and right; align-items up and down.
flex-direction: column
Main axis vertical, cross axis horizontal. The same two properties now do the opposite: justify-content moves things up and down.
justify-content runs along the main axis
justify-content distributes items along the main axis, and it only has anything to do when there is spare space.
Packed at the start. The default.
Packed in the middle.
Packed at the end.
First at the start, last at the end, gaps equal. The classic nav bar: logo left, links right.
Equal space around each item, so edge gaps are half the size of the gaps between.
Every gap identical, including the edges. Usually what people mean when they reach for space-around.
.header { display: flex; justify-content: space-between; align-items: center;}/* <header class="header"><a>Logo</a><nav>…</nav></header> */align-items runs across it
align-items positions items on the cross axis — perpendicular to the main one.
The default. Items fill the cross axis, which is why boxes in a row all come out the same height without you asking. Usually what you want.
Centred on the cross axis. Half of the famous centring problem.
Packed to one edge of the cross axis.
Text baselines line up, regardless of box size. Right for a row of items with different font sizes.
.centre { display: flex; justify-content: center; /* centred on the main axis */ align-items: center; /* centred on the cross axis */ min-height: 100vh;}That is the problem flexbox was invented for. It used to require absolute positioning and a negative-margin trick that depended on knowing the box's size in advance, and now it is two declarations that work whatever the content is.
gap replaced margins between children
gap puts space between flex items and none on the outside edges. Before it, everybody wrote a margin on every child and then a rule removing it from the last one.
/* The old way — and you had to remember the second rule */.item { margin-right: 1rem; }.item:last-child { margin-right: 0; } /* Now */.list { display: flex; gap: 1rem; }Two more properties finish the picture. flex-wrap lets items move onto a new line instead of shrinking indefinitely — and the combination below is a genuinely responsive layout with no media query.
.cards { display: flex; flex-wrap: wrap; gap: 1.5rem;} .card { flex: 1 1 250px; /* grow | shrink | ideal width */}flex: 1 1 250px is three values in one: may grow to fill spare space, may shrink if there is not enough, and would ideally be 250 pixels. Cards fill the row, wrap when they cannot fit, and share the leftover space — at any screen size.
Shorthand for 1 1 0%: take an equal share of the space. Two items with flex: 1 are exactly half each.
1 1 auto: grow and shrink, but start from the content's own size. Bigger items get more room.
0 0 auto: never grow, never shrink. For something that must keep its size, like a logo.
On one item, this absorbs all the spare space and pushes that item to the end. The idiomatic way to send one link to the right of a nav.
Key takeaways
- display: flex on a container makes its DIRECT children flex items. Grandchildren are unaffected.
- The container stays a block; only the arrangement of its children changes.
- Flexbox has a main axis and a cross axis, and flex-direction decides which is which.
- justify-content follows the main axis; align-items crosses it. They swap when you switch to column.
- Memorising "justify is horizontal" is true half the time and breaks on your first column.
- space-between is the nav bar: first at the start, last at the end.
- align-items: stretch is the default and is why boxes in a row come out equal height.
- justify-content: center plus align-items: center is the centring that used to need a trick.
- align-self overrides align-items for a single item.
- gap spaces items with nothing on the outside, replacing margin-on-every-child-except-the-last.
- flex-wrap plus flex: 1 1 250px is a responsive card grid with no media query.
- margin-left: auto on one item pushes it to the end of the row.
Quick check
Answer these to unlock the next chapter — 3 of 4 to pass. You can retake it anytime.
Answer every question to check.
Make a free account to read on
Every chapter is free — an account is how your progress, XP, and streak follow you from your laptop to your phone, and how you show up on the leaderboard. No payment, no trial.