You've set up a three-column card grid. Each card has an image, a category label, a title, a short description, and a call-to-action link. The layout looks fine when all the titles are the same length — but as soon as real content arrives, cards in the same row fall out of sync. The title in card one wraps to two lines and pushes the CTA down, while cards two and three stay short. The row looks ragged.
Before CSS Grid Subgrid reached baseline status, fixing this meant hardcoding heights, abusing display: contents, or reaching for JavaScript. Now, with subgrid supported in every major browser since Chrome 117, you can solve it with two CSS properties. Here's exactly how.
The Problem with Nested Grids
When you nest a grid inside a grid item, the child creates its own independent track system. The parent grid has no influence over the rows inside each card — they're defined separately and sized by their own content. So when one card's title is longer than its neighbours', its internal rows shift, and cross-card alignment breaks.
The classic workaround was to make each card a direct child of the parent grid, spanning multiple rows — effectively flattening the card's elements into the parent track system. That works for alignment, but it destroys semantic HTML structure: you can no longer wrap each card in a single <article> element with its children grouped naturally inside it.
Because .card's row tracks are independent of .card-grid's rows, each card calculates its own row heights. A long title in card one does not affect the title row height in cards two or three — they're on separate grids entirely.
How Subgrid Works
Subgrid lets a grid item inherit its parent's track definitions instead of creating new ones. Set grid-template-rows: subgrid on a grid item that spans multiple parent row tracks, and its children will align to those same parent tracks — as if they were direct children of the parent grid, while still being semantically nested inside their card element.
The key requirement: the child element must span at least two tracks on the axis you want to inherit. A card spanning one column cannot inherit column tracks, but it can inherit row tracks if the parent defines rows that cross the full grid.
Now every .card's children sit in the same row tracks as the equivalent children of every other card in the row. When one title wraps to two lines, that row expands for all cards simultaneously, keeping images, categories, titles, and CTAs perfectly in sync across the row.
Practical Example — Aligned Card Layouts
Setting Up the Parent Grid
Start with semantic HTML: a grid container holding <article> elements, each with its four child elements in document order.
Applying Subgrid to Each Card
The complete working CSS. The parent grid defines both column and row tracks. Each card spans one column but all four row tracks, then declares grid-template-rows: subgrid to inherit them. The card's children automatically land in the inherited tracks — no extra placement rules needed.
With this setup: the image row is always 200 px, the category row expands to the tallest category label across the row, the 1fr title row absorbs the longest title, and the CTA row is always flush to the bottom of each card — regardless of how much content each card holds.
Named Grid Lines with Subgrid
Named grid lines defined on the parent are inherited by subgrid children automatically. This lets you reference meaningful names in child CSS rather than relying on implicit numeric placement:
Named lines are especially useful in design systems where the track layout is defined once in a shared stylesheet and consumed in many components. Partial subgrid — inheriting rows only while leaving columns independent, or vice versa — is fully valid. See the MDN Subgrid reference for the full named-line inheritance spec.
Feature Detection and Browser Support
Subgrid is supported across all major browsers and is considered baseline. For projects that still need to reach older browser versions, wrap the subgrid-specific rules in an @supports block:
Browser support as of 2026:
| Browser | Subgrid support since | Notes |
|---|---|---|
| Chrome / Edge | 117 (Aug 2023) | Both columns and rows |
| Firefox | 71 (Dec 2019) | First browser to ship subgrid |
| Safari | 16 (Sep 2022) | Both columns and rows |
For current usage data, check caniuse.com/css-subgrid. With Chrome 117+ covering the last holdout, subgrid is safe to use in production without a fallback for most audiences. The @supports guard is still worth adding for any project targeting users on long-term-support enterprise environments.
Debugging Subgrid in DevTools
Getting subgrid wrong is easy — and the visual symptoms (misaligned elements, unexpected collapsing rows) look identical to other grid bugs. DevTools grid overlays are your first diagnostic tool.
Chrome / Edge: Open DevTools, select the parent grid container, and click the grid badge in the Elements panel to enable the grid overlay. Then select the subgrid child and enable its overlay too. You'll see both sets of tracks rendered simultaneously — the inherited tracks highlighted differently from any tracks the child would define independently. This immediately shows whether the subgrid relationship is active.
Firefox: Still the best subgrid visualisation. The Layout panel shows named lines with their labels directly on the overlay, making it easy to confirm that inherited names are resolving correctly.
Two quick diagnostic checks when subgrid isn't behaving:
- Verify the span. The child must have
grid-row: span N(or explicit placement covering N rows) where N matches the number of row tracks the parent defines in that section. A span of 3 on a 4-row subgrid leaves one track unaccounted for. - Confirm the child is a direct grid item. Subgrid only works when the element declaring
grid-template-rows: subgridis itself a direct child (grid item) of the parent grid. An extra wrapper element between the parent grid container and the subgrid element breaks the relationship entirely.
When Not to Use Subgrid
Subgrid is the right tool for cross-sibling alignment across nested containers — the card layout above being the canonical case. It's not always the best choice:
- Single-axis alignment within a single container. If you just need the items inside one flexbox row to stretch to the same height,
align-items: stretch(the Flexbox default) handles it without any grid involvement. - Deep nesting beyond one level. Subgrid inherits tracks one level at a time. A grandchild can subgrid from its parent, which subgrids from its grandparent — chaining works — but it adds cognitive overhead. If you're three levels deep and still fighting alignment, reconsider the component structure.
- Dissolving a single wrapper.
display: contentsmakes an element "transparent" to the grid, so its children become direct grid items of the grandparent. It's a narrower tool than subgrid but useful when you have a single semantic wrapper (like a<div>added for JavaScript purposes) that you don't want to influence layout. See the web.dev subgrid article for a direct comparison of these two approaches.
Subgrid is now production-safe and widely supported. The card layout pattern above will save meaningful time on almost any project that mixes variable-length content in a grid of repeating components. Start with grid-template-rows: subgrid for quick wins — you often only need to inherit the row axis, leaving each card free to define its own column structure independently.