CSS Subgrid
Subgrid lets a nested element adopt its parent's grid tracks, so content inside a row of cards lines up across them with no fixed heights or magic numbers.
In one line: subgrid lets a grid item borrow its parent grid's rows or columns, so the things inside a row of cards — titles, bodies, footers — align across every card without you hand-tuning a single height.
What it is
CSS Grid is great at laying out a row of cards. The problem is everything inside each card: one title wraps to two lines, the next to one, so the bodies start at different heights and the footers drift. Each card is its own grid, ignorant of its neighbours.
Subgrid fixes that. When you set grid-template-rows: subgrid (or grid-template-columns: subgrid) on a grid item that itself is a grid, that item stops defining its own tracks and instead adopts the tracks of the parent grid it spans. Now the title row, body row, and footer row are shared across the whole outer row, and everything lines up.
Why it matters
Before subgrid, alignment across cards meant fixed heights (min-height: 3rem on every title) that break the moment content changes language or length — the classic "magic number" that rots. Subgrid makes alignment a property of the layout, not a guess: the browser measures the tallest title in the row and gives every card the same title track, automatically and responsively.
It is now Baseline across modern browsers, so it is a real production tool, not a future bet — with a clean flexbox fallback for the long tail.
See it
Tweak it2
Toggle subgrid off and the cards size their rows independently — titles and tags drift out of line. Toggle it on and every region snaps to a shared track, however uneven the content.
How it works
Two grids cooperate:
- The outer grid is the row. Give it columns and, crucially, named or implicit rows the cards can share — e.g. three rows for title / body / footer.
- Each card is a grid item that spans those rows (
grid-row: span 3) and is itself a grid declaringgrid-template-rows: subgrid. That one keyword means "don't invent rows; use the three the parent already defined."
The card's own children then place onto the parent's tracks. Because all cards share the same parent rows, the browser sizes each track to the tallest content across the whole row — that is where the alignment comes from. Gaps (gap) are inherited from the parent too, so spacing stays consistent.
Build it
<ul class="cards">
<li class="card">
<h3>Quick note</h3>
<p>Short body.</p>
<span class="tag">Note</span>
</li>
<li class="card">
<h3>A longer card title that wraps onto two lines</h3>
<p>A longer body that runs on for several lines so the natural heights differ.</p>
<span class="tag">Article</span>
</li>
</ul>.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: 1rem;
}
.card {
display: grid;
/* Span three of the parent's rows, then adopt them. */
grid-row: span 3;
grid-template-rows: subgrid;
gap: 0.5rem;
}
/* Fallback where subgrid is unsupported: flex column, ragged but usable. */
@supports not (grid-template-rows: subgrid) {
.card {
display: flex;
flex-direction: column;
}
}Make it yours
Use the controls beside the demo above to change subgrid and cards in the row — each change updates the example live.
Experiment in the playground- Turn subgrid off and watch the footers drift — that ragged baseline is exactly what fixed
min-heighthacks try (and fail) to tame. - Add a card to the row and the shared tracks re-measure to the new tallest content. No value to update.
- Picture a card with a much longer title: every card's title row grows together, so the bodies still start on the same line.
Reproduce it with an LLM
Reproduce it with an LLM
You are a senior front-end engineer. Build a responsive row of product cards where each card has a title (1–2 lines), a body, and a footer with a price and button. Use CSS Grid with `grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr))` for the row, then make each card a grid item that spans the row's tracks with `grid-row: span 3` and `grid-template-rows: subgrid`, so titles, bodies, and footers line up across every card regardless of content length — no fixed heights or magic numbers. Provide a graceful fallback with `@supports not (grid-template-rows: subgrid)` using flexbox. Semantic HTML (a list of articles), and a visible focus style on the buttons. Return only the HTML and CSS.
Pitfalls & accessibility
Related
Layout & grids
A grid turns scattered elements into a system of columns and gutters. Learn CSS Grid, responsive reflow with auto-fill/minmax, and when to reach for subgrid.
Container queries
Container queries let a component respond to the width of its container instead of the viewport — so one card works in a sidebar and a wide column unchanged.