RizTech Academy logo
RizTech Academy
LayoutLesson 3 of 640 min

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

  1. Build a three-column grid with repeat(3, 1fr) and a gap. Turn on the devtools overlay and read the line numbers.
  2. Compare repeat(3, 33.33%) and repeat(3, 1fr), both with gap: 1rem.
  3. Put a 200-character unbroken string in a 1fr column and watch the grid overflow. Fix it with minmax(0, 1fr).
  4. Build a 200px 1fr sidebar layout.
  5. Use max-content 1fr for a label-and-value list.
  6. Write repeat(auto-fit, minmax(260px, 1fr)) and resize from 1400px to 320px. Count the column changes.
  7. Put three items in that grid, then swap auto-fit for auto-fill. Describe the difference.
  8. Make one item span two columns with span 2, then make another full-width with 1 / -1. Change the column count and confirm the full-width one still works.
  9. Build the four-area page layout with grid-template-areas.
  10. Rearrange it for mobile in a media query, then tab through the page and check the focus order still makes sense.
  11. Give one row string a different number of columns and find out what breaks.
  12. Centre a box with place-items: center in two properties.
  13. Make a grid narrower than its container and centre it with place-content, then try place-items and note that it does something different.
  14. Add grid-auto-rows: minmax(150px, auto) to a gallery with uneven content.
  15. Add grid-auto-flow: dense to a grid with a spanned item and compare visual order with DOM order.
  16. Build a row of cards whose footers align using subgrid.

Official documentation

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