CSS Subgrid Rows: Aligning Cards in Dynamic Lists

This article explores how the CSS subgrid value for grid-template-rows solves the common problem of misaligned content within dynamic card layouts. You will learn the mechanics of grid-template-rows: subgrid, the limitations of traditional layout methods, and a practical implementation to align card headers, bodies, and footers consistently across adjacent grid columns regardless of dynamic content size.

The Problem with Dynamic Card Layouts

In modern web design, dynamic card lists frequently contain varying amounts of content. One card may have a single-line title and short description, while an adjacent card contains a three-line title and lengthy text.

Using traditional CSS Grid or Flexbox, the outer card boundaries can easily stretch to match the height of the tallest card in a row. However, the internal sections of those cards—such as headers, body content, and call-to-action buttons—remain isolated within each card's local layout context. This causes buttons and text sections to sit at mismatched vertical positions across the row, creating an uneven visual rhythm.

What is grid-template-rows: subgrid?

Normally, setting display: grid on a child element creates a completely independent grid context disconnected from the parent grid's tracks.

The subgrid value changes this behavior. When applied to a nested grid container via grid-template-rows: subgrid, the child element adopts the row tracks defined by its parent grid instead of generating its own independent row tracks. This allows direct descendants of the nested container to place themselves along the parent grid's tracks, sharing sizing calculations directly with neighboring cards.

How Subgrid Aligns Cards in Dynamic Lists

To align internal card components across dynamic columns, the layout relies on two structural requirements:

  1. The parent grid spans multiple rows per card: Instead of each card occupying a single grid row, the parent grid allocates multiple rows (such as one for the header, one for the body, and one for the footer).
  2. The card uses subgrid for its rows: The card container spans these rows (for example, grid-row: span 3) and declares grid-template-rows: subgrid.

Because the cards share the exact same row tracks, an expanding element in one card expands that row for every other card in the same horizontal track. If one card has a three-line title, the shared header row grows, neatly pushing the body and footer down uniformly across all cards in that row.

Practical Implementation

HTML Structure

<div class="card-grid">
  <article class="card">
    <h3>Short Title</h3>
    <p>Brief text description.</p>
    <button>Read More</button>
  </article>

  <article class="card">
    <h3>A Much Longer Title That Wraps Across Multiple Lines</h3>
    <p>Detailed text description that takes up more vertical space.</p>
    <button>Read More</button>
  </article>
</div>

CSS Implementation

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  /* Automatically create rows sized by content */
  grid-auto-rows: auto auto auto;
  gap: 1.5rem;
}

.card {
  display: grid;
  /* Span across the 3 parent rows: header, body, footer */
  grid-row: span 3;
  /* Inherit the row track sizing from .card-grid */
  grid-template-rows: subgrid;
  
  border: 1px solid #ccc;
  padding: 1rem;
}

Summary of Benefits

Using grid-template-rows: subgrid for card lists provides several distinct advantages:

  • Pixel-perfect alignment: Headers, paragraphs, and action buttons align horizontally across dynamic sibling elements without fixed heights.
  • No JavaScript needed: Layout calculations are handled entirely in the browser's native rendering engine, eliminating the need for resize listeners or script-based height equalization.
  • Responsive and dynamic: The layout automatically adjusts when screen sizes change or when new content loads dynamically via APIs.