← back to the section

Flexbox is the workhorse layout of the modern web: toolbars, menus, cards, forms, "push the button to the right", "center this" — all of it is flexbox. The model is one-dimensional: the container lays out its children along a single axis and can flexibly distribute space between them. The key to understanding it is the axes and the three flexibility properties.

Container, axes, and direction

display: flex on a container enables the layout for its direct children:

.toolbar { display: flex; }

Children line up along the main axis; perpendicular to it is the cross axis. Where the main axis points is set by flex-direction: row (default, left to right), column (top to bottom), plus the reversed variants. Every property that follows is tied to the axes, not to the sides of the screen — change the direction, and "horizontal" alignment becomes vertical. That's not a quirk but a strength: the same logic lays out both a toolbar row and a sidebar column.

By default children don't wrap and are allowed to shrink; flex-wrap: wrap permits wrapping onto new lines — that's how fluid galleries of tags and cards are built.

Alignment: justify vs align

The pair that gets confused every time differs by axis:

  • justify-content — distribution along the main axis: flex-start, center, flex-end, space-between (edge items against the edges, the rest split evenly between), space-around, space-evenly.
  • align-items — alignment along the cross axis: stretch (default — stretch to the row height), center, flex-start, flex-end, baseline (along the text baseline — for rows with different font sizes).
.header {
  display: flex;
  justify-content: space-between; /* logo to the left, menu to the right */
  align-items: center;            /* everything vertically centered */
}

Perfect centering, which the web wrestled with for a decade, is three lines: display: flex; justify-content: center; align-items: center.

Precision tools: align-self overrides align-items for a single child; margin-left: auto on a child eats all the free space on its left — the idiom for "push the last item to the opposite edge" without wrappers. order changes the visual order without touching the markup (careful: tab order and screen reader order still follow the DOM — the mismatch hurts accessibility).

Spacing between children is the gap property: honest gaps only between items, without margin hacks and without "eating" the outer edges. Together with flex-wrap it also sets the spacing between lines.

Flexibility: grow, shrink, basis

The heart of flexbox is how children divide space. Each child has a trio of properties:

  • flex-basis — the starting size along the main axis (before distribution); auto — from the content.
  • flex-grow — the share of free space the child takes (0 — doesn't grow).
  • flex-shrink — willingness to shrink when space runs out (1 by default — everyone shrinks).

They're written with the shorthand flex: grow shrink basis. Three presets cover 95% of cases:

.item-a { flex: none; }    /* 0 0 auto — rigid: sized by content, doesn't grow, doesn't shrink */
.item-b { flex: auto; }    /* 1 1 auto — from content, extra space shared proportionally */
.item-c { flex: 1; }       /* 1 1 0   — EQUAL columns: start from zero, split everything */

The difference between flex: auto and flex: 1 is the basis: auto divides only the free space (whoever was bigger by content stays bigger), while flex: 1 zeroes out the starting sizes and divides everything — the columns come out strictly equal. The classic — "the input takes everything, the button takes its own size":

.search { display: flex; gap: 8px; }
.search input { flex: 1; }
.search button { flex: none; }

Overflow and min-width: 0

The most famous flexbox gotcha. A long unbroken string (a URL, code) sits inside a flex: 1 column — and the column refuses to shrink, breaking the layout. The reason: flex children have min-width: auto — "no narrower than my content". The cure — allow shrinking:

.column { flex: 1; min-width: 0; }
.column .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

Without min-width: 0, ellipsis text truncation inside flex columns doesn't work either — now you know why.

In short

  • Flex is a one-dimensional layout: a main axis (direction) and a cross axis. All properties are tied to the axes, not the sides of the screen.
  • justify-content — along the main axis, align-items — across it. Centering anything is three lines.
  • The flexibility trio: basis (start), grow (share of free space), shrink (shrinkability). flex: 1 — equal columns, flex: none — a rigid item, flex: auto — content-sized with proportional extra.
  • gap instead of margin hacks; margin-left: auto — push an item to the edge; order changes only the visual order (accessibility!).
  • Not shrinking / ellipsis not working — set min-width: 0 on the flex child.
  • Grid — two-dimensional layout: when there are many rows and columns and they're related.
  • Positioning — the previous topic in the series.
  • The box model — why margins don't collapse inside a flex container.