Things I’ve Learned About CSS Grid Layout

The following is a guest post by Oliver Williams. Oliver has been working with CSS grid layout and has learned quite a bit along the way. In this article he's going to hop around to different concepts that he's learned on that journey. I like this idea of learning little bite-sized chunks about grid layout, through isolated examples where possible. That makes learning the whole thing a bit less intimidating.

CSS grid looks set to land in browsers in early 2017. Until then you will need to enable grid for it to work (except in Firefox Nightly where it is enabled by default). Chrome Canary currently has the best implementation. Meanwhile, Firefox has an invaluable add-on called CSS Grid Inspector, which can display the lines of your grid. It is currently the only browser to have such a tool.

In Chrome enter `chrome://flags` into your address bar, find ‘Enable experimental Web Platform features’ and click enable. IE and Edge have old implementations different from the current spec, and are therefore not recommended for experimenting with grid at this point in time.

You can't have tetris-shaped grid areas

You'll work this out pretty quickly for yourself.

Your grid areas can only be rectangles (like the left), not arbitrary polygons (like the right).

Grid is designed to be used with flexbox, not instead of it

Grid and flexbox can act in similar ways. You may have seen people using flexbox to construct grid systems but that's not what flexbox was designed for. It's worth reading Jake Archibald's blog post Don't use flexbox for overall page layout.

A quick way to think about it is:

  • Flexbox is for one dimensional layout (row or column).
  • CSS grid is for two dimensional layout.

Or as Rachel Andrews put it:

Flexbox is essentially for laying out items in a single dimension – in a row OR a column. Grid is for layout of items in two dimensions – rows AND columns.

They can be combined as well. You can turn a grid item into a flex container. You can turn a flex item into a grid.

Let's take one useful example. We want to vertically center the text inside a grid item but we want the background (whether a color, a gradient or an image) to cover the items entire grid area. We can use the align-items property with a value of center but now the background doesn't fill the whole area of the item. The default for align-items is stretch — as soon as you set it to any other value, it no longer fills the space. Let's instead leave it set to stretch and turn our grid item into a flex container.

.grid {
  align-items: stretch;
}

.griditem {
  display: flex;
  align-items: center;
}

Using negative line numbers can be really helpful

Imagine a current CSS grid framework with a 12 column grid. At small screens, rather than reducing the number of columns, the content is told to span all twelve columns, giving the impression of being a single full-width column.

You could do this same kind of thing with grid:

/* For small screens */
.span4, .span6, .spanAll {
  grid-column-end: span 12;
}

/* For large screens */
@media (min-width: 650px) {
  .span4 {
    grid-column-end: span 4;
  }
  .span6 {
    grid-column-end: span 6;
  }
}

There's nothing wrong with this approach. However, with CSS grid, it's just as easy to redefine the number of columns. By using -1, you can be sure your content will always reach the end.

/* For small screens */
.span4, .span6, .spanAll {
  grid-column-end: -1;
}

For a large screen you may want as many as twelve columns. For a mobile, anywhere between one and four. It's easy to change the grid-template-columns value with a media query.

.grid {
  grid-template-columns: 1fr 1fr;
}

@media (min-width: 700px) {
  .grid {
    grid-template-columns: repeat(12, 1fr);
  }
}

There are likely some elements we want to span across the whole viewport for all screen sizes, like perhaps the header, footer, or some hero images.

For small screens we could write:

.wide {
  grid-column: 1 / 3; /* start at 1, end at 3 */
}

However, once we make our media query these elements will cover only the first two columns of twelve columns. We could include the new desired grid-column-end value of 13 in the same media query but there's a far easier method. Just set the end value to -1 and it will span all columns, however many there happen to be. Like:

.wide, .hero, .header, .footer {
  grid-column: 1 / -1;
}

See the Pen Easier media queries with -1 by CSS GRID (@cssgrid) on CodePen.

Grid areas create implicit line names

Named grid template areas and grid line numbers are two ways of placing content on the grid but you can use both placement methods in the same grid. You can also make use of the implicit line names that are created whenever you designate a grid-area.

.grid {
  grid-template-areas: "main main sidebar sidebar";
}

With this code we just created four implicit line names: main-start, main-end, sidebar-start, and sidebar-end.

This could be useful if you want to overlap content either over several grid areas or in a particular subsection of one grid area.

See the Pen implicit line names with grid areas by CSS GRID (@cssgrid) on CodePen.

There is a second way of defining grid areas

Just like grid areas create line names, specific line names can create grid areas. The syntax for defining grid areas looks like:

.grid {
  grid-template-areas: 
    "header header header"
    "main main sidebar"
    "footer footer footer";
} 

This syntax can become somewhat unwieldy if you have a lot of empty space in your design (Too many periods! Periods, in lieu of a name, signify an empty cell.) You may not know that there is another way to define grid areas. We can name grid lines whatever we like. However, if we follow the naming convention [name-start] and [name-end] we can create grid areas. Here's an example:

.grid {
  display: grid;
  grid-template-columns: 20px 100px [main-start] 1fr [main-end] 100px 20px;
  grid-template-rows: 100px [main-start] 100px [main-end] 100px;
}

.griditem1 {
  background-color: red;
  grid-area: main;
}

See the Pen Another way of defining grid-areas by CSS GRID (@cssgrid) on CodePen.

You probably wouldn't lay out a whole page of content with this method, but if you want to combine grid-area placement with line number based placement, it's nice to know about.

Use vmin for an equal sized box layout

CSS grid allows you to use any size unit for your rows and columns. Want equally-sized boxes but also want them to be responsive? If you want content that resizes with the container, you can use viewport units.

.grid {
  grid-template-columns: repeat(5, 20vw);
  grid-template-rows: repeat(5, 20vh);
}

