15 / 30
All 30 in 30 CSS Grid Layouts
- 01Full-Bleed Layout
- 02Sticky Sidebar Grid
- 03Responsive Media Gallery
- 04Modern Holy Grail
- 05Dynamic Data Grid
- 06Card Stack Overlay
- 07Holy Grail
- 0812-Column Grid
- 09Bento Box
- 10Magazine Editorial
- 11RAM Auto-Fit
- 12Quantity Query
- 13Pinterest Masonry
- 14Hexagonal Honeycomb
- 15Subgrid Card
- 16Dashboard Skeleton
- 17Pricing Table
- 18Kanban Board
- 19Aspect-Ratio Tiles
- 20Container-Query Card
- 21Diagonal Skew Grid
- 22Asymmetric Splash
- 23Mosaic Photo Wall
- 24Dense Packing
- 25Numbered Steps Track
- 26Mac App Sidebar
- 27Scroll-Snap Slider
- 28Aurora Map
- 29Logo Wall
- 30Periodic Table
Subgrid Card
The card-alignment problem, finally solved: titles of different lengths, descriptions of different depths — yet every title baseline, every description block and every button aligns PERFECTLY across cards, because each card inherits the parent's row tracks with grid-template-rows: subgrid. The demo shows misaligned vs aligned side by side.
Build layouts like this from scratch: CSS Grid templates, areas & responsive design
ZTM · Fundamentals of CSS Grid · 30‑day money-back guarantee · Affiliate link
The code
<section class="cgl-15" aria-label="Subgrid card alignment demo">
<div class="cgl-15__grid">
<article class="cgl-15__card">
<p class="cgl-15__eyebrow">Starter</p>
<h4>Ship your first project</h4>
<p class="cgl-15__desc">Every card spans the same four parent rows.</p>
<a href="#0" class="cgl-15__btn">Choose Starter</a>
</article>
<article class="cgl-15__card cgl-15__card--hot">
<p class="cgl-15__eyebrow">Studio · most popular</p>
<h4>A much longer title that wraps onto two full lines</h4>
<p class="cgl-15__desc">The title track grows for the tallest title — and every sibling's title track grows with it. That is subgrid.</p>
<a href="#0" class="cgl-15__btn">Choose Studio</a>
</article>
<article class="cgl-15__card">
<p class="cgl-15__eyebrow">Agency</p>
<h4>Scale it</h4>
<p class="cgl-15__desc">Short title, short copy — the button still lands on the shared final track, perfectly level with its neighbours.</p>
<a href="#0" class="cgl-15__btn">Choose Agency</a>
</article>
</div>
</section><section class="cgl-15" aria-label="Subgrid card alignment demo">
<div class="cgl-15__grid">
<article class="cgl-15__card">
<p class="cgl-15__eyebrow">Starter</p>
<h4>Ship your first project</h4>
<p class="cgl-15__desc">Every card spans the same four parent rows.</p>
<a href="#0" class="cgl-15__btn">Choose Starter</a>
</article>
<article class="cgl-15__card cgl-15__card--hot">
<p class="cgl-15__eyebrow">Studio · most popular</p>
<h4>A much longer title that wraps onto two full lines</h4>
<p class="cgl-15__desc">The title track grows for the tallest title — and every sibling's title track grows with it. That is subgrid.</p>
<a href="#0" class="cgl-15__btn">Choose Studio</a>
</article>
<article class="cgl-15__card">
<p class="cgl-15__eyebrow">Agency</p>
<h4>Scale it</h4>
<p class="cgl-15__desc">Short title, short copy — the button still lands on the shared final track, perfectly level with its neighbours.</p>
<a href="#0" class="cgl-15__btn">Choose Agency</a>
</article>
</div>
</section>.cgl-15,
.cgl-15 *,
.cgl-15 *::before,
.cgl-15 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-15 {
width: 100%;
min-height: 100vh;
--mint: oklch(0.85 0.12 165);
--ink: oklch(0.24 0.03 180);
--bg: oklch(0.2 0.03 175);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.4rem;
}
.cgl-15 .cgl-15__grid {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(min(190px,100%),1fr));
grid-auto-rows: auto;
gap: 14px;
}
.cgl-15 .cgl-15__card {
grid-row: span 4;
display: grid;
grid-template-rows: subgrid;
row-gap: .6rem;
background: oklch(0.25 0.03 175);
border: 1px solid color-mix(in oklab,var(--mint) 25%,transparent);
border-radius: 16px;
padding: 1.2rem;
}
.cgl-15 .cgl-15__card--hot {
background: color-mix(in oklab,var(--mint) 14%,oklch(0.25 0.03 175));
border-color: var(--mint);
}
.cgl-15 .cgl-15__eyebrow {
font-size: .66rem;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--mint);
}
.cgl-15 h4 {
font-size: 1.05rem;
line-height: 1.3;
color: white;
text-wrap: balance;
}
.cgl-15 .cgl-15__desc {
font-size: .8rem;
line-height: 1.6;
color: oklch(0.75 0.02 175);
}
.cgl-15 .cgl-15__btn {
align-self: end;
justify-self: start;
font-size: .82rem;
font-weight: 600;
color: var(--ink);
background: var(--mint);
text-decoration: none;
padding: .55rem 1rem;
border-radius: 999px;
min-block-size: 2.4rem;
display: inline-grid;
align-items: center;
}
.cgl-15 .cgl-15__btn:hover {
background: color-mix(in oklab,var(--mint) 85%,white);
}
.cgl-15 .cgl-15__btn:focus-visible {
outline: 2px solid var(--mint);
outline-offset: 3px;
}
@supports not (grid-template-rows: subgrid) {
.cgl-15 .cgl-15__card {
grid-template-rows: auto auto 1fr auto;
}
}.cgl-15,
.cgl-15 *,
.cgl-15 *::before,
.cgl-15 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-15 {
width: 100%;
min-height: 100vh;
--mint: oklch(0.85 0.12 165);
--ink: oklch(0.24 0.03 180);
--bg: oklch(0.2 0.03 175);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.4rem;
}
.cgl-15 .cgl-15__grid {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(min(190px,100%),1fr));
grid-auto-rows: auto;
gap: 14px;
}
.cgl-15 .cgl-15__card {
grid-row: span 4;
display: grid;
grid-template-rows: subgrid;
row-gap: .6rem;
background: oklch(0.25 0.03 175);
border: 1px solid color-mix(in oklab,var(--mint) 25%,transparent);
border-radius: 16px;
padding: 1.2rem;
}
.cgl-15 .cgl-15__card--hot {
background: color-mix(in oklab,var(--mint) 14%,oklch(0.25 0.03 175));
border-color: var(--mint);
}
.cgl-15 .cgl-15__eyebrow {
font-size: .66rem;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--mint);
}
.cgl-15 h4 {
font-size: 1.05rem;
line-height: 1.3;
color: white;
text-wrap: balance;
}
.cgl-15 .cgl-15__desc {
font-size: .8rem;
line-height: 1.6;
color: oklch(0.75 0.02 175);
}
.cgl-15 .cgl-15__btn {
align-self: end;
justify-self: start;
font-size: .82rem;
font-weight: 600;
color: var(--ink);
background: var(--mint);
text-decoration: none;
padding: .55rem 1rem;
border-radius: 999px;
min-block-size: 2.4rem;
display: inline-grid;
align-items: center;
}
.cgl-15 .cgl-15__btn:hover {
background: color-mix(in oklab,var(--mint) 85%,white);
}
.cgl-15 .cgl-15__btn:focus-visible {
outline: 2px solid var(--mint);
outline-offset: 3px;
}
@supports not (grid-template-rows: subgrid) {
.cgl-15 .cgl-15__card {
grid-template-rows: auto auto 1fr auto;
}
}/* No JavaScript — grid-template-rows: subgrid makes all cards share the parent's row tracks, aligning every section across cards. */
/* No JavaScript — grid-template-rows: subgrid makes all cards share the parent's row tracks, aligning every section across cards. */Here's a working CSS Grid Layout from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Subgrid Card
Source: https://codefronts.com/layouts/css-grid-layouts/subgrid-card/
The card-alignment problem, finally solved: titles of different lengths, descriptions of different depths — yet every title baseline, every description block and every button aligns PERFECTLY across cards, because each card inherits the parent's row tracks with grid-template-rows: subgrid. The demo shows misaligned vs aligned side by side.
## HTML
```html
<section class="cgl-15" aria-label="Subgrid card alignment demo">
<div class="cgl-15__grid">
<article class="cgl-15__card">
<p class="cgl-15__eyebrow">Starter</p>
<h4>Ship your first project</h4>
<p class="cgl-15__desc">Every card spans the same four parent rows.</p>
<a href="#0" class="cgl-15__btn">Choose Starter</a>
</article>
<article class="cgl-15__card cgl-15__card--hot">
<p class="cgl-15__eyebrow">Studio · most popular</p>
<h4>A much longer title that wraps onto two full lines</h4>
<p class="cgl-15__desc">The title track grows for the tallest title — and every sibling's title track grows with it. That is subgrid.</p>
<a href="#0" class="cgl-15__btn">Choose Studio</a>
</article>
<article class="cgl-15__card">
<p class="cgl-15__eyebrow">Agency</p>
<h4>Scale it</h4>
<p class="cgl-15__desc">Short title, short copy — the button still lands on the shared final track, perfectly level with its neighbours.</p>
<a href="#0" class="cgl-15__btn">Choose Agency</a>
</article>
</div>
</section>
```
## CSS
```css
.cgl-15,
.cgl-15 *,
.cgl-15 *::before,
.cgl-15 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-15 {
width: 100%;
min-height: 100vh;
--mint: oklch(0.85 0.12 165);
--ink: oklch(0.24 0.03 180);
--bg: oklch(0.2 0.03 175);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.4rem;
}
.cgl-15 .cgl-15__grid {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(min(190px,100%),1fr));
grid-auto-rows: auto;
gap: 14px;
}
.cgl-15 .cgl-15__card {
grid-row: span 4;
display: grid;
grid-template-rows: subgrid;
row-gap: .6rem;
background: oklch(0.25 0.03 175);
border: 1px solid color-mix(in oklab,var(--mint) 25%,transparent);
border-radius: 16px;
padding: 1.2rem;
}
.cgl-15 .cgl-15__card--hot {
background: color-mix(in oklab,var(--mint) 14%,oklch(0.25 0.03 175));
border-color: var(--mint);
}
.cgl-15 .cgl-15__eyebrow {
font-size: .66rem;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--mint);
}
.cgl-15 h4 {
font-size: 1.05rem;
line-height: 1.3;
color: white;
text-wrap: balance;
}
.cgl-15 .cgl-15__desc {
font-size: .8rem;
line-height: 1.6;
color: oklch(0.75 0.02 175);
}
.cgl-15 .cgl-15__btn {
align-self: end;
justify-self: start;
font-size: .82rem;
font-weight: 600;
color: var(--ink);
background: var(--mint);
text-decoration: none;
padding: .55rem 1rem;
border-radius: 999px;
min-block-size: 2.4rem;
display: inline-grid;
align-items: center;
}
.cgl-15 .cgl-15__btn:hover {
background: color-mix(in oklab,var(--mint) 85%,white);
}
.cgl-15 .cgl-15__btn:focus-visible {
outline: 2px solid var(--mint);
outline-offset: 3px;
}
@supports not (grid-template-rows: subgrid) {
.cgl-15 .cgl-15__card {
grid-template-rows: auto auto 1fr auto;
}
}
```
## JavaScript
```js
/* No JavaScript — grid-template-rows: subgrid makes all cards share the parent's row tracks, aligning every section across cards. */
```Here's a working CSS Grid Layout from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Subgrid Card
Source: https://codefronts.com/layouts/css-grid-layouts/subgrid-card/
The card-alignment problem, finally solved: titles of different lengths, descriptions of different depths — yet every title baseline, every description block and every button aligns PERFECTLY across cards, because each card inherits the parent's row tracks with grid-template-rows: subgrid. The demo shows misaligned vs aligned side by side.
## HTML
```html
<section class="cgl-15" aria-label="Subgrid card alignment demo">
<div class="cgl-15__grid">
<article class="cgl-15__card">
<p class="cgl-15__eyebrow">Starter</p>
<h4>Ship your first project</h4>
<p class="cgl-15__desc">Every card spans the same four parent rows.</p>
<a href="#0" class="cgl-15__btn">Choose Starter</a>
</article>
<article class="cgl-15__card cgl-15__card--hot">
<p class="cgl-15__eyebrow">Studio · most popular</p>
<h4>A much longer title that wraps onto two full lines</h4>
<p class="cgl-15__desc">The title track grows for the tallest title — and every sibling's title track grows with it. That is subgrid.</p>
<a href="#0" class="cgl-15__btn">Choose Studio</a>
</article>
<article class="cgl-15__card">
<p class="cgl-15__eyebrow">Agency</p>
<h4>Scale it</h4>
<p class="cgl-15__desc">Short title, short copy — the button still lands on the shared final track, perfectly level with its neighbours.</p>
<a href="#0" class="cgl-15__btn">Choose Agency</a>
</article>
</div>
</section>
```
## CSS
```css
.cgl-15,
.cgl-15 *,
.cgl-15 *::before,
.cgl-15 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-15 {
width: 100%;
min-height: 100vh;
--mint: oklch(0.85 0.12 165);
--ink: oklch(0.24 0.03 180);
--bg: oklch(0.2 0.03 175);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.4rem;
}
.cgl-15 .cgl-15__grid {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(min(190px,100%),1fr));
grid-auto-rows: auto;
gap: 14px;
}
.cgl-15 .cgl-15__card {
grid-row: span 4;
display: grid;
grid-template-rows: subgrid;
row-gap: .6rem;
background: oklch(0.25 0.03 175);
border: 1px solid color-mix(in oklab,var(--mint) 25%,transparent);
border-radius: 16px;
padding: 1.2rem;
}
.cgl-15 .cgl-15__card--hot {
background: color-mix(in oklab,var(--mint) 14%,oklch(0.25 0.03 175));
border-color: var(--mint);
}
.cgl-15 .cgl-15__eyebrow {
font-size: .66rem;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--mint);
}
.cgl-15 h4 {
font-size: 1.05rem;
line-height: 1.3;
color: white;
text-wrap: balance;
}
.cgl-15 .cgl-15__desc {
font-size: .8rem;
line-height: 1.6;
color: oklch(0.75 0.02 175);
}
.cgl-15 .cgl-15__btn {
align-self: end;
justify-self: start;
font-size: .82rem;
font-weight: 600;
color: var(--ink);
background: var(--mint);
text-decoration: none;
padding: .55rem 1rem;
border-radius: 999px;
min-block-size: 2.4rem;
display: inline-grid;
align-items: center;
}
.cgl-15 .cgl-15__btn:hover {
background: color-mix(in oklab,var(--mint) 85%,white);
}
.cgl-15 .cgl-15__btn:focus-visible {
outline: 2px solid var(--mint);
outline-offset: 3px;
}
@supports not (grid-template-rows: subgrid) {
.cgl-15 .cgl-15__card {
grid-template-rows: auto auto 1fr auto;
}
}
```
## JavaScript
```js
/* No JavaScript — grid-template-rows: subgrid makes all cards share the parent's row tracks, aligning every section across cards. */
```How this works
The parent grid declares grid-auto-rows: auto and the cards span four rows: grid-row: span 4 with display: grid; grid-template-rows: subgrid. Now the eyebrow, title, description and button of EVERY card share the same four parent tracks — each track sized by the tallest instance across the whole row. A two-line title in card 2 makes the title track taller for all cards; buttons all sit on the final track's baseline.
This is impossible with nested independent grids (each card would size its own rows) and hacky with flexbox (margin-top:auto only pins the LAST element). Subgrid is the first mechanism where siblings' internals align to each other. The gap is inherited from the parent but can be overridden locally.
Make it yours
- Add a card section (image, price row) by increasing the span AND adding one element per card.
- Cap description length with
-webkit-line-clampif a single rambling card making all cards tall is unacceptable. - Works identically for pricing tables, product cards, team bios — anything repeated.
- The columns can subgrid too (grid-template-columns: subgrid) for icon+text alignment inside rows.
Gotchas — read before shipping
- The span count must equal the number of card children exactly — a forgotten element shifts every subsequent track for that card only.
- Subgrid tracks are the PARENT's: you cannot resize one card's title track locally. That rigidity is the feature.
- Test the @supports fallback — pre-subgrid Safari 15 users get independently-sized cards (graceful, but check it).
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 117+ | 16+ | 71+ | 117+ |
subgrid is fully interoperable since Sept 2023 — Baseline 2023. The @supports fallback covers the stragglers.