Tags: web-dev concept

Grid

Date: 2026-09-27


Grid defines the tracks first, then places items into them. So the layout decides the sizes, and items in different rows line up. repeat(auto-fit, minmax(…)) gives a responsive layout without a breakpoint, and minmax(0, 1fr) fixes the column that won’t shrink.


CSS Grid is a two-dimensional layout mode: the container defines rows and columns (tracks), and children are placed into the cells between the grid lines. Two-dimensional means a column’s width is shared by every row, which is the thing Flexbox can’t do.

The shape

grid-template-columns: 200px 1fr 1fr;
grid-template-rows:    auto 1fr auto;

   line 1    line 2          line 3          line 4
     │ 200px  │     1fr       │     1fr       │
     ├────────┼───────────────┼───────────────┤  ← row line 1
     │ header ─────────────────────────────── │  auto
     ├────────┼───────────────┼───────────────┤
     │ nav    │ main          │ aside         │  1fr
     ├────────┼───────────────┼───────────────┤
     │ footer ─────────────────────────────── │  auto
     └────────┴───────────────┴───────────────┘

Items are placed by line number (grid-column: 1 / -1 spans all), by span (span 2), or by name through grid-template-areas:

.page {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
  grid-template-areas:
    "header header header"
    "nav    main   aside"
    "footer footer footer";
}
.page > header { grid-area: header; }

The areas string is a picture of the layout, which is why it’s the most readable form for page-level structure.

The units

  • fr — a share of the free space left after fixed and content-sized tracks are sized
  • minmax(a, b) — no smaller than a, no larger than b
  • auto — sized to content, and stretches to fill if there’s space
  • min-content / max-content — narrowest without overflow / widest without wrapping

The breakpoint-free card grid

The load-bearing idiom — as many columns as fit, each at least 240px:

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: 1rem;
}
gap 16px. n columns fit while  n × 240 + (n − 1) × 16  ≤  width

container 1000px  4 → 1008 ✗   3 → 752 ✓   →  3 columns of 323px
container  600px  3 →  752 ✗   2 → 496 ✓   →  2 columns of 292px
container  300px                           →  1 column  of 300px
container  200px                           →  1 column  of 200px   ← min() stops a 240px overflow

auto-fit vs auto-fill only differ when there are fewer items than would fit: auto-fill keeps the empty tracks, so two cards stay 240px wide with a gap beside them; auto-fit collapses the empties, so two cards stretch to fill the row. Usually auto-fit is what you meant.

This one line replaces most card-grid media queries — Responsive Design.

The blowout trap

1fr is really minmax(auto, 1fr) — the minimum is the content’s minimum size. A long unbroken string, a pre block or a wide table in one column forces it wider than its share, and the grid overflows.

grid-template-columns: 1fr 1fr;                       /* blows out on long content */
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); /* ← columns stay equal */

Same cause as flexbox’s min-width: auto trap, same family of fix.

Subgrid

A nested grid normally has its own tracks, so the titles in a row of cards can’t line up if one wraps to two lines. grid-template-rows: subgrid on the card makes it adopt its parent’s row tracks, so every card’s title, body and button align across the row [CHECK: subgrid support across current browsers if an older-browser audience matters].

Failure modes

  • Column blowout, above
  • Visual order ≠ source order. Placing items with lines or areas can reorder them visually, and focus order won’t follow — Keyboard Navigation
  • Grid for everything. A row of buttons with varying label lengths is content-sized: flex. Forcing it into tracks produces awkward equal widths
  • Implicit tracks. Items placed beyond the defined grid create rows sized by grid-auto-rows (default auto) — the source of “where did this extra row come from”

Where it connects

  • Flexbox — the one-dimensional counterpart; the table there covers choosing between them
  • Container Queries — for when the component, not the grid, needs to change shape