CSS Grid Subgrid: Alignment Across Card Components
A common challenge in frontend layouts is aligning internal elements of separate card components. For example, if you have a row of card elements with dynamic content, you want their headers, images, bodies, and footers to align horizontally across cards, regardless of content size differences.
Historically, this required rigid heights or complex JavaScript solutions. Modern CSS introduces Subgrid to solve this exact issue.
The Core Problem: Dynamic Card Row Alignment
Consider a standard three-column card row. Each card contains a title, a brief description, and a footer button:
<div class="grid-container">
<div class="card">
<h3>Short Title</h3>
<p>Brief text description.</p>
<button>Footer Button</button>
</div>
<div class="card">
<h3>A Much Longer Title That Wraps to Two Lines</h3>
<p>A longer text description that occupies more space vertically.</p>
<button>Footer Button</button>
</div>
<div class="card">
<h3>Title</h3>
<p>Short desc.</p>
<button>Footer Button</button>
</div>
</div>If we make the .grid-container a grid, the cards themselves align perfectly. However, the internal headers, paragraph text, and buttons do not. If Card 2's header wraps to two lines, Card 1's description will still start higher, breaking horizontal reading alignment across the cards.
The Solution: CSS Grid Subgrid
Subgrid allows child elements of a grid item (grand-children of the parent grid container) to opt-in to the parent grid's column or row tracks.
Here is the modern CSS way to solve this:
/* Parent Grid Container */
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
/* Card item (child of grid-container, parent of internal elements) */
.card {
display: grid;
/* Occupy three rows in the parent grid layout */
grid-row: span 3;
/* Opt-in to inherit the row definitions of the parent grid */
grid-template-rows: subgrid;
gap: 10px;
}How it works:
- The parent
.grid-containerdefines columns, but leaves rows to grow implicitly. - The card element spans 3 rows (
grid-row: span 3) within the parent container. - The card sets
grid-template-rows: subgrid. Instead of defining its own row heights, it hands control over to the parent grid container. - The browser aggregates the heights of headers, descriptions, and buttons across all cards in the row. It aligns them perfectly:
- Row 1 across all cards gets the height of the tallest header.
- Row 2 gets the height of the tallest description.
- Row 3 gets the height of the tallest button.
Specifying Subgrid for Columns
Subgrid works just as well for columns. If you want child elements of a component to align perfectly with the parent column structure, specify subgrid on columns:
.sidebar-wrapper {
display: grid;
grid-column: span 2;
/* Inherit columns from the parent layout grid */
grid-template-columns: subgrid;
}Browser Support and Fallbacks
CSS Subgrid is now widely supported in all modern greenfield browsers (Chrome, Safari, Firefox, Edge).
To ensure layouts remain usable on legacy browsers, use a feature query to wrap subgrid styles:
.card {
display: flex;
flex-direction: column;
justify-content: space-between;
}
@supports (grid-template-rows: subgrid) {
.grid-container {
/* Define row tracks for child contents */
grid-auto-rows: auto auto auto;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
}
}Key Takeaways for Interviews
- What is Subgrid?: It is a value for
grid-template-rowsorgrid-template-columnsthat allows a nested grid container to use the track lines defined on its parent grid container. - Why use it?: It aligns grandchild elements across different grid cells (like card contents in a multi-card row) without manual/hardcoded heights or JavaScript.
- Syntax: Set
grid-row: span Norgrid-column: span Nto declare how many tracks the subgrid spans, then specifygrid-template-rows: subgridorgrid-template-columns: subgridon the child container.
