CSS Grid Subgrid: Aligning Nested Elements Across the Parent Grid

subgrid (now baseline across all major browsers) solves one of CSS Grid’s longest-standing limitations: child elements inside a grid item could not align to the parent grid’s tracks. With grid-template-columns: subgrid (or rows: subgrid), a grid item participates in its parent’s track definition, enabling card-based layouts where titles, images, and CTAs line up perfectly across a row — without hard-coded heights.

Problem: CSS Grid layouts with nested components — a card inside a grid column that has its own inner grid — cannot align inner items with the outer grid's columns, causing visual misalignment between the inner and outer grids.

Solution: Use CSS Grid Subgrid — set grid-template-columns: subgrid on a nested grid container to inherit the column tracks from the parent grid. The nested grid's children then align to the parent's column lines, enabling consistent alignment across nested components without duplicating column definitions.


The examples below show a card grid where every card's internal elements snap to the parent grid rows, a holy-grail page layout using column subgrid, and how to combine subgrid with named lines for readable templates.


/* ── 1. Card grid with subgrid row alignment ─────────────────────────── */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    /* Define 4 named row tracks for card internals */
    grid-template-rows: auto auto 1fr auto;
    gap: 1.5rem;
}

.card {
    display: grid;
    /* Span all 4 parent row tracks */
    grid-row: span 4;
    /* Inherit the parent's row track definitions */
    grid-template-rows: subgrid;
    gap: 0;
    border: 1px solid #ddd;
    border-radius: 8px;
    overflow: hidden;
}

/* Now each child automatically aligns to the shared row track */
.card__image   { grid-row: 1; }  /* all images align to the same height */
.card__title   { grid-row: 2; padding: .75rem 1rem 0; }
.card__excerpt { grid-row: 3; padding: 0 1rem;  }   /* stretches to fill remaining space */
.card__footer  { grid-row: 4; padding: .75rem 1rem; background: #f9f9f9; }

/* ── 2. Column subgrid for form layout ─────────────────────────────────── */
.form-grid {
    display: grid;
    grid-template-columns: [label] max-content [field] 1fr [end];
    gap: .5rem 1rem;
}

.form-row {
    grid-column: label / end;   /* span both columns */
    display: grid;
    grid-template-columns: subgrid;  /* inherit label + field tracks */
}

.form-row label { grid-column: label; text-align: right; padding-block-start: .4rem; }
.form-row input { grid-column: field; }

/* ── 3. Combined row and column subgrid ─────────────────────────────────── */
.layout {
    display: grid;
    grid-template-columns: [sidebar] 260px [main] 1fr [end];
    grid-template-rows:    [header] auto [body] 1fr [footer] auto [page-end];
}

.sidebar {
    grid-column: sidebar;
    grid-row: header / page-end;     /* span full height */
    display: grid;
    grid-template-rows: subgrid;     /* align to parent row tracks */
}


NOTE: subgrid only works when the child element spans multiple tracks of the parent — a grid item that spans only one track has nothing to subgrid into; always pair grid-template-rows: subgrid with grid-row: span N where N matches the number of tracks the parent defines.