← back to the section

Flexbox lays things out along a single axis; Grid controls both at once — rows and columns as one grid. It's a "top-down" layout: first you describe the grid, then you place elements into it. Page skeletons, galleries, multi-column forms, dashboards — grid's home turf.

Tracks and the fr unit

.layout {
  display: grid;
  grid-template-columns: 240px 1fr;  /* sidebar and fluid content */
  grid-template-rows: auto 1fr auto; /* header, growing middle, footer */
  gap: 16px;
}

grid-template-columns/rows describe tracks — columns and rows. Sizes can be anything: pixels, percentages, auto (by content), and the big newcomer — fr: a share of the remaining space. Fixed tracks and gap are handed out first, the remainder is divided by fr shares: 1fr 2fr — "in a 1:2 ratio". The container's children place themselves into cells left to right, top to bottom; rows not described in the template are created implicitly (their height is set by grid-auto-rows).

Repetition is folded up by repeat(): repeat(12, 1fr) — the classic 12-column grid in one declaration.

Auto grid: responsiveness without media queries

The most famous line in grid:

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}

It reads: "as many columns as fit (auto-fit), each no narrower than 220px and stretching to an equal share (minmax)". The grid recalculates the number of columns for the screen width by itself: one on a phone, five on a monitor — not a single media query. For card galleries this is the default solution. (Its sibling auto-fill differs when there aren't enough items: it keeps the empty columns, while auto-fit stretches the filled ones.)

Placement: lines, span, and areas

An element can be placed into specific cells — by line coordinates (lines are numbered from 1, and there's one more of them than tracks):

.hero {
  grid-column: 1 / 3;      /* from line 1 to line 3 = two columns */
  grid-row: 1;
}
.wide { grid-column: span 2; } /* just "two tracks wide", wherever it lands */
.full { grid-column: 1 / -1; } /* -1 is the last line: full width */

Or by names, via grid-template-areas: the page layout is drawn right in the CSS as ASCII art:

.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-areas:
    "sidebar header"
    "sidebar content"
    "sidebar footer";
}
.page > header { grid-area: header; }
.page > nav    { grid-area: sidebar; }

The areas template is a self-documenting layout: restructuring the page for mobile means rewriting the "picture" in a media query without touching the elements themselves. A dot means an empty cell.

Alignment inside cells is the pair familiar from flexbox, only the names are more symmetrical: justify-* — along the row (the inline axis), align-* — along the column; *-items — for all, *-self — for one. Centering a cell's content: place-items: center (the align+justify shorthand).

Grid or flexbox?

Not competitors — a division of labor:

  • Grid — when the layout is two-dimensional or must be aligned across rows: a page skeleton, a "label-field" form in columns, a gallery where cards sit strictly on a grid.
  • Flexbox — when the layout is one-dimensional and sizes are dictated by the content: a toolbar, a menu, a row of buttons, wrapping tags.

A practical marker: if you catch yourself nesting flex wrappers to align things "across rows" — that's grid. If you're writing grid for a single row of buttons — that's flex. A flex container lives happily inside a grid cell and vice versa: a typical page is a grid skeleton with flex filling.

And the familiar gotcha applies here too: long content stops a column from shrinking — grid children have the same min-width: auto, cured by min-width: 0 (or minmax(0, 1fr) right in the template).

In short

  • Grid describes the grid up front: grid-template-columns/rows, tracks in px/auto/fr; fr is a share of what remains after fixed tracks and gap.
  • repeat(auto-fit, minmax(220px, 1fr)) — a responsive gallery without media queries; memorize it.
  • Placement: by lines (grid-column: 1 / -1), by span, or by named areas — grid-template-areas draws the layout as ASCII art.
  • Grid — two-dimensional and aligned across rows; flex — one-dimensional, driven by content. Nested flex wrappers for "cross-row alignment" are a signal to switch to grid.
  • Column overflow is cured by minmax(0, 1fr) or min-width: 0.
  • Flexbox — grid's one-dimensional partner.
  • Units and responsiveness — rem, vw, clamp, and media queries: what tracks are measured in.
  • Positioning — sticky headers inside grid skeletons.