Command Palette

Search for a command to run...

Hectal
PHASE 8Intermediate Baseline 2023+ ~28 min· topic 6 of 6

Topic 8.6

Subgrid, and Grid vs Flexbox

In one line

subgrid lets a nested grid reuse its parent's tracks, so titles, text and buttons line up across separate cards; and Grid (two-dimensional, layout first) and Flexbox (one-dimensional, content first) solve different problems and work best together.

Think of it like this

A row of lockers. Each locker has three shelves. If every locker sets its own shelf heights, the shelves zig-zag along the row: one locker's top shelf is tall, the next one's short. If all lockers share one set of shelf heights, decided by the tallest thing on each shelf in the whole row, the shelves line up perfectly. A normal nested grid is the first kind of locker; a subgrid is the second.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Nested grid
A grid item that is also a grid container, with its own separate tracks.
Subgrid
A nested grid that uses its parent's row or column tracks instead of making its own.
`grid-template-rows: subgrid`
The declaration that makes a grid item share its parent's rows for the rows it spans.
One-dimensional layout
Arranging items along one line at a time, a row or a column. Flexbox.
Two-dimensional layout
Arranging items in rows and columns at the same time. Grid.
Content-out
The items' content decides their sizes, and the layout adapts around them.
Layout-in
You define the structure (tracks) first, and the items fit into it.

Step by step

01See the problem

Three cards in a grid row. Each card is a flex column with a title, text and a button. One title wraps to two lines, so that card's text and button sit lower than the others'.

style.csswhole filecss
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
.card {
  display: flex;
  flex-direction: column; /* each card on its own */
}

02Make each card span three parent rows

Each card has three parts, so each card should cover three rows of the parent grid. grid-row: span 3 does that; the parent creates the rows implicitly.

style.csswhole filecss
.card {
  grid-row: span 3;
}

03Switch the card to subgrid

display: grid; grid-template-rows: subgrid; makes the card's three children use the parent's three rows. Each row now sizes to the tallest title, the tallest text and the tallest button across all cards.

style.csswhole filecss
.card {
  grid-row: span 3;
  display: grid;
  grid-template-rows: subgrid;
  gap: 8px; /* override the inherited gap inside the card */
}
Switch the card to subgriddiagram
Rendering diagram…

04Subgrid columns for forms

The same trick works sideways. A form has several groups; each group is a subgrid spanning two parent columns, so every label column is as wide as the longest label in the whole form.

style.csswhole filecss
form {
  display: grid;
  grid-template-columns: max-content 1fr;
}
fieldset {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
}

05Decide: grid or flex?

Ask two questions: do rows and columns both need to line up, and should the structure come first or the content? Then pick the tool.

Decide: grid or flex?diagram
Rendering diagram…

Try it yourself

  1. 1

    Break the alignment on purpose

    In the first example, remove grid-template-rows: subgrid; from .sub article. Predict both console values for .sub before running. (Without subgrid the card becomes a normal grid with its own rows, so they print false.)

  2. 2

    Change the inherited gap

    In the first example, delete gap: 6px from .sub article. Predict the space between title and text: the subgrid now inherits the parent's 10px row gap.

  3. 3

    Make flex behave like grid

    In the third example, change .flex > div to flex: 0 0 120px. Predict item 5's width (120): without growing, flex items keep their basis, but the row no longer fills edge to edge when the width changes. That's the trade-off grid solves with 1fr.

Code & diagrams

Cards that line up with subgrid New tab

In the plain version each card lays out on its own, so the long title pushes its text and button down. With subgrid, all three cards share the parent's rows.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

.plain buttons line up: false texts line up: false
.sub buttons line up: true texts line up: true
true
Subgrid columns: one label width for the whole form New tab

The label column is as wide as the longest label in either fieldset ("Postcode or PIN code"), so all four inputs line up. Change `subgrid` to `max-content 1fr` and run: each fieldset sizes its own label column, and the console prints `false`.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

all inputs start at the same x: true
Wrapping: Flexbox vs Grid New tab

Flexbox lays out each line on its own, so items 4 and 5 grow to share their line (180px each). Grid keeps them in the 120px column tracks. Neither is wrong: pick the behaviour your design needs.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

.flex item 1: 120 item 5: 180
.grid item 1: 120 item 5: 120
Grid vs Flexbox at a glancediagram
Rendering diagram…

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Subgrid on an item that spans one track

In the first example, remove grid-row: span 3; from .sub article but keep grid-template-rows: subgrid.

terminal
$ the page shows
── what you'll see ──
Each card's title, text and button are squashed into one row and overlap. The console prints false for .sub.

Break #2

Use Grid for a row of different-width tags

Lay out tags of different lengths (HTML, Accessibility, CSS) with grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)).

terminal
$ the page shows
── what you'll see ──
Short tags get wide, empty boxes, and long tags either wrap inside their cell or force every column wider.

Myth vs fact

