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.

An apartment block with a grid-like layout of windows 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:

Untitled-2

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:

Untitled-2

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:

Untitled-2

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:

Untitled-2

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:

Untitled-2

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; */
}

Here’s the above code live.

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:

Untitled-2

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
}

Here’s our simple layout now.

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 to 100vh (100 percent of the viewport height) on the gridWrapper class
  • we’ve added the grid-area of footer to our footer class
  • we’ve set the grid-template-columns to 1fr 2fr so that our sidebar element takes one third, and our main 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 our completed layout.

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

Continue to part 24, CSS tips and tricks for beginners >

Feel free to check out my work here: