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'.
.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.
.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.
.card {
grid-row: span 3;
display: grid;
grid-template-rows: subgrid;
gap: 8px; /* override the inherited gap inside the card */
}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.
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.
Try it yourself
- 1
Break the alignment on purpose
In the first example, remove
grid-template-rows: subgrid;from.sub article. Predict both console values for.subbefore running. (Without subgrid the card becomes a normal grid with its own rows, so they printfalse.) - 2
Change the inherited gap
In the first example, delete
gap: 6pxfrom.sub article. Predict the space between title and text: the subgrid now inherits the parent's 10px row gap. - 3
Make flex behave like grid
In the third example, change
.flex > divtoflex: 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 with1fr.
Code & diagrams
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
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
trueThe 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
Console
Nothing logged yet.Expected console output
all inputs start at the same x: trueFlexbox 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
Console
Nothing logged yet.Expected console output
.flex item 1: 120 item 5: 180
.grid item 1: 120 item 5: 120Break 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.
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)).
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: contentson 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-contenttracks, flex items with content-based basis) means the browser measures content, so very large lists benefit from fixed track sizes orcontent-visibility.
Remember this
- 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
**
subgrid** fixes that. On a grid item that spans several of its parent's tracks, writedisplay: 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
Rules for subgrid: the item must span the tracks it wants to share (
grid-row: span 3for a three-part card); you can usesubgridfor rows, columns or both (grid-template-columns: subgrid); the subgrid **inherits the parent'sgapbut 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
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
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. Withrepeat(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
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
What problem does subgrid solve, and how do you set it up for a row of cards?
What are the rules and limits of a subgrid?
How do you decide between Grid and Flexbox for a layout?
Why do the last items in a wrapped flex row look different from a grid's last row?
Practice
Make three product cards (title, description, price row) whose descriptions and price rows line up across the cards, using subgrid.
A header has a logo on the left and three links on the right. Grid or Flexbox? Write the CSS.
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 Nandgrid-template-rows: subgrid.I can use
grid-template-columns: subgridto 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.