CSS Grid

Table of Contents

  1. Rows and Columns Together
  2. Defining Columns and Rows
  3. The fr Unit, repeat(), and gap
  4. Placing Items
  5. Named Areas: Draw Your Layout
  6. Responsive Grids Without Media Queries
  7. Alignment in Grid
  8. Grid or Flexbox?
  9. Recipe: Page Layout and Gallery
  10. Try It Yourself

1. Rows and Columns Together

Flexbox arranges items along one line. CSS Grid arranges them in two dimensions — rows and columns at once, like a spreadsheet or a newspaper layout. Reach for it when you want to line things up in both directions.

As with Flexbox, it starts with the parent:

.gallery {
  display: grid;
}

The parent becomes a grid container, and its direct children become grid items. On its own this changes little — you also have to describe the tracks (the columns and rows).

2. Defining Columns and Rows

grid-template-columns lists the width of each column, and grid-template-rows lists the height of each row:

.layout {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
}

That makes three columns: one fixed at 200px, and two sharing the rest. Children fill the cells from left to right, then start a new row when the columns run out. Rows are created automatically at the height of their content, so you often only need to define columns.

 200px         1fr            1fr
┌───────┬──────────────┬──────────────┐
│   1   │      2       │      3       │   row 1
├───────┼──────────────┼──────────────┤
│   4   │      5       │      6       │   row 2
└───────┴──────────────┴──────────────┘

3. The fr Unit, repeat(), and gap

The fr unit is a fraction of the free space in the grid. It's Grid's version of flex: 1:

DeclarationResult
1fr 1fr 1frThree equal columns
1fr 2frTwo columns; the second is twice as wide
200px 1frA fixed sidebar and a flexible main column

repeat() saves typing:

grid-template-columns: repeat(3, 1fr);   /* same as 1fr 1fr 1fr */

gap adds space between rows and columns, exactly as in Flexbox:

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

Three equal columns with a gap between, in four lines. You can also set row-gap and column-gap separately.

4. Placing Items

Normally items are placed automatically, in order. When you want one to span more than one cell, tell it:

.featured {
  grid-column: span 2;      /* take up two columns */
}

.hero {
  grid-column: 1 / -1;      /* from first line to the last line */
}

Grid lines are numbered from 1, and -1 is the last line, so 1 / -1 means "the full width". grid-row: span 2 does the same downwards. You can also name exact lines — grid-column: 2 / 4 starts at line 2 and ends at line 4.

5. Named Areas: Draw Your Layout

Grid's most readable feature lets you draw the layout as text with grid-template-areas:

.page {
  display: grid;
  grid-template-columns: 14rem 1fr;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  gap: 1rem;
  min-height: 100vh;
}

.page > header  { grid-area: header; }
.page > aside   { grid-area: sidebar; }
.page > main    { grid-area: main; }
.page > footer  { grid-area: footer; }

The strings are rows, and each word is a cell. Repeating a name spans the cells. The picture in the CSS matches the picture on screen:

┌──────────────────────────────┐
│           header             │
├──────────┬───────────────────┤
│ sidebar  │       main        │
├──────────┴───────────────────┤
│           footer             │
└──────────────────────────────┘

To rearrange for a phone, you only redefine the template (see chapter 11) — the HTML stays untouched, and still reads in a sensible order for screen readers.

6. Responsive Grids Without Media Queries

The best-known Grid pattern is a gallery that sets its own column count based on the available width:

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
}

Read it as: "make as many columns as will fit, each at least 14rem and at most an equal share of the space."

  • minmax(14rem, 1fr) is a column that is never narrower than 14rem but can grow.
  • auto-fit creates as many of them as fit in the row.

On a wide screen: four columns. On a tablet: two. On a phone: one. And unlike the Flexbox version in chapter 9, every row has the same column widths, so the last row never stretches. Reach for this one first when you build any list of cards.

Tip: if the grid should never use too little room on tiny screens, write minmax(min(14rem, 100%), 1fr) so a column can shrink below 14rem when the container is narrower.

7. Alignment in Grid

The alignment properties from Flexbox work here too, with the same names:

PropertyWhereDoes
justify-itemsContainerAligns items horizontally inside their cell
align-itemsContainerAligns items vertically inside their cell
place-items: centerContainerShorthand: both at once
justify-content, align-contentContainerPlaces the whole grid inside the container
justify-self, align-selfOne itemOverrides it for one item

Centering something in both directions is display: grid; place-items: center; — even shorter than Flexbox's three lines.

8. Grid or Flexbox?

They aren't competitors; most pages use both. A rule that works:

Use…When…
FlexboxYou're arranging items in one direction and the content should decide the size: navbars, button groups, toolbars, a card's inner layout
GridYou're laying out the page or a region in rows and columns, and the layout should decide the size: page templates, galleries, dashboards

Another way to ask: "do I care about alignment across rows?" If yes — columns that line up from one row to the next — use Grid. If you only care about spreading items along one line, use Flexbox.

You can nest them freely: a Grid page layout holds a <header> that's a Flexbox navbar, and a Grid gallery of cards where each card uses Flexbox to pin its button to the bottom.

Combine the pieces. In index.html, wrap the projects in a gallery:

<section id="projects">
  <h2>Projects</h2>
  <div class="gallery">
    <article class="card">…</article>
    <article class="card">…</article>
    <article class="card">…</article>
  </div>
</section>
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
}

.card {
  display: flex;
  flex-direction: column;
}

.card .button {
  margin-top: auto;     /* pushes the button to the bottom of every card */
}

That's both tools in one component: Grid for the gallery, Flexbox inside each card so the buttons align at the bottom regardless of how much text each card has.

For the About page, use named areas for a two-column layout with the photo beside the text:

.about {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}

@media (min-width: 40rem) {
  .about {
    grid-template-columns: 12rem 1fr;
    align-items: start;
  }
}

The @media rule is the topic of the next chapter. For now, trust that it says "when the screen is at least 40rem wide, use two columns."

10. Try It Yourself

  1. Equal columns. Make a grid with 6 boxes and repeat(3, 1fr). Add a gap. Then change it to 1fr 2fr 1fr.
  2. Inspect it. In DevTools, click the grid badge next to the container to show the overlay with numbered lines. Which line is the end of the second column?
  3. Span. Make the first box grid-column: span 2. What happens to the rest? Then try grid-row: span 2.
  4. Draw a page. Build the header/sidebar/main/footer layout from section 5 with grid-template-areas on a scratch page.
  5. Build the gallery. Add the auto-fit gallery to your projects. Resize the window slowly, and count the columns at each width.
  6. Compare with Flexbox. Make the same cards with the Flexbox version from chapter 9 and with Grid. Add a 5th card. Which handles the last row better?
  7. Center anything. Use display: grid; place-items: center; to center a message in a full-height div.

Commit. You have all the tools; the last chapter is about making the page work well on every screen, and then putting it on the internet.

Buy Me a Coffee

Found this tutorial helpful? Consider buying me a coffee to support more free content!