This would work perfectly well on desktop and laptop but on mobile phones where the height of the screen exceeds the width, our content will spill over creating a horizontal scroll bar. Dudley Storey recently wrote a blog post about the usefulness of a lesser-known css unit: vmin. This unit will be a percentage of the viewport width on a portrait-oriented screen and a percentage of the viewport height on a landscape-oriented screen.

.gridcontainer {
  display: grid;
  width: 100vw;
  height: 100vh;
  justify-content: center;
  align-content: center;
  grid-template-columns: repeat(5, 20vmin);
  grid-template-rows: repeat(5, 20vmin);
}

We now have equally-sized boxes that adapt to any screensize.

See the Pen Boxy Layout with CSS Grid and vmin by CSS GRID (@cssgrid) on CodePen.

Absolute positioning

When you absolutely position a grid item, rather than its positioning context being its container (i.e. the entire grid) we can position it in relation to its specified grid-column and grid-row start and end lines. As usual, position: absolute removes an element from the flow of the document (i.e. it is ignored by other elements). This makes absolute positioning useful if you want to overlap grid items without disrupting the grids auto-placement algorithm. Auto-placement goes out of its way not to overlap items unless you explicitly declare both a grid-column-start and grid-row-start value for every item.

Try deleting position: absolute; from the div in this example and think about how many items you'd have to define grid-column and grid-row properties for!

See the Pen preserving auto-placement with position: absolute by CSS GRID (@cssgrid) on CodePen.

Order works differently from how you might think

If you've used the flexbox order property, then you already have this covered. All grid items have a default order value of 0. So order: 1; applied to a grid item would make after everything else, not before.

You can use negative numbers to push items to the front.

See the Pen Order value by CSS GRID (@cssgrid) on CodePen.

The limits of minmax

Do you want columns that equally divide space between themselves until they reach a maximum width? You might think you could use minmax() like so:

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

Unfortunately that isn't going to work. If max is less in value than min, it is ignored. The fr is invalid as a minimum value in minmax(). However, it's a very easy behaviour to achieve. The word auto, when used as a value in grid-template-columns or grid-template-rows, will make the column or row as large as its content.

See the Pen The value of auto vs fr by CSS GRID (@cssgrid) on CodePen.

Then we can set a max-width on the content:

.grid {
  display: grid;
  grid-template-columns: repeat(3, auto);
}

.item {
  max-width: 300px;
}

See the Pen The limits of minmax by CSS GRID (@cssgrid) on CodePen.

There might be good reasons for the way minmax() works and use cases I haven't thought of. Even so, I wrote an entire post on Medium entitled The One Thing I Hate About Grid.

Things are way easier if you name your grid lines

There are multiple approaches you can take. If you're really into typing, you can give lines multiple names.

.grid {
  grid-template-columns: [col1-start] 1fr [col1-end col2-start] 1fr [col2-end];
}

The easiest naming convention makes use of the grids auto-numbering. We don't have to type [col2], we can just type the name col followed by a number.

.griditem1 {
  grid-column-start: col 2;
}

Combined with the span keyword we can stop thinking about line numbers and start thinking in terms of the first column we want our content to fill and how many columns we want to take up, which is far more intuitive.

.grid {
  grid-template-columns: repeat(4, [col] 100px);
}

.griditem1 {
  grid-column: col 2 / span 2;
} 

The fr unit removes the need for math

Let's say we want to split our grid into four equal columns. Its easy to work out the percentages. We just write grid-template-columns: 25% 25% 25% 25%.

What if we then want to make use of the grid-gap property? grid-gap: 10px. We have three gaps between our columns, so our grid width will now be 100% + 30px and unwanted horizontal scrolling will appear with some content overspilling the screen on the right side. We could make use of calc to solve our problem, but using the fr unit is far easier: grid-template-columns: 1fr 1fr 1fr 1fr.

See the Pen fr unit vs percentage by CSS GRID (@cssgrid) on CodePen.

The second thing I hate about grid

There is no way to force the auto-placement algorithm to leave some columns or rows empty

The grid-gap values provides an easy way for spacing our content. While we can specify a separate value to divide our columns and our rows with grid-row-gap and grid-column-gap, these values must be uniform. If we want to separate our first and second row by ten pixels and our second and third row by 50 pixels, grid offers us no way of doing this, other than by creating rows and keeping them empty.
You may have seen the periods/dots in the grid-template-area syntax:

grid-template-rows: 
  "header header header"
  "main    main   main"
  "  .       .       ."
  "secondary secondary secondary"
  "footer footer footer";

This could have provided a simple way to control the auto-placement algorithm to stop it from placing items in these areas. Unfortunately it doesn't work like that: this syntax simply denotes that we don't want to turn the third row into a named grid-area. Auto-placed items will still end up there.

Some design advice: You don't necessarily need 12 columns (and columns need not be uniform in size)

Twelve columns is the default of web design. The Bootstrap grid uses 12 columns and just about every other grid framework currently out there. There is a good reason for this: twelve is divisible by both three and four, giving more flexibility in how we can lay out content across the page. We can divide our content evenly into 12 parts, 6 parts, 4 parts, 3 parts, or in half.

While some people like the familiarity that comes with consistently using the same grid for every project, there is no need to have more columns than you actually need and you should build the grid that's right for your content and desired layout rather than a one-size-fits all set-up.

Take a look at the examples on Gridset. Gridset is a useful tool for making grids, but once the native CSS grid module lands you won't need tools like this. It does showcase some excellently-designed grids.

I took the liberty of remaking one using the CSS grids:

See the Pen text layout with CSS Grid module by CSS GRID (@cssgrid) on CodePen.