CSSIntermediate

CSS Grid Basics: Rows, Columns, fr Units and gap

Build two-dimensional layouts with CSS Grid: define columns and rows, use fr units and repeat(), set gaps, span items and make a responsive gallery.

All CSS lessons

What you will learn

Flexbox lays things out along one line. CSS Grid lays them out in two dimensions at once, rows and columns, so everything lines up in a true grid. It is the best tool for page layouts, galleries, dashboards and any design based on a table-like structure. In this lesson you will learn how to create a grid, how to size columns and rows (including the very useful fr unit and the repeat() function), how to add spacing with gap, how to place items across several cells, and how to build a gallery that adapts to the screen width with no media query.

The HTML we will style

<h1 class="page-title">Grid Lab</h1>

<!-- Part 1: three equal columns and a gap -->
<div class="grid grid-3">
  <div class="cell">1</div>
  <div class="cell">2</div>
  <div class="cell">3</div>
  <div class="cell">4</div>
  <div class="cell">5</div>
  <div class="cell">6</div>
</div>

<!-- Part 2: fixed and flexible columns -->
<div class="grid grid-mixed">
  <div class="cell">sidebar (200px)</div>
  <div class="cell">content (1fr)</div>
  <div class="cell">extra (1fr)</div>
</div>

<!-- Part 3: items spanning columns and rows -->
<div class="grid grid-span">
  <div class="cell cell-wide">A (2 columns)</div>
  <div class="cell">B</div>
  <div class="cell">C</div>
  <div class="cell cell-tall">D (2 rows)</div>
  <div class="cell">E</div>
  <div class="cell">F</div>
  <div class="cell">G</div>
</div>

<!-- Part 4: a responsive gallery -->
<div class="gallery">
  <div class="photo">1</div>
  <!-- ... photos 2 to 6 ... -->
</div>
Selector Element Role
.grid the first three container <div>s marks them as grids (a shared hook)
.grid-3 Part 1 container grid container: three equal columns
.grid-mixed Part 2 container grid container: a fixed column plus flexible ones
.grid-span Part 3 container grid container: three columns, with rows made automatically
.cell all the blue <div>s in Parts 1 to 3 grid items
.cell-wide item A spans two columns
.cell-tall item D spans two rows
.gallery Part 4 container grid container with responsive columns
.photo the six boxes in the gallery grid items

As with Flexbox, the container is the parent, and its direct children are the items.

Grid vocabulary

A few words are worth knowing before the code:

         column 1     column 2     column 3
       1           2           3           4      <- column LINES
    1  +-----------+-----------+-----------+
       |  cell     |  cell     |  cell     |   row 1
    2  +-----------+-----------+-----------+
       |  cell     |  cell     |  cell     |   row 2
    3  +-----------+-----------+-----------+
          ^
          row LINES are numbered too
  • Grid container is the parent with display: grid. Grid items are its direct children.
  • Tracks are the rows and columns themselves.
  • Cells are the individual boxes where a row and column meet.
  • Lines are the numbered dividers between tracks. Three columns have four lines, numbered from 1. You use these numbers to place items.
  • Gap (or gutter) is the space between tracks.

Creating a grid

/* the <div class="grid grid-3"> */
.grid-3 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

Two lines are enough:

  • display: grid makes the parent a grid container. By itself it creates a single column.
  • grid-template-columns defines the columns: how many there are and how wide each one is. Here: three.

The six items fill the grid automatically, from left to right and then onto a new row, so you get two rows of three. You did not write a single row definition. Grid creates rows as it needs them (the implicit grid).

The fr unit

fr stands for fraction of the free space. It is a unit invented for Grid:

.grid-3 {
  grid-template-columns: 1fr 1fr 1fr;   /* three equal parts */
}

The browser adds the fr numbers (1 + 1 + 1 = 3) and gives each column that share of the available width. Different numbers give different ratios:

grid-template-columns: 1fr 2fr;        /* the second column is twice as wide */
grid-template-columns: 1fr 2fr 1fr;    /* a wide column in the middle */

You can mix fr with fixed sizes. Fixed columns are subtracted first, and fr columns share what is left:

/* the <div class="grid grid-mixed"> */
.grid-mixed {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;   /* a 200px sidebar, then two equal columns */
}

The first column is always 200px. The other two split the remaining width equally, whatever the container’s size. A very common layout is grid-template-columns: 250px 1fr;, a fixed sidebar and a main area that takes all the rest.

Other values you can use for column and row sizes: px, %, rem, and auto (as wide as the content needs).

repeat(): don’t repeat yourself

Writing 1fr 1fr 1fr 1fr 1fr 1fr gets tedious. The repeat() function does it for you:

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

.example {
  grid-template-columns: repeat(4, 100px);     /* four 100px columns */
}

.example-2 {
  grid-template-columns: 200px repeat(2, 1fr); /* 200px, then two 1fr columns */
}

gap: space between cells

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

gap puts space between the rows and columns, but not around the outside, so you never need margins on the items. Two values set the row gap and column gap separately: gap: 20px 10px (row first, then column), or use row-gap and column-gap on their own. gap works in Flexbox as well, as you saw before.

Rows

If you do not define rows, they are created automatically, and each is only as tall as its content. To control their size:

/* fixed height rows */
.grid-span {
  grid-auto-rows: 80px;
}

/* at least 80px tall, but taller if the content needs it */
.grid-span {
  grid-auto-rows: minmax(80px, auto);
}
  • grid-auto-rows sets the height of every automatically created row. This is the property you will use most often.
  • grid-template-rows defines specific rows when you know exactly how many you want (grid-template-rows: 100px 1fr 60px; for a header, a body and a footer).
  • minmax(min, max) is a function that says “no smaller than this, no bigger than that”. minmax(80px, auto) is a very safe row height.

Placing items on the lines

By default, items are placed one after another. You can make an item span several tracks, or put it in a specific spot, using the line numbers:

/* item A in Part 3: starts at column line 1, ends at line 3 (covers two columns) */
.cell-wide {
  grid-column: 1 / 3;
}

grid-column: 1 / 3 means “start at column line 1 and end at column line 3”, so the item covers columns 1 and 2. There is an easier way to say “cover two tracks from wherever you are”:

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

.cell-tall {
  grid-row: span 2;        /* take up two rows */
}

Together with a container for Part 3:

/* the <div class="grid grid-span"> */
.grid-span {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 80px;
  gap: 12px;
}

/* item A */
.cell-wide { grid-column: span 2; background-color: tomato; }

/* item D */
.cell-tall { grid-row: span 2; background-color: seagreen; }

Here is what happens to the seven items, in order, on a 3-column grid:

+-----------------------+-----------+
|  A  (spans 2 columns) |     B     |   row 1
+-----------+-----------+-----------+
|     C     |           |     E     |   row 2
+-----------+     D     +-----------+
|     F     |  (2 rows) |     G     |   row 3
+-----------+-----------+-----------+

The grid places each item in the next free cell, skipping cells that are already taken. If there are gaps in the result, grid-auto-flow: dense lets later items move back to fill them.

Aligning inside the cells

Items stretch to fill their cell by default, like Flexbox’s align-items: stretch. You can align them:

.grid-3 {
  justify-items: center;    /* horizontally inside each cell: start, end, center, stretch */
  align-items: center;      /* vertically inside each cell */
}

The shorthand place-items: center sets both. On an item, justify-self and align-self override it for just one cell. And to centre one element in the middle of a container, Grid can do it in two lines:

.center {
  display: grid;
  place-items: center;
  min-height: 100vh;
}

This is the pattern that makes people fall in love with Grid. We want as many columns as will fit, each at least 160px wide, all sharing the leftover space:

/* the <div class="gallery"> */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
}

/* each <div class="photo"> */
.photo {
  aspect-ratio: 1;               /* always a square */
  background-color: seagreen;
  color: white;
  display: grid;
  place-items: center;
  font-weight: bold;
}

