CSS Grid provides us with a powerful layout system for websites. The CSS-Tricks guide gives you a comprehensive overview of Grid’s properties with layout examples. What we’re going to do here is a reverse approach to show you the smallest possible set of grid properties you need to know to meet most of your layout needs.
These five properties will get you up and running:
display
(for thegrid
value)grid-template-columns
grid-gap
grid-auto-flow
grid-column
/grid-row
Here’s how simple it is. Let’s assume you want to implement the following layout for small, medium and large screens.
This is the markup we’ll be working with:
<!-- Stuff before -->
<nav class="container-nav">
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</nav>
<div class="container-main">
<section class="item item-type-a"></section>
<section class="item item-type-b"></section>
<section class="item item-type-b"></section>
<section class="item container-inner">
<section class="item-inner"></section>
<section class="item-inner"></section>
<section class="item-inner"></section>
<section class="item-inner"></section>
<section class="item-inner"></section>
</section>
</div>
<!-- Stuff after -->
If we apply a few baseline styles, this is what we get, which is already sufficient for small screens:
Now we can get into the grid properties!
Use display: grid to divide the page into independent layout containers
First, we need to determine which parts of the page should be aligned with grid layouts. It is possible to define a single grid layout for the whole page. However, for websites with a very complex structure (e.g. news websites), handling a large grid quickly becomes complicated to wrangle. In this case, I recommend breaking things down into several, independent grid containers.
Like this:
Where do you draw the line between what is and isn’t a grid? Here’s a personal rule of thumb I follow:
If the layout in a particular part of the page does not fit into the grid of an adjacent or surrounding part of the page, make that part its own grid container.
I have drawn the grid lines into the page section with the class .container-main
in the following image You may notice that the section with the .container-inner
class from the markup does not fit exactly into the grid of rows.
Here’s another possible layout where the small sections fit into the surrounding grid if a finer line raster is chosen. A separate grid container is not absolutely necessary here.
To kick this off, let’s .container-main
into a grid container. This is the basic building block for CSS Grid — turning an element into a grid container with the display
property:
.container-main {
display: grid;
}
We’ll want to do the same with our other grid containers:
.container-inner {
display: grid;
}
.container-nav {
display: grid;
}
Use grid-template-columns to define the required columns
Next, we’re going to define the number of columns we need in each grid container and how wide those columns should be. My guideline for the number of columns: use the smallest common multiple of the maximum number of columns required for the different screen sizes.
How does that work? The .container-main
element has a total of two columns on medium-sized screens. If we take that and multiply it by the number of columns on large screens (three), we get a total of six columns.
We can do the same for our navigation, the .container-inner
element. There are three columns on medium-sized screens, which we multiple by one column on large screens to get a total of three columns.
The .container-nav
element provides no number of columns. In this case, the grid system should automatically adjust the number of columns to the number of menu elements. It’s common to add or remove items in a navigation, and it’d be great if it responded accordingly, which is something grid can help us with a little later on.
OK, so we defined the number of columns for each grid container. Let’s use the grid-template-columns
property to set those into place. But, first a couple of minor details:
- The
grid-template-columns
property is only used on the grid container. In other words, you won’t find it being used (at least correctly) on a grid item inside the container. - The property accepts a bunch of different values that both define the number of columns and how wide they should be. The one we’re interested in here is the fractional (
fr
) unit. I’d highly suggest checking out Robin’s overview because it’s unique to grid and does an amazing job doing calculations to decide how grid elements fit inside a grid container.
We need six equal-width columns in .container-main
. We can write that like this:
.container-main {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
}
Or, we can turn to the repeat()
function to simplify it into something more readable:
.container-main {
display: grid;
grid-template-columns: repeat(6, 1fr);
}
Let’s take that knowledge and apply it to our .container-inner
element as well, which we decided needs three columns.
.container-inner {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
Use grid-gap to add spacing between grid items
By default, grid uses all the space it has in a grid container to fit in grid items. Having elements flush next to one another might be a design requirement, but not for the particular layout we’re making. We want some breathing room between things!
We have the grid-gap
property for that. Again, this is a property that’s just for grid containers and what it does is create vertical and horizontal spacing between grid items. It’s actually a shorthand property that combines the vertical spacing powers of grid-row-gap
and horizontal spacing powers of grid-column-gap
. It’s handy that we’re able to break things out like that but, in times like this where we’re working with the same amount of spacing in each direction, the shorthand grid-gap
is much nicer to write.
We want 20px
of space between grid items in .container-main
, 10px
of space in .container-inner
, and 5px
of space in .container-nav
. No problem! All it takes is a one-liner on each grid container.
.container-main{
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-gap: 20px;
}
.container-inner {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
}
.container-nav {
display: grid;
grid-gap: 5px;
}
Use grid-column and grid-row to determine the size of the individual grid items
Now it is time to put the layout into the shape we want it!
First is the grid-column
property, which allows us to extend a grid item across n columns, where n is the number of columns to span. If you’re thinking this sounds an awful lot like the rowspan
attribute that lets us extend cells across multiple rows in HTML tables, you wouldn’t be wrong.
It looks like this when we use it on a grid .item
in our .container-main
element, and on the .inner-item
elements in .container-inner
:
.item {
grid-column: span 6;
}
.item-inner {
grid-column: span 3;
}
What we’re saying here is that each item span six rows in our main container and three rows in our inner container — which is the total number of columns in each container.
An interesting thing about CSS Grid is that we are able to name the lines of the grid. They come with implicit names out of the box but naming them is a powerful way to distinguish between the starting and ending lines for each column on the track.
We can change the number of columns and rows the items should span at different breakpoints:
@media screen and (min-width: 600px) {
.item-type-b {
grid-column: span 3;
}
.item-inner {
grid-column: span 1;
}
}
@media screen and (min-width: 900px) {
.item {
grid-column: span 2;
grid-row: span 2;
}
.item-type-b{
grid-row: span 1;
}
.item-inner{
grid-column: span 3;
}
}
Using grid-auto-flow to control the placing of the elements
CSS Grid places elements one row after the other. This is why the result in our example looks like this at the moment:
A column-by-column placement can be achieved by setting the grid-auto-flow
property to column (row
is the default value). Our layout will profit from column-wise placement in two cases. First, it makes our menu items finally appear in a horizontal orientation. Secondly, it brings the elements of the container class into the desired grouping.
The final result
Conclusion: More or less specification?
The grid system allows us to work under the motto, “make as many specifications as necessary, but as few as possible.” We’ve only covered a few of the specifications necessary to turn elements into a CSS grid container and the items inside it into grid items for the sake of showing just how little you need to know to build even complex layouts with CSS Grid.
CSS Grid supports additional use cases where:
- We want to make even fewer specifications in order to instead rely more on automatic positioning.
- We want to make even more specifications in order to determine more details of the resulting layout.
If the first case applies, then it’s worth considering the following additional grid options:
- When creating the grid with
grid-template-columns
, you can have the grid system automatically determine the width of individual columns with the auto keyword or adapt it to the existing content with the settingsmin-content
,max-content
, orfit-content
. - You can let the grid system automatically determine the number of required columns with the help of
repeat
,auto-fill
,auto-fit
, andminmax
. Even media queries can become redundant and these tools help make things flexible without adding more media queries.
Here are a couple of articles on the topic that I recommend: Becoming a CSS Grid Ninja! and Auto-Sizing Columns in CSS Grid: auto-fill vs. auto-fit.
If the second case applies, CSS Grid offers even more settings options for you:
- You can explicitly specify the width of the columns in the unit of your choice (e.g.
px
or%
) using thegrid-template-columns
property. In addition, the propertygrid-template-rows
is available to define the number and width of rows, should there be a specific number of them. - You can also define specific column or row numbers for positioning as values for
grid-column
andgrid-row
(or use the propertiesgrid-column-start
,grid-column-end
,grid-row-start
, orgrid-row-end
).
And we haven’t even gotten into CSS Grid alignment! Still, the fact that we can accomplish so much without even broaching that topic shows how powerful CSS Grid is.
Excellent write up! Bookmarked for the inevitable future referencing :)
Just wanted to point out that
grid-gap
,grid-column-gap
, andgrid-row-gap
are actually deprecated in favor ofgap
,column-gap
, androw-gap
.https://developer.mozilla.org/en-US/docs/Web/CSS/gap
Wow deprecated? That’s wild. Still, I’d bet a hundred bucks browsers never remove support for it. Too much in the wild already.
Pretty interesting to see
gap
used on a flexbox example there too. I’m assuming that means it can be used with bothdisplay
values.All the gap properties are now defined in the Box Alignment specification. This means that they can apply to any layout method that makes sense.
The deprecated grid- prefixed properties are also covered there: https://www.w3.org/TR/css-align-3/#gap-legacy
There is no reason to use the prefixed properties at this point as all browsers support the unprefixed versions, but if you have sites that use them, they aren’t going to go away and break things.
I still don’t understand why there is not more of a focus on grid-template-areas. If you want to talk about the biggest benefit that grid has brought, it’s literally the ability to visually mock up layouts within your css!
Here’s a solution without the use of media queries: https://codepen.io/dannievinther/pen/GRJeqVv
Works in Firefox 75+ (Nightly) and Chrome
Any reason to prefer non media queries solution?
@Max.
Not necessarily. However, there might be some benefits if you choose to regard the various elements as individual (and flexible) components. The context in which components enter into isn’t always strictly defined, so by inscribing instructions — so to speak — to the component itself, you somewhat ensure that that it fits into any context no matter the context. Setting media query rules isn’t that flexible, and they are solely based on the viewport — components aren’t necessarily.
Using techniques that don’t require media queries adds flexibility when it comes to the way your elements are laid out without needing to explicitly define them.
That being said, it will all depend on your workflow and/or context :)
I have written an article on the subject here: https://blog.logrocket.com/flexible-layouts-without-media-queries/
I love CSS Grid & use it in quite a lof or projects – however one big drawback for me is the fact there is bad IE support.. For example, the autofill doesn’t work on IE11, so as fallback I need to set the location of every child element using grid-template-columns/columns.. Because I still have to support IE11 for all my projects.. I’m so tired of IE!
Great stuff, CSS grid is gamechanger. going to layout some of my old plugins (AND simplify most html spitters) with pleasure :)