JF's Dev Blog

Django, Vue, and other things, too

A Responsive Blog Layout Using CSS Grid

Introduction

CSS Grid is a fancy new layout strategy for webpages. Can I use... puts global browser support for CSS Grid around 84%, with recent versions of Firefox, Chrome, Safari, and Edge all supporting the standard. Current versions of Android and iOS browsers also ship with CSS Grid support, making it a fine choice for building a responsive website layout (like this one!) if your audience uses modern web browsers.

Mozilla has an excellent Introduction to CSS Grid Layout, and I recommend reading up on CSS Grid before making sense of the code samples in this post.

The Lay(out) of the Land

Take a look at the layout of this website. Really size it up. It's got a header that spans the width of the page. Below that, there's a large section for content with a sidebar alongside. At the bottom, we've got a footer that's downright edgy--in that it stretches from one edge of the page to the other. Here's a shorthand representing the different areas of this website, a 2x3 grid with the header and footer each occupying an entire row:

header   header
content  sidebar
footer   footer

In what is possibly the neatest bit of CSS I've ever seen, with very little markup we can use the above shorthand to define a valid CSS Grid:

.container {
  display: grid;
  grid-template-areas:
    'header header'
    'content sidebar'
    'footer footer';
}

header {grid-area: 'header'}
.content {grid-area: 'content'}
.sidebar {grid-area: 'sidebar'}
footer {grid-area: 'footer'}

Here, we're using our shorthand as the grid-template-areas attribute, which CSS grid understands as a 2x3 grid. To use dev-friendly names in the grid-template-areas definition, all we have to do is assign grid-area attributes to elements that will occupy areas of the grid.

Now that we've got the styles, we'll need the HTML. The markup to go along with that CSS is simpler than a syrup made by combining one part water and one part sugar:

<div class="container">
  <header/>
  <section class="content"/>
  <aside class="sidebar"/>
  <footer/>
</div>

(Using content sectioning elements introduced in HTML5 adds semantic value to the HTML, and it can help prevent div soup [</div></div></div>] at the end of a document.)

With this HTML layout and CSS grid template, we've got the beginning of a pretty schwifty grid. One thing we're missing is information about how the template areas should fill up the grid container.

Trying It on for Size

Before we look at the code to size areas within our grid, here's a preview of how Firefox Developer Edition represents this page's grid:

Firefox dev tools grid layout

The bulk of the grid is dedicated to the first column of the second row, the content grid area. The header and footer only take up as much room as they need, and the sidebar is quite a bit smaller than content.

With that in mind, let's take a look at the grid's CSS with sizing information added:

.container {
  display: grid;

  /* Grid sizing */
  grid-template-columns: 1fr 20rem;
  grid-template-rows: auto 1fr auto;

  grid-template-areas:
    'header header'
    'content sidebar'
    'footer footer';
}


header {grid-area: 'header'}
.content {grid-area: 'content'}
.sidebar {grid-area: 'sidebar'}
footer {grid-area: 'footer'}

The new grid-template-* lines do a lot of work.

grid-template-columns specifies how the grid's columns are sized. For this site, the second column (where sidebar lives) is set to a fixed width of 20rem, while the first column (where content lives) takes up 1fr, which means "one fraction of the available space". Because there are only two columns, content takes up all the space in the container not allocated to sidebar.

grid-template-rows--contain your surprise--defines sizing rules for rows in the grid. Here, both header and footer are set to auto, which means they'll only take up as much space as their content. Speaking of content, it's set to 1fr, meaning it will take up the rest of the available space.

This demonstrates a substantial advantage of CSS grid: it provides a single place, just in CSS, to define a page layout. In contrast, changing a page layout when using a CSS framework like Bootstrap involves editing class attributes in a number of HTML elements. The markup is more verbose, too:

<!-- content and sidebar in Bootstrap -->

<div class="row">
  <div class="col">
    <section class="content"/>
  </div>
  <div class="col-sm-4">
    <aside class="sidebar"/>
  </div>
</div>


<!-- content and sidebar with CSS grid -->

<section class="content"/>
<aside class="sidebar"/>

To change our layout using CSS grid, all we need to do is to update the grid-related CSS classes. It makes for a clean separation of content and presentation.

Making a Grid Responsive

Because a grid is defined at the container level, the container is the only thing we need to change to make a grid responsive:

/* Use a one-column layout on extra small screens */
@media(max-width: 575px) {
  .container {
    grid-template-areas:
      'header'
      'content'
      'sidebar'
      'footer';
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr auto auto;
  }
}

Thanks to grid's grid-template-areas, we can change a page's layout without having to reorder HTML elements in the DOM. We didn't have to touch a single HTML element to make our two-column layout single-column for small screens.

Conclusion

I'd been meaning to learn CSS grid for quite a few months, and I'm glad that I took the time to dig into it a bit. It's a powerful standard that can power modern web layouts with joyfully little CSS and markup. Grid even made it easy for me to drop Bootstrap's grid system for this website, but that's a story for another day.