An introduction to CSS Grid

I recently ran a class at work to introduce some of my colleagues to CSS Grid. Writing the materials and learning more about it got me very excited about this layout module. Seeing the reactions of my colleagues was also exciting. What we can do now is incredible. All that's required to build a fully responsive traditional grid of columns and rows is three lines of CSS. No media queries! Traditional grids are so easy to create, they're almost boring to build. It's time to be more experimental and push the boundaries with layouts.

CSS Grid Layout is an incredibly powerful layout module in CSS. It is two-dimensional, so it can handle columns and rows.

It has some similarities to flexbox, but there is a fundamental difference between the two. Flexbox is also very powerful, but it is largely one-dimensional. Flexbox handles columns or rows. CSS Grid handles rows and columns.

Together, Grid and flexbox can help us create layouts that were previously impossible to build in CSS.

The Basics

1. Define the grid container

It all starts by creating a grid container. We do this by declaring display:grid; on a parent element. As soon as we do this all the direct children of that element become grid items. It works in the same way as flexbox. You'll notice with CSS Grid that all of the grid styles are placed on the parent element.

.parent {
  display: grid;
}

grid-step-1.png#asset:617

All of the direct children are now grid items. This will not change how the items are displayed yet because it has just created a single column grid for the items. We need to define grid tracks to add more columns. A grid track is the space between two adjacent grid lines, so essentially the columns or rows of the grid. So on the grid above, the space between the top and bottom of each column is a track.

2. Add rows and/or columns

Quite simply, we use the grid-template-columns and grid-template-rows properties to add rows and columns.

.parent {
  display: grid;
  grid-template-columns: 100px 100px 200px;
}

grid-step-2.png#asset:618

Now we have three columns. We have specified the width of those columns to 100px 100px and 200px. If we add more children to the grid, it will continue to place all children in the order of 100px 100px 200px like this:

grid-step-3.png#asset:619

Do you want some space around the grid items? We use the grid-gap property for that.

.parent {
  display: grid;
  grid-template-columns: 100px 100px 200px;
  grid-gap: 10px;
}

grid-step-4.png#asset:623

If we want to we can style rows too using grid-template-rows. In the example below, the first row is 50px tall, the second row is 200px tall, the third will be 50px tall when some items are added to it.

.parent {
  display: grid;
  grid-template-columns: 100px 100px 200px;
  grid-template-rows: 50px 200px 50px;
  grid-gap: 10px;
}

What about a flexible grid?

Using pixels results in fixed widths, which is not flexible or responsive.

Meet the fr unit. fr represents a fraction of the available space in the grid container. So we can switch px to fr.

.parent {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-gap: 10px;
}

Three things are happening here:

  • The columns are equal width (they are all 1 fraction)
  • The columns are flexible (1 fraction of the screen width
  • Grid calculates the width of each column based on the available space and the width of the gap (if specified), which in this case is 10px.

grid-step-6.png#asset:628

Note: There's a more DRY way to create multiple columns with equal width. We can write: grid-template-columns: repeat(3, 1fr)

But we don't always want equal width columns.

The we can just change the fractions:

.parent {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  grid-gap: 10px;
}

grid-step-7.png#asset:629

We can also mix units. We might want one column to have a fixed width and the rest of the space distributed equally between the others. That's possible too.

.parent {
  display: grid;
  grid-template-columns: 1fr 300px 1fr;
  grid-gap: 10px;
}

Wrap as many items into the browser as possible.

We need to get familiar with the auto-fill or auto-fit keywords for this. We can change the repeat value in the grid-template-columns to the below.

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fill, 200px);
  grid-gap: 10px;
}

auto-fill represents the number of times we want to repeat tracks. Instead of saying repeat(3, 200px) like before, we are telling Grid to fill the space with as many 200px tracks as possible. Hence, the term auto fill.

grid-step-8.png#asset:630

However, we're back to the problem of no flexibility. Each item is 200px wide as specified, but when there isn't enough space for the next item, they are wrapped onto new lines. Makes sense, but we might want to fill the whole space.

To add flexibility we can use the minmax keyword. Now we can set a minimum width of 200px and maximum width of 1fr.

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-gap: 10px;
}

This means we'll still see as many 200px columns as will fit into the width of the browser, but if there is space left over it will be distributed between them equally (1 fraction each).

Example using minmax

It means that the items are likely to be slightly bigger than 200px most of the time, depending on the width of the browser, but they won't get smaller than 200px and they are flexible and responsive. Winning!

Final hurdle

My last problem is only noticeable when all the items fit into the first row of the grid.

Grid using auto-fill keyword

With the keyword auto-fill, Grid creates as many tracks as will fit, even when there aren't enough items to fill the tracks, so they leave an empty space. I can see this being a really useful tool for more experimental with layouts, but for something like a traditional card grid, we probably don't want empty tracks.

Hello responsive grid with three lines of CSS!

We can solve the previous problem by using auto-fit instead of auto-fill. The auto-fit keyword will fit as many items as possible into the space, instead of tracks. This means there are no empty tracks left over. Remember it like this:
auto-fill = fill the space with tracks
auto-fit = fit the items into the space

Here's the CSS we can write to create a responsive grid. No media queries required! How amazing

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 10px;
}

The working example on codepen: A simple responsive grid using CSS Grid.

Further reading