CSS Grid Ships in Firefox 52 and Chrome 57: The Basics and a Safe Fallback
Page layout on the web has always been borrowed from something else. We used tables, then floats, then inline-block, and lately flexbox, which is great for a single row or column but was never meant to handle both directions at once. In the past week two of the biggest browsers shipped something that was designed for exactly that. Firefox 52 and Chrome 57 both have CSS Grid Layout turned on by default, and it’s worth learning now instead of waiting for everyone else to catch up.
Who shipped it and when
Mozilla went first. The Firefox 52 release notes say version 52 was first offered to release channel users on March 7, and list CSS Grid Layout as enabled. The same day, Mozilla’s Firefox 52 post on Hacks explained that the release supports CSS Grid Layout Module Level 1, a spec that defines 18 new CSS properties, and introduced a new Grid Inspector tool that shows the grid lines right on the page.
Chrome followed two days later. The Chrome team’s stable channel update from March 9 promoted Chrome 57 to stable for Windows, Mac and Linux, rolling out over the following days and weeks. The Chrome Platform Status entry for CSS Grid Layout lists 57 as the shipping milestone on both desktop and Android, and the Chromium blog made Grid the headline of its Chrome 57 beta post back in February.
Safari isn’t there yet. On March 9 the WebKit blog published an introduction to Grid Layout that says you can experiment with it in Safari Technology Preview, and that the team is looking forward to seeing it in shipping versions of Safari soon. Microsoft is in a different spot. Internet Explorer 10 introduced a prefixed grid implementation, and the Edge platform status data as of March 13 lists an update to that implementation, to support things like the implicit grid, the grid-template syntax and grid-gap, as under consideration.
The core ideas in the spec
Everything below follows the W3C CSS Grid Layout Module Level 1 Candidate Recommendation of 9 February 2017, the latest published snapshot.
It starts with a grid container. Setting display: grid makes an element generate a block level grid container (inline-grid gives you an inline one), and its children become grid items. One detail matters a lot later: the spec says float and clear have no effect on a grid item.
The container’s tracks come from grid-template-columns and grid-template-rows, which take a space separated list of sizes. You can use lengths, percentages and auto, but the interesting one is the new fr unit. The spec defines it as a fraction of the free space in the grid container, and each track’s share works out to its flex factor times the free space, divided by the sum of all the flex factors. So 1fr 3fr hands one quarter of what’s left to the first column and three quarters to the second. An fr isn’t a length, so you can’t mix it into calc().
Two functions keep track lists short. repeat(4, 1fr) writes four equal columns without typing them out, and the count can also be auto-fill or auto-fit so the number of tracks depends on the space available. minmax(min, max) sets a size range for a track. An fr value is allowed as the maximum, where it sets the flex factor, but it’s invalid as the minimum.
Gutters between tracks use grid-row-gap and grid-column-gap, and the grid-gap shorthand takes the row gap first and then an optional column gap. Those are the names in the current spec and the names Firefox 52 and Chrome 57 understand, so that’s what I’m using here.
Then there’s placement. grid-template-areas lets you draw the layout as strings. Each string is a row, each name in it is a cell, repeating a name across neighboring cells makes a bigger area, and a . leaves a cell empty. An area has to be a filled in rectangle or the declaration is invalid. Items go into an area with grid-area: name. If you’d rather work with lines, which are numbered starting at 1, grid-column: 1 / 3 stretches an item from the first column line to the third, grid-row: 2 puts it in the second row, and grid-column: 2 / span 2 starts at line 2 and covers two tracks.
A word of caution comes straight from the spec. Grid placement doesn’t affect ordering in non-visual media, and authors are told to use it only for visual reordering, not logical reordering. Keep your source order sensible and let the grid handle the visual arrangement.
A small page layout
Here’s a layout with a header, a sidebar, main content and a footer. The markup is just four elements in reading order.
<div class="page">
<header class="site-header">Header</header>
<nav class="sidebar">Sidebar</nav>
<main class="content">Main content</main>
<footer class="site-footer">Footer</footer>
</div>
And this is the whole stylesheet, fallback included. I’ll explain the float parts in the next section.
/* Fallback first: floats that every browser understands */
.page { max-width: 1100px; margin: 0 auto; }
.sidebar { float: left; width: 25%; }
.content { float: right; width: 72%; }
.site-footer { clear: both; }
/* Grid for browsers that pass the feature query */
@supports (display: grid) {
.page {
display: grid;
grid-template-columns: minmax(180px, 1fr) 3fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-gap: 16px 24px;
}
.site-header { grid-area: header; }
.sidebar { grid-area: sidebar; width: auto; }
.content { grid-area: main; width: auto; }
.site-footer { grid-area: footer; }
}
/* One column on narrow screens */
@media (max-width: 640px) {
.sidebar, .content { float: none; width: auto; }
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"sidebar"
"main"
"footer";
}
}
On a wide screen the grid has two columns. The sidebar gets one share of the free space but never less than 180px, the main area gets three shares, and the header and footer span both columns because their names fill both cells in their rows. Rows aren’t listed in grid-template-rows at all here, since the three strings in grid-template-areas already define three rows, and the spec gives rows like that their size from grid-auto-rows, which starts out as auto.
At 640px and narrower the media query swaps in a single column and a four row template. Nothing about the items changes, because they’re placed by area name, not by position. That’s my favorite thing about named areas: the responsive change is a new drawing of the layout, not a pile of overrides on every item.
Browsers without Grid
Two browsers with Grid is great, but plenty of people are still on Safari, Edge and Internet Explorer. The tool for that is the feature query, @supports, and the good news is that it’s already well established. Mozilla’s Hacks article Using Feature Queries in CSS from last August says @supports has worked in Firefox, Chrome and Opera since mid 2013, works in every version of Edge, and shipped in Safari 9 in fall 2015, while no version of Internet Explorer supports it. So every browser that has shipped Grid also understands feature queries.
My advice is to write the fallback first, outside any query, and put the grid inside @supports (display: grid). Internet Explorer doesn’t know what a feature query is, so it skips the whole block and uses the floats. Edge and the shipping version of Safari understand the query, but the test fails because neither has the current unprefixed Grid yet, so they use the floats too. Firefox 52 and Chrome 57 pass the test and get the grid.
This is where the spec rule about floats pays off. Once an element is a grid item its float is ignored, so you don’t have to undo the floats inside the query. Widths are different. The 25% and 72% would still apply to the grid items and squeeze them inside their areas, so the example resets them to auto inside the @supports block. The narrow media query clears both for the float layout as well, which gives old browsers a simple stacked page on small screens.
I’d keep the fallback modest. It doesn’t have to look identical, it just has to be readable and usable, and floats are fine for a header, two columns and a footer. Before shipping, check the layout at a few widths in a browser that has Grid and one that doesn’t, since that’s the only real proof both paths work.
The takeaway
Grid is live for anyone on the current Firefox or Chrome, WebKit says Safari is on the way, and feature queries let you start using it without leaving other browsers behind. Learn the container, fr, repeat(), minmax(), the gap properties and named areas, write a plain fallback, and wrap the grid in @supports. When more browsers ship it, you won’t need to rewrite anything.