CSS Grid in Depth
Prerequisites: CSS Grid basics
Named areas
grid-template-areas draws the layout as text:
.page {
display: grid;
grid-template-columns: 14rem 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 1rem;
}
.page > header { grid-area: header; }
.page > nav { grid-area: sidebar; }
.page > main { grid-area: main; }
.page > footer { grid-area: footer; }
@media (width < 40rem) {
.page {
grid-template-columns: 1fr;
grid-template-areas: "header" "main" "sidebar" "footer";
}
}
Each string is a row; repeating a name spans cells; a . leaves a cell
empty. Areas must be rectangles. Changing the layout for small screens is
just a new map.
Remember that screen readers and keyboard users follow the HTML order, not the visual grid order. Keep them consistent.
Implicit tracks and auto-placement
.feed {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(10rem, auto);
grid-auto-flow: dense;
}
grid-auto-rowssizes rows created automatically.grid-auto-flow: denseback-fills holes left by large items (which can put items out of source order visually).
Named lines
.article {
display: grid;
grid-template-columns:
[full-start] minmax(1rem, 1fr)
[content-start] minmax(0, 65ch)
[content-end] minmax(1rem, 1fr)
[full-end];
}
.article > * { grid-column: content; }
.article > .full-bleed { grid-column: full; }
Text sits in a readable center column; images marked .full-bleed
stretch edge to edge. content refers to the area between
content-start and content-end.
subgrid
Nested grids normally have their own tracks, so the titles, texts, and
buttons of neighboring cards don't line up. subgrid lets a child grid
reuse its parent's tracks:
<style>
.cards { display: grid; grid-template-columns: repeat(2, 200px); gap: 0 20px; }
.card { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
</style>
<div class="cards">
<article class="card"><h3>Short</h3><p>One line.</p><a href="/a">Buy</a></article>
<article class="card"><h3>A much longer title that wraps onto several lines</h3><p>One line.</p><a href="/b">Buy</a></article>
</div>
<script>
const [a, b] = [...document.querySelectorAll(".card")];
console.log("paragraphs aligned:", a.querySelector("p").getBoundingClientRect().top === b.querySelector("p").getBoundingClientRect().top);
console.log("buttons aligned:", a.querySelector("a").getBoundingClientRect().top === b.querySelector("a").getBoundingClientRect().top);
</script>
paragraphs aligned: true
buttons aligned: true
Both cards span three rows of the parent grid and use them for their
title, text, and link, so the long title pushes the text down in
both cards. subgrid works in all major browsers (Firefox 71,
Safari 16, Chrome and Edge 117).
Masonry-style layouts
A native masonry layout, display: grid-lanes, is being standardized
(Safari 26.4 was first to ship it). Until it's widely available, use CSS
columns (columns: 3) for simple cases.
Practice
Write a grid-template-areas layout for a dashboard: a header across
the top, a narrow nav on the left, main content, and a right-hand
"stats" column; below 50rem, stack everything in one column.
Show solution
.dashboard {
display: grid;
grid-template-columns: 12rem 1fr 16rem;
grid-template-areas:
"header header header"
"nav main stats";
gap: 1rem;
}
@media (width < 50rem) {
.dashboard {
grid-template-columns: 1fr;
grid-template-areas: "header" "nav" "main" "stats";
}
}
.dashboard > header { grid-area: header; }
.dashboard > nav { grid-area: nav; }
.dashboard > main { grid-area: main; }
.dashboard > aside { grid-area: stats; }