Atomic Smash homepage splash

Practical CSS Grid: Build a responsive page with a sidebar

Words by George HieronJanuary 19, 2018

In this tutorial, I will show you how to create a page layout with a sidebar, which then switches to a tabbed layout on mobile devices, all using the power of CSS Grid (and a touch of jQuery).

CSS Grid in a nutshell

CSS Grid is a native, built-in CSS solution for creating grid layouts, which is quickly becoming popular as browser support for the rules improves. To read more examples, check out Grid by Example, by Rachel Andrew: https://gridbyexample.com/

Getting started

I have put together a simple Codepen example of this, which I will be referring to throughout. You can check it out here: https://codepen.io/AtomicSmash/pen/LeMjrd

See the Pen CSS Grid – Page with Sidebar example by AtomicSmash (@AtomicSmash) on CodePen.

Firstly you can see that I have put in a hero image, to add some context to the page content. Below that, I have the main post container. This contains four main divs, the page header content, tabs, the post content and the sidebar.

I have set the grid rules on the main post__container—sidebar class, using grid-template-columns to achieve an equally-spaced 12-column layout with a gutter of 30px between each column and row. This is all that is needed to create the initial grid layout. At this point, each of the elements inside this div will fill out one column of the 12 I have created, leaving the other eight empty.

 

.post__container--sidebar {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-gap: 30px;
  align-items: start;
  @media screen and (max-width: 768px) {
    grid-column-gap: 0;
  }
}

Dividing up the content

I want the post header and content to appear on the left and the sidebar on the right; to achieve this, I have told page__header—content and post__content to span the first seven of the 12 columns I have. This is done with grid-column: 1/8;, meaning that these elements will span from the first edge (or ‘slice’, as I like to think of it – within the grid, these are the same width as the grid gap) of the grid, to the eighth.

.page__header__content,
.post__content {
   // Span the first seven columns
   grid-column: 1/8;
   @media screen and (max-width: 768px) {
     // Full grid width on mobile
     grid-column: 1/13;
     margin: 0;
   }
}

Likewise, I can tell post__sidebar to span the last four columns with grid-column: 9/13; – to go from the ninth edge to the very end of the grid at its 13th edge. This leaves us a nice gap of one fraction (1fr) of the 12-column layout, plus the 30px grid gap either side of it. Resizing the window to below 1000px causes the sidebar to then inherit the rule grid-column: 8/13;, retaining a readable width as the size of the viewport decreases.

.post__sidebar {
  // Span the last four columns
  grid-column: 9/13;
  grid-row: 1/4;
  @media screen and (max-width: 1000px) {
    grid-column: 8/13;
  }
  @media screen and (max-width: 768px) {
    // Full grid width on mobile
    grid-column: 1/13;
    grid-row: 3;
  }
}

We should now have this layout for desktop:

Grid-layout page with sidebar - desktop view

The responsive bit

Next, I want to make sure the page layout is responsive and effective for mobile devices. This is where the tabs will come in and I will be manipulating the content’s position.

As with columns, with Grid you can also specify rows. I want the page header to stay at the top of the container, so I can give it grid-row: 1; to make sure it stays in place.

.page__header__content {
  // Fix header content in the first row, always at the top of the page
  grid-row: 1;
}

The tabs have a class show—mobile, which keeps them set to display: none until required at mobile sizes. When they come in, I will want them underneath the header content, but above the post content and sidebar content – so I have given them the property grid-row: 2;.

.tabs__post {
  grid-row: 2;
  grid-column: 1/13;
}

Then the post content and the sidebar have grid-row: 3;, and all elements are set to grid-column: 1/13; so that they span the full width of the container. The sidebar has the class tabs__content—hidden so that by default, it is not displayed at mobile sizes, as now it shares the exact same grid area as the post content – otherwise, the two would overlap.

.post__content {
  // Fix post content in row 3 on mobile - below the tabs which are in row 2 
  @media screen and (max-width: 768px) {
    grid-row: 3;
  }
}
.post__sidebar {
  @media screen and (max-width: 768px) {
    // Full grid width on mobile
    grid-column: 1/13;
    grid-row: 3;
  }
}

The mobile view should look something like this:

Grid-layout page with sidebar - mobile view

Finally, I will need to add a little jQuery to make the tab buttons functional. Essentially all that is happening here is adding a tab—active class to the clicked tab, and using the (this) function to remove or add the tabs__content–hidden and tabs__content—revealed classes to show and hide the content.

$(".tab").on('click', function() {
  $(".tab").removeClass("tab--active");
  $(this).addClass("tab--active");

  if ($(this).hasClass("tab--js--content")) {
    $(".post__content")
      .removeClass("tabs__content--hidden")
      .addClass("tabs__content--revealed");
    $(".post__sidebar")
      .removeClass("tabs__content--revealed")
      .addClass("tabs__content--hidden");
  } else if ($(this).hasClass("tab--js--sidebar")) {
    $(".post__sidebar")
      .removeClass("tabs__content--hidden")
      .addClass("tabs__content--revealed");
    $(".post__content")
      .removeClass("tabs__content--revealed")
      .addClass("tabs__content--hidden");
  }
});

And that’s it! A simple example of how you might put CSS Grid into practice. I hope you found this useful; comment below if you give it a go!

Go back to top

Keep up to date with Atomic news