Starter
Everything you need to publish a first project.
Nested grids that inherit their parent's tracks, so cards align across a row.
Everything you need to publish a first project.
Shared libraries, review flows and staging.
SSO, audit logs, a named contact and an uptime commitment in writing.
Subgrid on — titles, body and footers line up across cards regardless of content length.
Subgrid is supported in Chrome 117+, Firefox 71+ and Safari 16+ — about 93% of browsers. It needs no fallback for most audiences; where it matters, a plain nested grid degrades to cards that simply do not align.
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
grid-template-rows: repeat(3, auto);
gap: 8px 16px;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
}
/* Because each card spans the parent rows and subgrids onto them,
every title, body and footer sits on the same line across all cards —
no fixed heights, no JavaScript. */
Subgrid fixes one specific, extremely common problem: a row of cards where one has a two-line title and the rest have one, so every button below sits at a different height. The toggle above shows the difference directly — the cards on either side of it hold identical content.
Choose how many columns of cards you want and the row tracks each card should span — typically title, body and footer.
Toggle it and watch the cards. With subgrid off, each card is its own grid and the buttons sit at different heights. With it on, they line up.
Rows is the usual choice. Columns is for nested blocks that need to align to the page grid; both is rare but supported.
The output is two rules — the parent track definition and the child that inherits it. Paste it over your existing card grid.
Putting display: grid on a card gives it its own tracks, sized to its own content.
That is usually what you want — but it means the card knows nothing about its siblings. The
card with more text gets a taller first row, and nothing downstream can line up.
subgrid replaces the child's track sizes with the parent's, so all the cards are
measuring against the same ruler.
A subgrid child has to span the tracks it wants to inherit. If your card is placed in
a single row and you set grid-template-rows: subgrid, it inherits exactly one
track and nothing changes. Give the card grid-row: span 3 first, then subgrid it —
that is the step that makes the whole thing work.
The workarounds subgrid replaces are all worse: fixed card heights that break with a longer
title, a flex: 1 spacer that only aligns the last element, or JavaScript that
measures the tallest card and applies a height. All three cost something. Subgrid costs
nothing and degrades to a plain nested grid where it is unsupported.
Once your cards inherit the parent tracks, the parent is the only place you size anything — which pairs well with fluid track sizes from the clamp generator, and with drawing the parent itself in the grid generator.
grid-template-rows: subgrid on a child and its rows line up with the parent’s rows — so headings, body copy and buttons across a row of cards align with each other even though each card has a different amount of text.flex: 1 spacers, or JavaScript measuring. Subgrid removes all three.grid-template-rows: subgrid and grid-template-columns: subgrid are independent, and you can set both. Rows is the far more common case; columns is useful when a nested block needs to align to the page’s main column grid.grid-row: span 3 and it inherits three of the parent’s rows — that is where the alignment comes from.gap. Track sizes cannot be overridden — that is the whole point of subgrid.