HTML and CSS Basics, part 23: CSS grid
Let's learn all about the newest standard in building layouts in HTML and CSS
By: Ajdin Imsirovic 26 September 2019
In this article, we’ll cover CSS grid layout. However, before we can even start discussing CSS grid, we need to understand a few features of CSS flexbox that we glossed over in previous articles in this series.
Photo by mike nguyen on Unsplash
Flexbox is one-dimensional
Flexbox is called a one-dimensional layout system.
A nice example of this is a row of links:
In an earlier article in this series, we saw how to use flexbox to create a simple layout.
As we can see above, by default, flexbox lines up the items it wraps on one dimension, horizontally.
This is controlled by its default property: value
of flex-direction: row
.
Initially, we added the outer-most wrapper to our layout, wrapping three elements: <header>
, <section>
, and <footer>
, and we added this wrapping element the CSS declaration of display: flex
.
Here’s the result:
We can see above the the direction of our wrapped flex children is left-to-right, as we expect from the default flex-direction
property of row
. You see, when you set an element to display: flex
, you also implicitly set it to flex-direction: row
because that’s the default browser style (and this default browser style, in turn, follows the CSS flexbox specification).
The next step in our layout’s creation was to change the default flex-direction: row
into flex-direction: column
. Since we’re overriding the default, we now had to explicitly add this CSS declaration to our layout.
To flip the axis of alignment, we can simply used flex-direction: column
.
What this will do is still the same as before - in the sense that flexbox is still one-dimensional.
The result was as follows:
See what I mean?
Flexbox is one-dimensional, but you can nest it to make it become two-dimensional
The solution we employed in our flex-based layout includes nested flexbox elements.
Here’s an explanation of what we did:
In the diagram above, we can see that we’re nesting two children elements, aside and main, inside the section element (so we’ve set it to display: flex
).
And here’s the same diagram again, only this time we’re highlighting flex directions:
As we can see in the diagram above, the parent wrapping flex element (the dark green one), holds elements 1, 2, and 3, aligned vertically (on top of one another).
Then one of this element’s children, the element labeled 2
, is itself a wrapping flex element, and is itself a parent element to elements 2.1
and 2.2
. This flexbox-enabled element’s children are lined up horizontally (next to one another).
What we did here was actually a bit hacky: we used nested one-dimensional flexbox wrapping elements to get to a two-dimensional layout.
Now that we’ve added another piece of the puzzle to the flex layout, let’s talk about CSS grid.
Introduction to CSS grid
CSS grid is called a two-dimensional layout system.
The reason? We don’t have to nest grids to get the elements moving in two dimensions, both horizontal and vertical.
Sometimes an example is the best explanation, so let’s explain this in code.
We are immitating the elements we used when we employed flexbox to create our layout.
<div class="outerWrap">
<div>1</div>
<div>2</div>
<div>2.1</div>
<div>2.2</div>
<div>3</div>
</div>
Let’s also give it some styles:
.outerWrap {
display: grid;
max-width: 1000px;
margin: 0 auto;
}
div {
margin: 20px;
border: 1px solid black;
background: tomato;
color: white;
font-size: 40px;
height: 100px;
}
The focus here is on the outerWrap
class and its display: grid
. The rest of the styles are just some coloring to make the divs stand out and easier to distinguish.
Here’s the live preview in this codelab.
Initially, we can see that the above CSS grid starts off just like flexbox: one-dimensional.
Let’s convert it to 2d, by setting its grid-template-column
and grid-template-row
properties.
Here’s the updated CSS:
.outerWrap {
display: grid;
grid-template-columns: 100px 700px 100px;
grid-template-rows: 500px 50px;
max-width: 1000px;
margin: 0 auto;
}
div {
margin: 20px;
border: 1px solid black;
background: tomato;
color: white;
font-size: 40px;
/* height: 100px; */
}
Now we can see the updated code in this codelab.
In the above updated CSS, we see the commented-out height
property inside the CSS div
selector. Why did we still leave it there, and not just deleted it?
To emphasize the fact that it is our grid-template-rows
that set the height.
In a CSS grid layout, the height is set by values given to the grid-template-rows
property.
Since we don’t have to set the heights and widths, could we also get rid of margins?
Let’s try it out.
Let’s see the updated CSS:
div {
/* margin: 20px; */
gap: 20px;
border: 1px solid black;
background: tomato;
color: white;
font-size: 40px;
/* height: 100px; */
}
Obviously, in this update to our layout, we’re left without the outer margin we had before. But the spacing between items in our grid looks exactly the same as the one in the previous example.
Finally, to add the outer spacing to our grid, we could simply specify padding: 20px
to outerWrap
.
Next, let’s see how to avoid the hard-coded pixel values from the above grid-template-columns
and grid-template-rows
.
Using fr
units instead of hard-coded pixel values
Let’s update our CSS code to this:
.outerWrap {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
div {
/* margin: 20px; */
border: 1px solid black;
background: tomato;
color: white;
font-size: 40px;
/* height: 100px; */
}
Let’s focus on one part of the above code in particular:
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
As we can see, the values above are pretty intuitive: 1fr
, as you might have guessed it, stands for “1 fraction”. A fraction of what?
In a CSS grid layout, the 1fr is the smallest unit of either the grid columns or grid rows in your grid wrapper element.
Now, it should be obvious that we’ll have an easy time recreating the following simple layout:
Looking back to everything we’ve covered in this article series, we’ve gone a long way from when we first wanted to build a layout such as the one in the above image.
Now, let’s rebuilt the layout in the image above, only this time, we’ll use the CSS grid layout.
Building a simple layout using CSS grid
We’ll start off this a wrapper:
<div class="gridWrapper">
<!-- all our grid items will go here -->
</div>
Next, let’s add four elements:
<div class="gridWrapper">
<div class="header">header</div>
<div class="sidebar">aside</div>
<div class="main">main</div>
<div class="footer">footer</div>
</div>
That’s it for the HTML! Let’s now begin adding the CSS:
body {
background: #42ceb7;
}
.gridWrapper {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
width: 1000px;
margin: 0 auto;
}
.header,
.footer {
background: #fffa97
}
.sidebar {
background: #ffce99
}
.main {
background: #5d9cf9
}
Hmm… this is not exactly what we had in mind. It’s obvious that we’re missing something.
First, let’s see how to expand the header so that it fits the entire top area of our layout.
Improving our layouts with grid-template-areas
First, let’s add our grid template areas. You can think of them as a way to “explain” our grid layout.
Here’s the updated CSS:
.gridWrapper {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
width: 1000px;
margin: 0 auto;
}
Now, we can add the grid-area
property to our .header
class:
.header {
grid-area: header
}
This update to our layout can be seen here.
Alright, that’s better, we’re getting there!
Now we can update the footer area following the exact same approach. Here’s the full updated CSS:
body {
background: #42ceb7;
margin: 0;
}
.gridWrapper {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 1fr 2fr;
grid-template-rows: 1fr 1fr 1fr;
width: 1000px;
margin: 0 auto;
height: 100vh;
}
.header {
grid-area: header
}
.footer {
grid-area: footer
}
.header,
.footer {
background: #fffa97
}
.sidebar {
background: #ffce99
}
.main {
background: #5d9cf9
}
Here’s our updated layout now.
While it’s nice to have a look at the full CSS code (as we did above), there actually haven’t added many changes in this latest update.
Here’s what we did:
- we’ve set the
height
to100vh
(100 percent of the viewport height) on thegridWrapper
class - we’ve added the
grid-area
offooter
to ourfooter
class - we’ve set the
grid-template-columns
to1fr 2fr
so that oursidebar
element takes one third, and ourmain
element takes up two thirds of the available space.
Now all that’s left to do is limit the height of the header and footer, and fully extend the height of the middle area of our page (the sidebar and the main area).
Making the middle row of a grid layout extend the full height of the available screen
Doing this in the CSS grid layout is trivially easy.
We just need to update grid-template-rows
on the gridWrapper
:
grid-template-rows: 50px 1fr auto;
What’s up with those values? Let’s start with the 50px
value.
The 50px
value takes the place of what was set to 1fr
earlier. Actually, in the previous layout, we had this value: 1fr 1fr 1fr
. That meant that there was a total of three equal fractions to every row in our layout.
Now we are replacing the 1fr 1fr 1fr
with 50px 1fr auto
.
Effectively, we are saying this: “replace the first slot with the measure of 50px”.
The second part, remains the same.
The third 1fr
now becomes auto
.
What we’re saying is this: “take this fraction and make it so that it takes up only as much space as its inner content takes up”.
By doing this, we’re implicitly affecting the middle 1fr
so that it takes up whatever is left.
In CSS grid, when we add pixel units or auto instead of 1fr, that 1fr will take up as much space as is left to be taken up.
That’s it for setting up our grid measurements.
Now all that’s left to do is to “normalize” our <body>
element by removing the margin, so we’ll add this CSS:
body {
background: #42ceb7;
margin: 0;
}
Here’s the complete CSS code after the above update:
body {
background: #42ceb7;
margin: 0;
}
.gridWrapper {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 1fr 2fr;
grid-template-rows: 50px 1fr auto;
width: 1000px;
margin: 0 auto;
height: 100vh;
}
.header {
grid-area: header
}
.footer {
grid-area: footer
}
.header,
.footer {
background: #fffa97
}
.sidebar {
background: #ffce99
}
.main {
background: #5d9cf9
}
That’s it for this basic introduction to CSS grid.
In this article, we’ve furher examined the CSS flexbox model and why it is called 1-dimensional.
We’ve also seen how we can fake two dimensions (horizontal and vertical) in pure flexbox-based layouts.
Next, we examined how the CSS grid, a two-dimensional layout model, can be used to rebuilt a basic layout.
CSS grid is a much larger topic than what we’ve covered here, but with this article, you’ve started off on the right foot to further exploration.
We’ve almost finished this article series. The next article will conclude it.
In the next article, we’ll cover some smaller topics that were left out of the picture, but that are very useful to know.
Use the below links to navigate through other tutorials in this guide.
< Back to part 22: CSS variables
You are here: Part 23, CSS Grid