Subgrid fundamentals: inheriting parent tracks
Subgrid solves a problem that has plagued CSS layouts since Grid launched: aligning content inside nested elements to the outer grid's tracks.
The problem subgrid solves
Without subgrid, each grid container defines its own independent set of tracks. Children of a grid item cannot see or align to the outer grid's columns or rows. This creates misalignment when you have cards with headers, bodies, and footers that need to line up across a row.
/* Without subgrid — each card has its own grid */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
.card {
display: grid;
grid-template-rows: auto 1fr auto;
/* These rows are independent per card.
If one card has a taller header, its body and footer
shift down — other cards don't follow. */
}
The result: card headers, content areas, and footers at different vertical positions across the row. Subgrid fixes this by letting card children inherit row tracks from the parent grid.
Basic subgrid syntax
To use subgrid, a grid item must itself be a grid container. Then set grid-template-columns: subgrid or grid-template-rows: subgrid (or both) to inherit the parent's tracks for that axis.
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr auto; /* shared row tracks */
gap: 1.5rem;
}
.card {
display: grid;
grid-row: span 3; /* span all 3 row tracks */
grid-template-rows: subgrid; /* inherit parent's rows */
/* Now .card's children align to the parent's row tracks.
All card headers share the same row height. */
}
.card__header { /* auto-placed in row 1 */ }
.card__body { /* auto-placed in row 2 */ }
.card__footer { /* auto-placed in row 3 */ }
grid-row: span 3 to access all of them.Subgrid on one axis only
You can use subgrid on one axis and define independent tracks on the other. This is the most common pattern — inherit row alignment while keeping columns independent (or vice versa).
/* Inherit rows from parent, define own columns */
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid; /* inherits parent rows */
grid-template-columns: 4rem 1fr; /* own column definition */
gap: 0.75rem;
}
/* Inherit columns from parent, define own rows */
.table-row {
display: grid;
grid-column: 1 / -1; /* span all parent columns */
grid-template-columns: subgrid; /* inherits parent columns */
grid-template-rows: auto; /* own row definition */
}
Gap behaviour in subgrid
By default, a subgrid inherits the parent grid's gap values. You can override the gap on the subgrid container to use different spacing for the child items — but the track lines themselves remain aligned with the parent.
.parent {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 2rem;
}
.child {
display: grid;
grid-column: span 4;
grid-template-columns: subgrid;
gap: 0.5rem; /* overrides parent's 2rem gap */
/* Track lines still align with parent,
but spacing between this element's children is 0.5rem */
}
Real-world example: aligned card grid
This is the canonical subgrid use case. A row of cards where every header, body, and footer lines up perfectly, regardless of content length.
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
grid-auto-rows: auto 1fr auto; /* repeating 3-row pattern */
gap: 1.5rem;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
border-radius: 0.5rem;
background: oklch(0.19 0.02 260);
overflow: hidden;
}
.card__image {
/* Row 1 — all images share the same row height */
aspect-ratio: 16 / 9;
object-fit: cover;
width: 100%;
}
.card__body {
/* Row 2 — grows to fill, pushing footer down */
padding: 1rem;
}
.card__footer {
/* Row 3 — all footers align at the bottom */
padding: 0.75rem 1rem;
border-top: 1px solid oklch(0.3 0.02 260);
}
Nested subgrid (grandchild alignment)
Subgrid can chain through multiple levels. A grandchild can align to the outermost grid by having both the child and grandchild use subgrid. This enables deeply nested content to participate in a single top-level grid.
.outer {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 1rem;
}
.middle {
display: grid;
grid-column: span 6;
grid-template-columns: subgrid; /* inherits outer's 6 cols */
}
.inner {
display: grid;
grid-column: 2 / 5; /* span 3 of the 6 cols */
grid-template-columns: subgrid; /* inherits those 3 tracks */
}
/* .inner's children now align to columns 2–4 of .outer */