Command Palette

Search for a command to run...

Hectal
PHASE 8Intermediate ~28 min· topic 1 of 6

Topic 8.1

Grid Basics

In one line

display: grid turns an element into a grid container; grid-template-columns and grid-template-rows draw its tracks, the fr unit shares out leftover space, and gap puts gutters between tracks, while the children fill the cells in order.

Think of it like this

An egg tray. The tray has fixed rows and columns of cups before any egg arrives. You put eggs in one by one, left to right, row by row, and each egg lands neatly in a cup. You never measure where an egg goes: the tray decides. A CSS grid is that tray: you draw the rows and columns on the parent, and the children drop into the cups.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Grid container
The element with display: grid. It owns the rows and columns.
Grid item
A direct child of the grid container. It is placed into the grid's cells.
Grid line
An invisible dividing line between tracks, numbered from 1. Three columns have four lines.
Track
The space between two neighbouring grid lines: one column or one row.
Cell
The box where one row track crosses one column track.
Grid area
A rectangle made of one or more cells, which an item can fill.
`fr` unit
A share of the free space left after fixed sizes and gaps are taken away.
Free space
The container's size minus all fixed tracks, content-sized tracks and gaps.
`gap`
The gutter between rows and between columns. Never added around the outside.
Auto-placement
The browser putting items into the next empty cell, in order, when you don't place them yourself.

Step by step

01Turn on grid

Six boxes inside a .grid parent. Add display: grid to the parent. The boxes still stack, because a grid with no column template has a single column. But they are now grid items: gaps and tracks will apply to them.

style.csswhole filecss
.grid {
  display: grid;
}

02Draw three columns

List one size per column. Now the six boxes flow into two rows of three: auto-placement fills row 1 left to right, then starts row 2.

style.csswhole filecss
.grid {
  display: grid;
  grid-template-columns: 100px 1fr 2fr;
}

03See the lines, tracks and cells

Three column tracks need four column lines. Two rows need three row lines. Each crossing of a row and a column is a cell. DevTools draws these lines for you: click the small grid badge next to the element in the Elements panel.

See the lines, tracks and cellsdiagram
Rendering diagram…

04Add gutters with gap

gap: 10px puts 10px between columns and between rows, but not around the outside. The gaps come out of the space before fr is shared, so the fr columns get a little narrower.

style.csswhole filecss
.grid {
  display: grid;
  grid-template-columns: 100px 1fr 2fr;
  gap: 10px;
}

05Work out the fr sizes by hand

Container 420px. Take away the fixed column (100px) and the two gaps (20px): 300px of free space. The fr numbers add up to 3, so one fr is 100px. Column 2 is 1fr = 100px, column 3 is 2fr = 200px. The console in the first example prints exactly that.

Work out the fr sizes by handdiagram
Rendering diagram…

06Size the rows too

grid-template-rows: 60px auto makes the first row 60px tall and the second as tall as its tallest item. Without it, every row is auto.

style.csswhole filecss
.grid {
  display: grid;
  grid-template-columns: 100px 1fr 2fr;
  grid-template-rows: 60px auto;
  gap: 10px;
}

Try it yourself

  1. 1

    Predict the fr sizes

    In the first example, change the columns to 120px 1fr 1fr and keep gap: 10px. Work it out first: 420 − 120 − 20 = 280px of free space, so each 1fr is 140px. Run and check the console.

  2. 2

    Make the percentages fit

    In the second example, change .pct to calc(50% - 10px) calc(50% - 10px). Predict the right edge (400). That's the arithmetic fr does for you, which is why fr is the usual choice.

  3. 3

    Cause and cure a blowout

    In the third example, change .wide to width: 380px. Predict the .a columns (380px 20px) and the .b columns (still 200px 200px). Then add overflow: hidden to .grid > div and see the content get clipped inside its fair column in .b.

Code & diagrams

Columns, fr and gap New tab

`getComputedStyle` returns the **resolved** column sizes in px, so `1fr 2fr` comes back as `100px 200px`. Change `gap` to `30px` and work out the new sizes before you run.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

columns: 100px 100px 200px
gaps: 10px 10px
item 1 width: 100
item 2 width: 100
item 3 width: 200
item 4 is on a new row: true
fr subtracts the gaps, percentages don't New tab

The dashed outline is the 400px container. With percentages, the second column ends at 420px: it sticks out by exactly one gap. `fr` shares what is left after the gap, so it fits.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

.pct column width: 200 right edge: 420
.fr column width: 190 right edge: 400
1fr has a minimum: the blowout New tab

In `.a` the first column refuses to shrink below its content (300px), so the second gets only 100px. In `.b` the columns stay equal and the green content overflows into column 2.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

.a 300px 100px
.b 200px 200px
Grid vocabularydiagram
Rendering diagram…

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Put display: grid on the wrong element

In the first example, move display: grid and grid-template-columns from .grid to .grid > div.

terminal
$ the page shows
── what you'll see ──
The six boxes stack in one column again; nothing is in columns. The console prints columns: none.

Break #2

Use percentages with a gap

Write grid-template-columns: 25% 25% 25% 25%; gap: 16px; on a full-width grid.

terminal
$ the page shows
── what you'll see ──
The last column hangs 48px past the right edge, and a horizontal scrollbar appears on a narrow screen.

Myth vs fact

Myth

display: grid affects every element inside the container.

Fact

Only the direct children become grid items. Grandchildren are laid out normally inside their parent item, unless that item is itself a grid (or a subgrid).

Myth

1fr always means equal columns.

