Introducing CSS Grids

A quick look at some of the new features from the CSS Grid Specification Level 1 and what they mean for the future of our CSS layouts.

By Luke Whitehouse on

CSS

Article

With it's specification having recently matured to Candidate Recommendation, I thought it'd be a great time to look at what the CSS Grid spec will be able to provide us with in the future. What new layout techniques can we hope to define and more importantly, will we save any time using them? Plus, it's always cool to look at new stuff.

A Candidate Recommendation (CR) is a document that W3C believes has been widely reviewed and satisfies the Working Group's technical requirements. W3C publishes a Candidate Recommendation to gather implementation experience. W3C Process Document: Maturity Levels

Browser compatability

Naturally at this stage, Browser Support is going to be poor. Up until now its features and syntax have been very much up in the air and prone to change. Now that we're at CR, things will start to simmer down and we can get a real feel for what we'll be working in the years to come. That being said, there are still new additions talked about daily so take everything with a pinch of salt.

At the time of this writing, only the latest version of IE and Edge support this (FYI: The Microsoft team created the original proposal for CSS Grids). You can also get this working on Chrome by going to chrome://flags in your browser and enabling 'Experimental Web Platform features'.

Here's a full up to date chart of browser support for CSS Grids.

The basic grid syntax

Now that we've got through the disclaimers, let's talk features! First things first, to create a new Grid layout you'll first need to create a list of items within a single container.

<div class="my-new-grid">
  <div>Grid item</div>
  <div>Grid item</div>
  <div>Grid item</div>
  <div>Grid item</div>
</div>

Our grid system will be defined soley by the containing element itself, rather than any of its children. To 'active' the CSS Grid, we will need to add the display: grid; property.

.my-new-grid {
  display: grid;
}

CSS Grids are defined using a concept of Grid tracks. A track is a generic term for both 'Grid Columns' and 'Grid Rows'. Each track can have its own sizing applied to it, controlling the width/height. This can be achieved by two core properties: grid-template-columns and grid-template-rows respectively. As the grid-template-columns will generally be more commonly used, lets take that as our first example, but bare in mind that the syntax is the same for both properties.

.my-new-grid {
  display: grid;
  grid-template-columns: 100px 100px;
}

Each value of the grid-template-columns defines a new column, with its width being whatever CSS unit used.

NOTE: You can use any CSS unit you'd like when defining grid-template-columns or grid-template-rows.

In this case, we've created two 100px columns, so all items within our grid will conform to this 2 column grid. Using the same HTML as above, this means we now have a 2x2 grid with both columns having widths of 100px.

OK, awesome, now let's talk gutters. Imagine how cool it'd be if you could just write grid-column-gap, grid-row-gap or even just a shorthand grid-gap? Well, you can!

.my-new-grid {
  display: grid;
  grid-template-columns: 100px 100px;
  grid-gap: 10px 10px;
}

grid-gap defines the row gaps first, then column gaps. Alternatively, if both values are the same you can simply omit the second value.

.my-new-grid {
  display: grid;
  grid-template-columns: 100px 100px;
  grid-gap: 10px;
}

What about a responsive grid?

Theres one big problem when it comes to creating responsive grid layouts using existing unit measurements, and that's to do with our good friend box-sizing. You know how in our current grid systems (be it float, or inline-block based) we use the box-sizing property to make sure any padding is subtracted from an element's width, rather than added on? Unfortunately thats not the case when using grid-gap and you'll find that you'll have overflowing columns left, right and center.

Whilst yes, you could use percentages for everything, we need a solution that works with fixed width gutters aswell.

The fraction unit

In comes the fraction (fr) unit to save the day. I'll lead by example this time.

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

We define our two columns as 1fr, which would equate to 1 fraction of the grid's width. As there are only two columns defined, this would be half width each.

Luckily, the fr unit takes into account any other unit values and subtracts that value from the overall width by default, meaning we'll never have to worry about overflowing our grids again.

Lets take a more complex example.

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

Whilst we're only defining 3 explicit columns, we're asking the second column to span across two fractions, and seeing as there are 4 fractions now, that equates to half, rather than a third.

Cool right? But that's not even the best bit! You can even mix tracks with fixed values too.

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

In this example we've got a 6 column grid, with the second column spanning two. However, we've now introduced a fixed width to the third column, meaning that the third column will always stay at 100px, regardless of screen size, and the other columns can scale based on that.

To explain a little further, given the above example if our .my-new-grid had a width of 900px, then that would mean that the overall width the fractions have to distribute between themselves is 760px, due to the 100px from the third columns, and four 10px gutters from grid-gap.

Repetition is king

When defining our tracks (rows/columns), we also have access to the repeat() function. This allows you to repeat a common pattern rather than duplicating what you've already wrote.

Lets say we want to create a 12 column grid. Based on what we know so far, we'd do something like this:

.my-new-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

As you can see, its not the nicest thing to look at and just looks plain wrong. Instead, using the repeat() function we can write

.my-new-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
}

The first argument is how many itterations you'd like to go through, and the second is the pattern you'd like to repeat. Both code examples ultimately create the same thing, with the second being much more maintainable.

We can also chain a repeat function with other columns too. Lets say we wanted a 12 column grid, with the first and last columns being fixed width, and the rest being 1fr.

.my-new-grid {
  display: grid;
  grid-template-columns: 100px repeat(10, 1fr) 100px;
}

We can also use our repeat() function to auto fill the remaining space of our container with grid columns.

.my-new-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 100px);
  width: 600px;
}

This would mean we have now 6 columns, as 100 goes into 600 6 times. The auto-fill keyword allows us to fill in the remaining space with as many columns as possible, and stops once it can't fit in anymore (note: it will not overflow).

min-max yourself

The last feature I want to cover is the minmax() function. Taking two parameters, we can specify that a value can go no smaller than the minimum value (first parameter), but no larger than the maximum one (second parameter). This is great when you want to have a little more control of your fixed width columns.

.my-new-grid {
  display: grid;
  grid-template-columns: minmax(100px, 1fr) repeat(3, 1fr) min-max(100px, 1fr);
}

This will give us a 5 column grid, with the first and last columns never going smaller than 100px, but scaling proportionate to the other columns and the container itself.

Bringing everything together

OK, lets recap. We now know how to 'active' a CSS grid, and even create tracks without repeating ourselves.

.my-new-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr 2fr);
}

We know how to give those tracks a little spacing.

.my-new-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr 2fr);
  grid-gap: 10px;
}

And we can even dynamically create tracks based on the available space.

.my-new-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr 2fr);
  grid-template-rows: repeat(auto-fill, 1fr);
  grid-gap: 10px;
}

Finally, we know how to give our tracks minimum and maximum constraints.

.my-new-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr 2fr);
  grid-template-rows: repeat( auto-fill, minmax(150px, 1fr) );
  grid-gap: 10px;
}

Further reading

Theres a lot more than we can cover in this one article. Here's a few resources that may peak your interest if you're looking to get ahead of the curve and refine your understanding on this specification:

I hope you found this article useful and its spurred you onto looking further into the CSS Grid specification, I know I will be.

Until next time, thanks for reading!

Follow us on Twitter, Facebook or Github.
© Copyright 2021 Assortment.
Created and maintained by Luke Whitehouse.