Subgrid: Card Grids Where Everything Finally Lines Up
Look at any card grid with real content: one title wraps to two lines, one description runs long, and suddenly the prices sit at different heights and the buttons zigzag across the row. Designers ask for alignment; developers explain that each card is its own layout world; everyone settles for min-height hacks. Subgrid ends the argument:
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 24px;
}
.product-card {
display: grid;
grid-row: span 4; /* claim 4 rows of the parent */
grid-template-rows: subgrid; /* and USE those shared rows inside */
row-gap: 8px;
}
/* image, title, description, price+button - each child lands in a shared track */
grid-template-rows: subgrid means: instead of computing my own rows, adopt the parent's row tracks for the span I occupy. Every card's title now lives in the same row track - so the row is as tall as the tallest title in that row of the grid, and every price and button aligns across cards. The alignment is structural, not simulated.
The mental model in one sentence
A subgrid doesn't create tracks; it borrows them - sizing information flows both ways, so the contents of every card negotiate the shared track sizes together. That's exactly the cross-parent alignment that flexbox and nested grids fundamentally can't express.
Second use: form label columns
form.aligned {
display: grid;
grid-template-columns: max-content 1fr;
gap: 12px 16px;
}
.field-row {
display: grid;
grid-column: span 2;
grid-template-columns: subgrid; /* label column width shared across ALL rows */
align-items: center;
}
Every label sits in one shared max-content column - the widest label sets it, all rows follow, and wrapping fields keep their structure. The classic table-layout alignment, with grid semantics and responsive freedom.
Rules and edges
- Explicit span required: the subgridded element must span the parent tracks it wants (grid-row: span 4 above). Count your card's internal sections and match.
- Gaps inherit but can be overridden - row-gap on the subgrid adjusts spacing within it without breaking track alignment.
- Line names pass through, which keeps complex layouts readable: name the rows in the parent, place by name inside cards.
- It's per-axis: subgrid rows, columns, or both. The card case uses rows; the form case uses columns.
Support: Firefox pioneered it years ago; Chrome 117+ and Safari 16+ completed the set in 2023 - it's safely universal for current browsers, with a graceful story for ancients (cards fall back to independent rows: exactly the misalignment you already had). Combined with container queries for the card's internal responsive behavior, the card-grid problem - the actual, full, designer-approved version - is now just CSS.
Full-stack web developer sharing practical tutorials and building tools that ship.
Got something on your mind?
My inbox is open - no forms disappearing into the void here.
- Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
- Hire me for a project Have something custom in mind? Let's talk scope and timelines.
- Product support Bought something here? I'll help you get it running.
I usually reply within 1-2 business days.