skip to content

Three sibling cards sit in one CSS grid row, each with a title, a body and a footer of different lengths, and the three footers must line up horizontally. How do you build that with subgrid?

level: seniorimportance: should knowfreq 42%

answer

  1. parent owns the bands
  2. each card spans them all
  3. subgrid the rows, not the columns
  4. children auto-place into the shared rows
  5. pad the children, not the card

basics

~20 s

Give the outer grid three row tracks for title, body and footer, make every card span all three with grid-row: span 3, and set grid-template-rows: subgrid on the card. All three cards then share the same row lines, so titles, bodies and footers align across them.

solid answer

~50 s

Define the rows once on the outer grid — something like `grid-template-rows: auto 1fr auto` for title, body and footer — then place each card with `grid-row: span 3` and give it `display: grid; grid-template-rows: subgrid`. Each card now inherits those three rows instead of computing its own, so the title row is as tall as the tallest title across all three cards, and every footer starts on the same line. The card's title, body and footer land in rows 1, 2 and 3 by auto-placement, so no per-child placement is needed. Two details decide whether it works: the span must match the number of rows, and padding belongs on the card's children rather than on the card itself, since padding on the subgrid box insets its tracks away from the shared lines. Without subgrid, stretching only equalises the cards' outer heights — the boundary between a one-line and a two-line title still differs.

code

css · 18 lines
css
.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: auto 1fr auto;
  grid-auto-rows: auto 1fr auto;
  gap: 1rem;
}

.card {
  grid-row: span 3;
  display: grid;
  grid-template-rows: subgrid;
  border: 1px solid;
}

.card > * {
  padding-inline: 1rem;
}

go deeper

for a junior

Know the shape of the fix: the outer grid declares the rows, each card spans them, and the card uses grid-template-rows: subgrid so its parts land on shared lines.

for a middle

Explain why the span count must match the number of bands, and how the card's own children auto-place into the inherited rows without any per-child placement.

for a senior

Show you have shipped it: name the padding trap, handle wrapping bands with grid-auto-rows, and say plainly when stretched equal-height cards already suffice without subgrid.

for a principal

Own the contract question — whether the card component may assume a three-band outer grid at all, since subgridding couples a component's internals to its parent's row template and limits where it can be reused.

## The layout, stated precisely "Cards line up" is two different requirements, and it is worth separating them because only one of them needs subgrid. 1. **Equal card heights.** Grid items stretch to fill their row by default, so cards in the same row are already the same height, and a footer pushed to the bottom of each card is already aligned. No subgrid required. 2. **Equal *internal* bands.** The line where the title ends and the body begins should be the same in every card, even when one title wraps to two lines and another does not. This is what plain nesting cannot do, because each card's rows are sized from its own content. Subgrid solves the second by making all cards share one set of row tracks. ## The recipe ```css .cards { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: auto 1fr auto; /* title / body / footer */ gap: 1rem; } .card { grid-row: span 3; display: grid; grid-template-rows: subgrid; } ``` The outer grid declares the three bands once. Each card spans all three rows and subgrids them, so its own three children auto-place into row 1, row 2 and row 3. Because every card contributes to the same tracks, the `auto` title row grows to the tallest title anywhere in the set, and the `auto` footer row to the tallest footer; `1fr` gives the body whatever is left. The result is three cards whose internal horizontal rules coincide. Note what you did *not* have to do: no per-child `grid-row` declarations, no JavaScript measuring, no fixed title height, no `-webkit-line-clamp` truncation to force titles to one line. ## Getting the span right The span decides the track count, so it must equal the number of bands. Two failure modes: - **Span omitted.** The card occupies one row, subgrids one track, and its three children pile into that row plus implicit rows the card creates for itself. The layout collapses in a way that looks unrelated to the span. - **Span larger than the template.** Spanning four rows when the template declares three makes the parent create an implicit row; whether that is a bug depends on whether you also sized implicit rows. If the number of bands varies per card, subgrid is the wrong tool — the shared track count is the whole premise. ## Wrapping to multiple rows With more cards than columns, auto-placement puts the next cards on the following rows, each spanning three. Alignment then holds *within each band of cards* — row 4's cards share their own three tracks — which is normally what you want: a card near the bottom of a long list should not be stretched to match a title far above it. Make sure implicit rows are sized deliberately, since only the first three rows come from `grid-template-rows`. Placing `grid-auto-rows: auto 1fr auto` alongside the template keeps subsequent bands behaving like the first. ## Padding, the usual trap Cards want padding. But padding on the subgrid element insets its track area from its grid area, so the shared lines shift inward by the padding and the cards stop agreeing. Two ways out: ```css /* pad the children, not the subgrid box */ .card > * { padding-inline: 1rem; } .card > :first-child { padding-block-start: 1rem; } .card > :last-child { padding-block-end: 1rem; } ``` Or keep the padded, visually-styled box as a separate element and put the subgrid on an unpadded wrapper. Borders and background can stay on the card itself as long as the border width is not what you are aligning against. ## What the non-subgrid answer buys you Before subgrid, the standard approach was a column layout inside each card with the body allowed to absorb free space, so the footer sat at the bottom of its own card. Combined with stretched equal-height cards, footers did end up level. The honest limitation is the internal bands: a two-line title in one card pushes that card's body down while its neighbour's body starts higher. If the design tolerates that, you do not need subgrid at all — which is a legitimate thing to say in an interview, and shows you know what the feature is actually buying. ## What an interviewer is listening for The shape of the answer — rows declared on the parent, span, `subgrid` — plus at least one of the real-world details: the span/track-count coupling, the padding caveat, or the distinction between equal card heights and equal internal bands. Candidates who have only read about subgrid usually give the first half and stop.

  • What happens if you forget `grid-row: span 3` on the card?
    The card occupies a single parent row, so `grid-template-rows: subgrid` inherits exactly one track. Its three children then overflow into implicit rows that the card creates locally, and every card sizes those independently — so you get the old misaligned result, plus a row-height surprise in the parent. The span is what sets the inherited track count.
  • How does this behave when the cards wrap onto a second row?
    Each band of cards shares its own set of three tracks, so alignment holds within a band but not between bands — usually the desired behaviour. Only the first three rows come from `grid-template-rows`, so set `grid-auto-rows` to the same pattern, otherwise the implicit rows for later bands size differently from the first.
  • When would you not reach for subgrid here at all?
    When only equal card heights and bottom-aligned footers are required. Grid items already stretch to the row height, and a column layout inside each card that lets the body absorb free space puts every footer at the bottom. Subgrid earns its place only when the *internal* boundaries — title-to-body, say — must match across cards.

saying these in an interview costs you the question

  • Setting subgrid on the card without spanning the rows
  • Padding the subgrid element and wondering why lines drift
  • Confusing equal card heights with equal internal bands
  • Reaching for JavaScript to measure and equalise title heights
  • Fixing the title to a hard-coded height to force alignment

context