CSS Grid is the shortest route to card lists and page skeletons—yet 1fr and repeat(auto-fit, minmax(...)) are exactly the kind of thing you can never quite recall. This article is a quick reference to the Grid properties you actually use, with live demos.
The whole map
Grid’s properties, like Flexbox’s, come in two kinds: the ones you put on the parent (the container) and the ones you put on a child (an item).
| Where it goes | Property | What it does |
|---|---|---|
| Parent | display: grid | The starting signal |
| Parent | grid-template-columns | The number and width of columns |
| Parent | grid-template-rows | The number and height of rows |
| Parent | gap | The space between cells |
| Parent | justify-items | Horizontal alignment inside each cell |
| Parent | align-items | Vertical alignment inside each cell |
| Parent | grid-template-areas | Name the cells and place by name |
| Child | grid-column | Which columns it spans |
| Child | grid-row | Which rows it spans |
| Child | justify-self / align-self | Change alignment for this one only |
In practice, grid-template-columns and gap cover 80%. Get those and you can start building.
grid-template-columns—the blueprint for columns
The star of the show. You write “how many columns, at what widths,” separated by spaces. Press the buttons and compare how the values behave.
Cells line up across for as many columns as you declared, and when they run out, they move to the next row automatically. So “six boxes in two columns” gives you three rows without you counting. You never declare the row count.
The fr unit
fr is short for fraction, and it means “a unit for sharing out the space that’s left.” It is not a fixed length like px.
| What you write | What it means |
|---|---|
1fr 1fr | Half and half |
2fr 1fr | Split two to one |
100px 1fr | First column fixed at 100px, second takes the rest |
1fr auto | First column stretches, second is as wide as its content |
“px for columns you want fixed, fr for columns you want to stretch”—that’s the most common shape in Grid. A sidebar-plus-main two-column layout is done with 240px 1fr.
repeat()—shorten a repeated pattern
Repetition like 1fr 1fr 1fr 1fr can be collapsed with repeat().
.grid {
grid-template-columns: repeat(3, 1fr); /* 1fr three times = 3 equal columns */
grid-template-columns: repeat(2, 100px 1fr); /* 100px 1fr twice = 4 columns */
}The first argument is how many times, the second is what to repeat. Change the count in one place and you’re done.
Responsive without media queries
This is the single most rewarding thing to learn in Grid: a way to write columns that change count with the screen width, on their own.
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}Read it like this.
minmax(240px, 1fr)—one card is “at least 240px, at most a share of what’s left”auto-fit—place as many as fit under that rule, and wrap when they don’t
So the judgement “if it would go under 240px, use fewer columns” is made by the browser. That one line finishes phone support for a card list. Drag the width in the demo below to see it.
gap—the space between cells
.grid {
gap: 16px; /* 16px both ways */
gap: 24px 16px; /* 24px between rows, 16px between columns */
}Unlike spacing each item with margin, this leaves no stray space on the outside. If you’re using Grid, hand your spacing to gap.
grid-template-rows—row heights
Rows work the same way as columns. Leave it out and rows are sized to their content.
.layout {
grid-template-rows: 80px 1fr 60px; /* header, body, footer */
min-height: 100vh;
}Putting 1fr in the middle gives you the layout where the footer sticks to the bottom even when there’s little content.
Properties for children
grid-column / grid-row—span across cells
For “just this one card should take two columns.” span is the readable form.
.wide {
grid-column: span 2; /* two cells across */
}
.tall {
grid-row: span 2; /* two cells down */
}You can also name the grid lines. grid-column: 1 / 3 means “from line 1 to line 3,” which is two cells. You count the lines, not the cells—that’s Grid’s mental model, and the first thing that trips people up.
.hero {
grid-column: 1 / -1; /* from the first line to the last = full width */
}-1 means “the rightmost line,” a handy way to stay full width even when the column count changes.
justify-self / align-self—change alignment for one item
Override the position within its own cell, for that child only.
.item {
justify-self: center; /* horizontally centred in its cell */
align-self: end; /* bottom-aligned in its cell */
}grid-template-areas—place by name
For a whole-page skeleton, naming the cells and drawing the layout reads beautifully.
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-areas:
"header header"
"side main"
"footer footer";
gap: 16px;
}
.layout > header { grid-area: header; }
.layout > aside { grid-area: side; }
.layout > main { grid-area: main; }
.layout > footer { grid-area: footer; }The strength is that the CSS shows you the shape of the layout. To rearrange it for phones, you rewrite grid-template-areas inside a media query and nothing else.
Five recipes you can use as-is
A responsive card list
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}Sidebar plus main content
.layout {
display: grid;
grid-template-columns: 240px 1fr;
gap: 24px;
}A gallery of square photos
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 8px;
}
.gallery img {
width: 100%;
aspect-ratio: 1 / 1; /* make it square */
object-fit: cover; /* crop the overflow */
}Make one item big
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.cards .featured {
grid-column: 1 / -1; /* full width */
}Dead centre
.center {
display: grid;
place-items: center; /* justify-items and align-items in one */
min-height: 100vh;
}place-items: center is the shortest way to centre both ways in one line. What took two lines in Flexbox takes one here.
Flexbox or Grid?
| What you’re building | The better fit |
|---|---|
| A nav menu or a row of buttons | Flexbox |
| A header (logo left, menu right) | Flexbox |
| A card list | Grid |
| The whole page skeleton | Grid |
| A photo gallery | Grid |
| A row whose widths follow the content | Flexbox |
| Cells that must line up in both directions | Grid |
Roughly: one direction is Flexbox, a grid of cells is Grid. When in doubt, decide on whether the columns need to line up. Flexbox doesn’t line up columns across wrapped rows; Grid always does.
TipsFlexbox cheat sheetThe Flexbox cheatsheet, with live demosChecklist when Grid isn’t working
- Nothing lines up →
display: gridgoes on the parent of the things you’re arranging gapdoes nothing →gapalso goes on the parent- Some child isn’t in a cell → only the direct children of the element with
display: gridland in cells. Have you wrapped them in an extradiv? - Columns overflow → are you adding up to 100% with
%or px? Switching tofrsubtracts thegapfor you align-itemsdoes nothing → if the row is exactly as tall as its content, there’s nothing to align within. Checkgrid-template-rowsormin-height- An image bursts out of its cell → give images
max-width: 100%(orwidth: 100%). A grid cell won’t shrink below its content’s minimum width, so a large image widens the column
Summary
- Learn
grid-template-columnsandgapfirst—they cover 80% of the work fris a unit for sharing out what’s left; unlike%, it never overflows when combined withgaprepeat(auto-fit, minmax(240px, 1fr))gives you a card list with no media queries- For a full-width child,
grid-column: 1 / -1. Counting lines is Grid’s mental model - One direction is Flexbox; if the cells must line up, it’s Grid
Lining up columns is work best handed to Grid. Write it once for a card list, and your fingers will remember it after that.