Fact

1fr is minmax(auto, 1fr): a column never shrinks below its content's minimum size. Wide content makes its column bigger. Use minmax(0, 1fr) when columns must stay equal.

Myth

Gaps are just margins on the items.

Fact

Gaps belong to the grid, not the items. They appear only between tracks, never at the outer edges, and they don't collapse or add up the way margins do.

Myth

Grid is only for whole-page layouts.

Fact

Grid is just as good for small components: a form with label and input columns, a card's icon-and-text row, a calendar, a keypad.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    The track sizing algorithm runs in stages: fixed tracks get their size, then intrinsic tracks (auto, min-content, max-content) are sized from their items' contributions, then fr tracks share the free space, and finally auto tracks are stretched if justify-content/align-content is normal or stretch. An fr track whose share would be smaller than its base size is frozen at that base size and the share is recomputed, which is exactly the blowout in the third example.

  • ▸

    getComputedStyle(el).gridTemplateColumns returns the used track sizes in px, including implicit tracks, not what you wrote. To read what you wrote, look at the stylesheet rule (el.style or the CSSOM) or DevTools' Styles pane.

  • ▸

    Grid items are blockified: an <a> or <span> child gets a block-level display, float is ignored on them, vertical-align has no effect, and margins between items don't collapse. Text directly inside the container is wrapped in an anonymous grid item, which you can't select or style.

  • ▸

    Firefox's Grid Inspector and Chrome's grid overlay (the grid badge in Elements, and the Layout pane) draw line numbers, track sizes and area names on the page. They're the fastest way to debug any grid in this phase.

Remember this

  1. 1

    display: grid makes an element a grid container. Its direct children (not grandchildren) become grid items. Nothing visible changes until you describe the columns: with no column template, a grid has one column, so the items still stack, now with grid rules applied. display: inline-grid does the same but the container itself sits in a line of text like an inline-block. Both values are about the container; the items are always laid out the grid way.

  2. 2

    A grid is made of lines, and the space between two neighbouring lines is a track: a column track runs top to bottom, a row track runs left to right. Where one row crosses one column you get a cell, the smallest piece of the grid. A rectangle of several cells is a grid area. Lines are numbered from 1: a three-column grid has four column lines, 1 to 4. You'll place items by these numbers in Topic 8.3.

  3. 3

    grid-template-columns lists the width of each column track, separated by spaces: grid-template-columns: 200px 1fr 2fr makes three columns. grid-template-rows does the same for rows: grid-template-rows: 80px auto. Every value in the list is one track. You can use any length (px, rem, %), auto (sized by the content, and it also grows to soak up spare space), and the grid's own unit, fr.

  4. 4

    **fr means "a fraction of the leftover space**". The browser first takes away fixed tracks (200px), content-sized tracks (auto) and the gaps from the container's width. What remains is the free space, split between the fr tracks in proportion to their numbers. In a 420px grid with 100px 1fr 2fr and two 10px gaps, the free space is 420 − 100 − 20 = 300px, so 1fr is 100px and 2fr is 200px. Percentages don't work this way: 50% 50% is half of the container each, and the gap is added on top, so the grid overflows.

  5. 5

    gap sets the gutters between tracks: gap: 16px for both directions, or gap: 12px 24px for row gap then column gap (longhands row-gap and column-gap). Gaps go only between tracks, never around the outside edge, so you don't need the "remove the last margin" tricks of older layouts. The old name grid-gap still works as an alias.

  6. 6

    Children fill the grid by auto-placement: the first item goes into row 1 column 1, the next into column 2, and so on; when a row is full, the next item starts a new row. If there are more items than the rows you defined, the grid creates extra rows by itself (the implicit grid, Topic 8.2). Grid items behave differently from normal blocks: their margins never collapse with each other, float on an item is ignored, and inline children (like a bare <span> or text) become block-level items.

  7. 7

    One subtle rule causes most "my grid is too wide" bugs: 1fr is really minmax(auto, 1fr). Its minimum is auto, which for a grid item means "at least as wide as my content can shrink to". A long URL, a wide image or a <pre> block makes its column grow past its fair share and squeezes the others. Writing minmax(0, 1fr) removes that minimum, so columns stay equal and the content overflows or wraps instead.

Explain it without notes

01

What happens to an element and its children when you set display: grid?

02

How does the browser calculate the size of an fr track?

03

Why does 50% 50% with a gap overflow, while 1fr 1fr doesn't?

04

Why can a 1fr column end up much wider than its neighbours, and how do you stop it?

Practice

01

Make a grid with a fixed 240px sidebar column and a main column that takes the rest, with a 24px gap.

02

A 600px-wide grid has grid-template-columns: 1fr 2fr 1fr and gap: 20px. How wide is each column?

03

Build a 3 by 2 grid of equal boxes where each row is exactly 100px tall and the gaps are 12px.

Trade-offs

  • ↔

    fr columns adapt to the container but can grow to fit wide content; minmax(0, 1fr) keeps them equal but makes overflow your job (wrap, scroll or clip).

  • ↔

    Fixed px tracks are predictable but don't adapt to narrow screens; combine them with fr or the functions in Topic 8.2 so at least one track can flex.

Done when you can

  • I can turn an element into a grid and say which elements become grid items.

  • I can name grid lines, tracks, cells and areas, and count the lines of a grid.

  • I can calculate fr sizes by hand from the container width, fixed tracks and gaps.

  • I know why % tracks plus gap overflow and fr tracks don't.

  • I can fix a grid blowout with minmax(0, 1fr).