Myth

Grid replaces Flexbox.

Fact

They solve different problems. Most real pages use Grid for the overall structure and card lists, and Flexbox inside components.

Myth

Grid is slower than Flexbox.

Fact

Both are fast for normal page sizes. Performance problems come from huge numbers of items or layout thrashing in JavaScript, not from choosing one or the other.

Myth

Subgrid is too new to use.

Fact

It is Baseline 2023: supported in current Chrome, Edge, Firefox and Safari. Without support, a nested grid still lays out sensibly, only without cross-card alignment.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    A subgrid's own padding, border and margin are applied to its first and last shared tracks: the parent sizes those edge tracks so the subgrid's content still fits, which is why padded cards still line up correctly.

  • ▸

    Item sizes inside a subgrid contribute to the parent's track sizing, which is what makes alignment across cards possible. It also means a single huge item in one card makes that row taller in every card of the row.

  • ▸

    display: contents on a wrapper makes its children act as direct grid items and was the old workaround for missing subgrid. It removes the wrapper's box (no background, border or padding) and has had accessibility bugs with semantic elements such as lists and buttons, so prefer subgrid now.

  • ▸

    Flexbox resolves sizes per line, so it can't align items across lines; Grid resolves tracks for the whole container. For performance, both are single-pass in common cases, but intrinsic sizing (auto, max-content tracks, flex items with content-based basis) means the browser measures content, so very large lists benefit from fixed track sizes or content-visibility.

Remember this

  1. 1

    A grid item can itself be a grid (display: grid): that's a nested grid. But a nested grid has its own tracks and knows nothing about its parent's, so things inside separate cards can't line up with each other. A card with a two-line title pushes its text lower than its neighbour's, and the buttons end up at different heights.

  2. 2

    **subgrid** fixes that. On a grid item that spans several of its parent's tracks, write display: grid; grid-template-rows: subgrid;. The item stops making its own rows and uses the parent's row lines instead. Its children are placed on those shared rows, and their sizes feed back into the parent's track sizing. Now each row is as tall as the tallest matching piece across all cards, so titles, text and buttons line up.

  3. 3

    Rules for subgrid: the item must span the tracks it wants to share (grid-row: span 3 for a three-part card); you can use subgrid for rows, columns or both (grid-template-columns: subgrid); the subgrid **inherits the parent's gap but may set its own; it can use the parent's line names** and add its own (subgrid [title] [body] [foot]); and it gets no implicit tracks in the subgridded direction, so extra children are squeezed into the last track. Subgrid became Baseline 2023, supported in every current browser.

  4. 4

    Grid vs Flexbox in one sentence: Flexbox is one-dimensional and content-out (items decide their own sizes and the line wraps where it must), while Grid is two-dimensional and layout-in (you define the tracks and items fit into them). Ask: "Do I care about rows and columns lining up?" If yes, Grid. If you have a single row (or column) of things whose sizes come from their content, Flexbox.

  5. 5

    The difference shows up when items wrap. With flex-wrap, each line is laid out on its own: the two items left on the last line grow to fill it, so they're wider than the ones above. With repeat(auto-fill, minmax(…, 1fr)), every item sits in a column track, so the last row keeps the same column widths as all the others. Galleries and card lists usually want the grid behaviour; tag lists and button groups usually want the flex behaviour.

  6. 6

    Real layouts use both: Grid for the page and for card grids, Flexbox inside components (a nav bar's links, a card's footer with a price on the left and a button on the right, an icon next to a label). Many things can be done with either, like centring one item (Phase 6, Topic 6.5); pick the one that says your intent most clearly, and don't fight a tool into doing the other's job.

Explain it without notes

01

What problem does subgrid solve, and how do you set it up for a row of cards?

02

What are the rules and limits of a subgrid?

03

How do you decide between Grid and Flexbox for a layout?

04

Why do the last items in a wrapped flex row look different from a grid's last row?

Practice

01

Make three product cards (title, description, price row) whose descriptions and price rows line up across the cards, using subgrid.

02

A header has a logo on the left and three links on the right. Grid or Flexbox? Write the CSS.

03

Write a form where every label column is the width of the longest label, across two separate <fieldset>s.

Trade-offs

  • ↔

    Subgrid gives perfect cross-card alignment, but cards become tied to the parent's structure: a card can't be dropped into a non-grid parent and keep its internal layout.

  • ↔

    Grid gives consistent columns at the cost of empty cells in the last row; Flexbox fills every line edge to edge at the cost of uneven item widths.

Done when you can

  • I can make cards line up internally with grid-row: span N and grid-template-rows: subgrid.

  • I can use grid-template-columns: subgrid to align form labels across groups.

  • I know a subgrid inherits the gap, uses parent line names and creates no implicit tracks.

  • I can explain one-dimensional vs two-dimensional and content-out vs layout-in.

  • I can choose Grid or Flexbox for a given component and combine them.