"Flexbox is one-dimensional. Pick the axis you care about, and Flexbox makes everything along that axis behave."
What "One-Dimensional" Actually Means
Flexbox arranges its children along a single axis at a time — either a row or a column. The children can wrap to additional lines, but conceptually you're always thinking about "a line of items." Compare this to Grid, which thinks in rows AND columns simultaneously.
This matters for the mental model: before you write Flexbox, decide whether the layout is fundamentally a row or a column. If you can answer that cleanly, Flexbox is the right tool. If you need precise control over both rows AND columns at once, you want Grid.
The Two Axes (And Why Their Names Matter)
Flexbox names alignment by axis, not by direction:
- Main axis — the axis the children flow along. Determined by
flex-direction. - Cross axis — perpendicular to the main axis.
Default flex-direction: row → main is horizontal (left-to-right in LTR), cross is vertical. Set flex-direction: column → main becomes vertical, cross becomes horizontal. The properties don't change names; their effective direction does.
This is why justify-content (acts on main) and align-items (acts on cross) work the same in both row and column flex containers — the axis they refer to swaps automatically.
The Four flex-direction Values
row— default. Main axis = inline direction (LTR: left-to-right).row-reverse— main axis reverses. Items flow right-to-left (in LTR).column— main axis = block direction (top-to-bottom).column-reverse— main axis reverses. Items flow bottom-to-top.
The -reverse variants are useful for: "newest first" chat lists (column-reverse keeps the latest at the bottom of source order but visually at the top), or RTL-aware layouts that need explicit reversal.
Wrapping: flex-wrap
By default, flex items try to fit on one line, even if they overflow the container. flex-wrap: wrap allows them to wrap to multiple lines when they don't fit:
The flex-flow Shorthand
flex-flow: row wrap sets flex-direction and flex-wrap in one declaration. Convenient when you're using both.
Gap: The Modern Spacing Property
gap: 1rem on a flex container puts 1rem of space between every pair of children. row-gap and column-gap control the two axes independently. This replaced the old margin-based spacing patterns (negative margins on the container, margins on every child except the last) entirely.
gap for flex spacing. Reserve margin for individual element offsets. gap doesn't cause margin collapse, works regardless of which child is first or last, and stays correct under inserts/removes. Two lines of CSS replace what used to be a dozen.
"But what about older browsers?" — gap on flex is supported in all browsers shipped after late 2021. By 2026, the question doesn't come up.
Decision Tree: Flexbox or Grid?
- A row of buttons, navbar items, chips — Flexbox. One dimension. Wrap allowed.
- A stack of cards, a sidebar with sections — Flexbox column.
- Header above main above footer — Flexbox column, main with
flex: 1. - A photo grid where each row has a specific number of columns — Grid.
- A magazine-style layout with a sidebar, main, header, footer at specific positions — Grid (with template-areas).
- Equal-width columns that share leftover space — Flexbox with
flex: 1on each. - Columns of specific widths (e.g., a 240px sidebar + flexible main) — Either works; Grid
grid-template-columns: 240px 1fris the tightest.
What Flexbox Won't Do (And Why It's OK)
Flexbox doesn't align items along two axes at once with precision. If you want item A in row 2, column 3 and item B in row 1, column 1 of the same container, that's Grid. Flexbox arranges items along the main axis in source order; the cross-axis alignment is for the whole line, not individual placement.
Flexbox also doesn't have explicit row/column "tracks" the way Grid does. Items wrap when they overflow, but you can't say "this item goes in row 3." If you find yourself wanting that, you want Grid.
Pippa's Note
flex: 1 in the middle (grows to fill available height, scrolls when content overflows), composer at the bottom (auto height), header at the top. Three children, one container, one direction. The conversation sidebar is a Flexbox column with conversation rows. Every horizontal arrangement (action button rows, tag chips, the brain picker in council) is a Flexbox row with gap. Almost every UI surface in the WebUI is a flex tree — Grid is reserved for the few page-level layouts where two-dimensional precision earns its keep.