Read repeat(auto-fit, minmax(160px, 1fr)) like a sentence:

  • minmax(160px, 1fr): each column is at least 160px wide, and can grow to share the free space.
  • auto-fit: create as many columns as fit in the container.
  • When the container is wide, you get 4 or 5 columns. As it narrows, columns drop out and the items re-flow into fewer, wider columns. On a phone you get one column. There is no media query in sight.

A closely related keyword is auto-fill. The difference appears only when there are fewer items than the space allows: auto-fill keeps empty columns, auto-fit collapses them so that the items stretch. For galleries, auto-fit is usually what you want.

Grid or Flexbox?

Flexbox Grid
Dimension one (a row or a column) two (rows and columns)
Starts from the content: items decide their size the layout: the container defines the tracks
Best for navbars, toolbars, small components, centring page layouts, galleries, dashboards, forms
Items on different rows line up in columns no yes

They are not rivals: use Grid for the big structure, and Flexbox inside the pieces. A card in a grid can be a flex column, as you saw in the last lesson. The holy-grail page from the sticky lesson can be written as display: grid; grid-template-columns: 200px 1fr 180px;.

Putting it together

*, *::before, *::after { box-sizing: border-box; }
body { font-family: Arial, Helvetica, sans-serif; }

.cell {
  padding: 20px;
  background-color: royalblue;
  color: white;
  font-weight: bold;
}

/* Part 1 */
.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 24px;
}

/* Part 2 */
.grid-mixed {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
  gap: 12px;
  margin-bottom: 24px;
}

/* Part 3 */
.grid-span {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 80px;
  gap: 12px;
  margin-bottom: 24px;
}

.cell-wide { grid-column: span 2; background-color: tomato; }
.cell-tall { grid-row: span 2;    background-color: seagreen; }

/* Part 4 */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
}

.photo {
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  background-color: seagreen;
  color: white;
  font-weight: bold;
}

Common mistakes

  • Putting display: grid on the items instead of the container.
  • Forgetting display: grid and wondering why grid-template-columns does nothing.
  • Expecting fr to share the whole width when other columns are fixed. fr shares only what is left over.
  • Writing 1fr columns with wide content, which can stretch them. Use minmax(0, 1fr) to let a column shrink below its content.
  • Mixing up lines and tracks. Three columns have four lines. grid-column: 1 / 3 covers two columns.
  • Fixed row heights that cut text off. Use minmax(80px, auto) or leave rows automatic.
  • Using Grid for a simple single row where Flexbox is simpler, or Flexbox for a two-dimensional layout where Grid is cleaner.
  • Using fixed px columns that overflow on small screens. Prefer fr, minmax() and auto-fit.
  • Expecting gap to add space around the outside. It is only between tracks. Add padding to the container for outside space.

Practice

  1. Click Practice in Editor. Change repeat(3, 1fr) to repeat(4, 1fr), then to 1fr 2fr 1fr. How do the six cells re-flow?
  2. Build .grid-mixed with 200px 1fr 1fr and resize the preview. Which column ignores the width change?
  3. Make item A span two columns and item D span two rows. Is every cell filled? What would happen if you removed item G?
  4. Place an item using line numbers, for example grid-column: 2 / 4. Which columns does it cover?
  5. Build the gallery with repeat(auto-fit, minmax(160px, 1fr)) and resize the preview. When does it drop to two columns, and then to one?
  6. Change auto-fit to auto-fill and remove most of the photos. What is the difference?
  7. Rebuild the three-column holy grail layout from the previous lesson using grid-template-columns: 200px 1fr 180px;.

Recap

  • display: grid on a parent makes it a grid container. Its direct children become grid items placed in cells.
  • grid-template-columns defines the columns, grid-auto-rows (or grid-template-rows) the rows. Rows are created automatically when you do not define them.
  • The fr unit shares the free space. repeat(3, 1fr) gives three equal columns, and 200px 1fr gives a fixed sidebar and a fluid main area.
  • gap adds space between cells.
  • Items can span tracks with grid-column: span 2, or sit between numbered lines with grid-column: 1 / 3.
  • repeat(auto-fit, minmax(160px, 1fr)) makes a responsive gallery without a media query.
  • Use Grid for the page structure (two dimensions) and Flexbox for the pieces (one dimension).