CSS Grid
Grid lays things out in two dimensions — rows and columns at the same time, with items aligned to both. That is the difference from Flexbox, and it is the whole reason to learn it.
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
Three equal columns, even gaps, rows created automatically as items arrive. Before Grid this needed percentages, negative margins and a clearfix.
Turn on the Grid overlay in devtools before you read any further. In the Elements
panel, a grid badge appears next to any grid container — click it and the browser
draws every line, numbers them, and shades the gaps. Grid is much harder to learn
without it and almost easy with it.
Defining the tracks
grid-template-columns: 200px 200px 200px; /* fixed */
grid-template-columns: 1fr 1fr 1fr; /* three equal fractions */
grid-template-columns: 200px 1fr; /* sidebar and content */
grid-template-columns: repeat(3, 1fr); /* same as 1fr 1fr 1fr */
grid-template-columns: repeat(2, 1fr) 2fr; /* mix */
grid-template-rows: auto 1fr auto; /* header, body, footer */
fr, which is the unit that makes Grid work
1fr is one share of the leftover space, after fixed tracks and gaps are taken
out. So 200px 1fr is a 200px sidebar and a content column that takes everything
else, with no calc() and no arithmetic.
The important difference from %: 1fr accounts for gap automatically.
repeat(3, 33.33%) with a gap overflows; repeat(3, 1fr) does not.
One fr gotcha, and it is the Grid equivalent of Flexbox's min-width: 0:
1fr means minmax(auto, 1fr), so a track will not shrink below its content's
minimum size. A long unbroken string in a 1fr column pushes the grid wider than
its container. The fix:
grid-template-columns: repeat(3, minmax(0, 1fr));
minmax(0, 1fr) lets the track shrink to nothing. Once you have been caught by this
you will type minmax(0, 1fr) by reflex.
minmax(), min-content, max-content, auto
grid-template-columns: minmax(200px, 1fr) 2fr; /* at least 200, then flexible */
grid-template-columns: max-content 1fr; /* as wide as its longest line */
grid-template-columns: min-content 1fr; /* as narrow as its longest word */
grid-template-columns: auto 1fr; /* content-sized, but stretchable */
max-content is useful for a label column: exactly as wide as the longest label,
with no guessing.
The responsive one-liner
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.5rem;
}
This is the single most useful thing in Grid. As many columns as fit at 260px minimum, stretching to fill; fewer columns as the screen narrows; one column on a phone. No media queries at all.
auto-fit versus auto-fill is the classic interview question and the difference is
real: with fewer items than columns, auto-fill keeps the empty tracks (so three
cards in a five-column grid stay card-width and sit on the left), while auto-fit
collapses them (so three cards stretch to fill the row). auto-fit is usually what
looks right.
Placing items
You can let Grid auto-place everything — which is right most of the time — or place items explicitly.
.item {
grid-column: 1 / 3; /* from line 1 to line 3, so two columns wide */
grid-row: 2 / 4;
grid-column: span 2; /* two columns wide, wherever it lands */
grid-column: 1 / -1; /* the full width, whatever the column count */
}
Lines, not tracks. A three-column grid has four vertical lines, numbered 1 to 4,
and -1 counts from the end. grid-column: 1 / -1 for a full-width row is the one
to remember — it survives a change in column count.
Named areas, which are worth it for a page layout
.page {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100dvh;
gap: 1rem;
}
.page > header { grid-area: header; }
.page > .side { grid-area: sidebar; }
.page > main { grid-area: main; }
.page > footer { grid-area: footer; }
The CSS is a picture of the layout. Six months later you can read it, which is not true of line numbers. Rearranging for mobile is then one block:
@media (max-width: 700px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
}
Note the sidebar moved below main — and because grid-template-areas changes
visual order only, the DOM order is unchanged. The order warning from the Flexbox
lesson applies: make sure the HTML order still reads sensibly, because that is what a
keyboard and a screen reader follow.
A . in the template means a deliberately empty cell, and every row string must have
the same number of columns or the whole declaration is invalid.
Alignment in Grid
Grid has both axes, so there are four properties plus two shorthands:
.grid {
justify-content: center; /* the whole grid, along the inline axis */
align-content: center; /* the whole grid, along the block axis */
justify-items: start; /* every item within its cell, inline */
align-items: center; /* every item within its cell, block */
place-items: center; /* align-items + justify-items */
place-content: center; /* align-content + justify-content */
}
.item {
justify-self: end; /* one item, inline */
align-self: start; /* one item, block */
place-self: center;
}
The distinction: *-content moves the tracks inside the container; *-items moves
the items inside their cells. If your grid is smaller than its container and you
want it centred, that is place-content. If your items are smaller than their cells,
that is place-items.
And the shortest centring in CSS:
.centre {
display: grid;
place-items: center;
min-height: 100dvh;
}
Two properties. place-items works properly in Grid, unlike in Flexbox where
justify-items is ignored.
Implicit tracks
Items that do not fit your explicit tracks create implicit ones, sized by
grid-auto-rows:
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(150px, auto);
gap: 1rem;
}
Every row is at least 150px and grows with its content. Without it, implicit rows are
auto — sized to content, so a row with one short card is short.
grid-auto-flow: row; /* default */
grid-auto-flow: column;
grid-auto-flow: dense; /* backfill gaps left by spanned items */
dense fills holes by pulling later items into them, which means the visual order
stops matching the DOM order in a way you did not choose. Same warning as order.
Subgrid
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}
subgrid lets a child use its parent's tracks, so a row of cards can align their
headings, bodies and footers with each other even when the text lengths differ.
Before subgrid this needed a single flat grid and careful placement. It is supported
in all current browsers; check caniuse.com if you support older ones.
Grid and Flexbox together
They are not competitors, and the usual real answer uses both:
.cards { /* Grid: the two-dimensional arrangement */
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.5rem;
}
.card { /* Flexbox: the one-dimensional inside */
display: flex;
flex-direction: column;
}
.card__footer { margin-top: auto; }
Grid for the page and the collection; Flexbox for the contents of a component. The next lesson is entirely about choosing.
Check your work
What Grid is for. Two dimensions — rows and columns aligned together.
The first thing to do when learning it. Turn on the devtools Grid overlay.
What 1fr is a fraction of. The leftover space, after fixed tracks and gaps.
Why 1fr sometimes overflows. It is minmax(auto, 1fr), so it will not shrink
below its content. Use minmax(0, 1fr).
The most useful line in Grid. repeat(auto-fit, minmax(260px, 1fr)) — responsive
with no media queries.
auto-fit versus auto-fill. Collapses empty tracks, versus keeps them.
What the numbers in grid-column: 1 / 3 are. Lines, not tracks.
How to make a full-width row. grid-column: 1 / -1.
Why named areas are worth it. The CSS is a picture of the layout, and a mobile rearrangement is one block.
What invalidates grid-template-areas. Rows with different numbers of columns.
*-content versus *-items. Moves the tracks in the container, versus the items
in their cells.
The shortest centring in CSS. display: grid; place-items: center.
What grid-auto-rows is for. Sizing the implicit rows your items create.
What dense and order have in common. Both break the match between visual and
DOM order.
What subgrid solves. Aligning the insides of sibling cards to each other.
Practice
- Build a three-column grid with
repeat(3, 1fr)and a gap. Turn on the devtools overlay and read the line numbers. - Compare
repeat(3, 33.33%)andrepeat(3, 1fr), both withgap: 1rem. - Put a 200-character unbroken string in a
1frcolumn and watch the grid overflow. Fix it withminmax(0, 1fr). - Build a
200px 1frsidebar layout. - Use
max-content 1frfor a label-and-value list. - Write
repeat(auto-fit, minmax(260px, 1fr))and resize from 1400px to 320px. Count the column changes. - Put three items in that grid, then swap
auto-fitforauto-fill. Describe the difference. - Make one item span two columns with
span 2, then make another full-width with1 / -1. Change the column count and confirm the full-width one still works. - Build the four-area page layout with
grid-template-areas. - Rearrange it for mobile in a media query, then tab through the page and check the focus order still makes sense.
- Give one row string a different number of columns and find out what breaks.
- Centre a box with
place-items: centerin two properties. - Make a grid narrower than its container and centre it with
place-content, then tryplace-itemsand note that it does something different. - Add
grid-auto-rows: minmax(150px, auto)to a gallery with uneven content. - Add
grid-auto-flow: denseto a grid with a spanned item and compare visual order with DOM order. - Build a row of cards whose footers align using
subgrid.
Official documentation
- MDN — Basic concepts of grid layout — Tracks, lines,
frandminmax(), with every property listed. - MDN — Grid template areas — Named areas in full, including the rules that invalidate a template.
- MDN — Auto-placement in grid layout — Implicit tracks,
grid-auto-flowand whatdensereally does. - MDN — Subgrid — Aligning the insides of sibling cards to each other.
- Firefox DevTools — CSS Grid Inspector — The best Grid overlay of any browser. Worth installing Firefox for.
Next: which of the two to reach for.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship