No Time Dad

A blog about web development written by a busy dad

Responsive Sidenav Template Using CSS Grid

Intro

I have been searching for the ultimate sidenav layout lately. In my mind, the “ultimate” layout is the one that is simplest to implement and extend. I’ve gone back and forth between flexbox and grid for this, and I’ve decided that either one is a good choice for creating a sidenav layout. Whichever one you’re most comfortable using is the right choice.

I will use grid for this example. I find that grid’s concept of columns and rows is easier for me to understand compared to flexbox containers.

My goal for this layout is that the page should have a sidenav section on the left and a content section on the right when viewed on larger screens. On smaller screens, the page should have a content section on the bottom and a header section on the top.

It should look like this on smaller screens:

Header
Content

And it should look like this on larger screens:

Sidenav
Content

Ideally, the content from the sidenav would move to the header on smaller screens. You could implement a dropdown menu for those items, but that would probably require some JavaScript and that is not something we are going to get into in this post. The header in this example does set you up nicely to implement a dropdown, though.

Another nice thing about this template is that it does not have width specifications for the sidenav or header elements. The sidenav shown above is only as wide as the word “Sidenav”. This is intentional. I like letting the contents determine the size of the element. This example uses auto for the sidenav and header elements, which means the content controls the width. Setting a specific width or height for an element can lead to alignment headaches later.

You can view the template demo here. Try resizing the screen to see how the elements change.

Building the layout

Structuring the html for the layout is easy. We’ll start with a parent wrapper div where we initialize the grid. Inside of the wrapper div we have a header div, a sidebar div, and a content div. I designed this template with a mobile first approach, so our main css will be for small screens and we’ll add a media query later for larger screens=.

Below is the html for this template. It will remain unchanged.

<div class="Wrapper">
  <div class="Header">Header</div>
  <div class="Sidenav">Sidenav</div>
  <div class="Content">Content</div>
</div>
Small screens

Our Wrapper selector is where we set display: grid; to initialize the grid. We’ll define our grid to be one column wide in this selector since the header section will sit on top of the content section on small screens. Next we’ll add the grid-template-rows property which sets the height of the rows.

We have two rows on small screens, the header row and the content row. We want the header row height to be determined by the content inside of it, and we want the content section to take up all of the remaining space on the page. To acheive this, we’ll use grid-template-rows: auto 1fr; and min-height: 100vh;. Lastly, the sidenav itself should be set to display: none; on smaller screens.

Here is our css so far:

.Wrapper {
  /* Initialize the grid */
  display: grid;
  /* Single column on small screens */
  grid-template-columns: 1fr;
  /* Let the header content determine the size via auto, and let the content fill available space via 1fr */
  grid-template-rows: auto 1fr;
  /* Allow content and sidenav to take entire page height */
  min-height: 100vh;
}

.Header {
  /* For display purposes, can be omitted */
  border: 2px solid orange;
}

.Sidenav {
  /* Hide the sidenav on small screens */
  display: none;
}

.Content {
  /* For display purposes, can be omitted */
  border: 2px solid blue;
}
Large screens

On screens 768px or larger we’ll hide the header and display the sidenav. We can do this by add a media query with the min-width set to 768px. This size is common for tablets, so when we define min-width: 768px in our media query we are saying; “for any device that is a tablet or larger, apply these styles”.

On larger screens we’ll switch over to a two column layout. One column for the sidenav and one column for the content. The sidenav column’s width is defined as auto so we can let the content determine its width. The content column’s width will use the fractal unit 1fr because we want it to take up the remaining space on the page. We’ll add display: block; to our Sidenav selector to make the element visible on the page. Conversely, we’ll add display: none; to our Header selector because we don’t want it to be visible on large screens. Lastly, we’ll remove our row height via grid-template-rows: none; because we only have a single row on larger screens and we want it to be full height.

We’ll add the media query below to our css from the previous section.

...
@media (min-width: 768px) {
  .Wrapper {
    /* Switch to a 2 column grid where the first column's (sidenav)
     width is determined by the content. 
     The second column's (content) width should fill the remaining space */
    grid-template-columns: auto 1fr;
    /* Let both elements take the full height via min-height: 100vh 
      from the previous section */
    grid-template-rows: none;
  }
  .Sidenav {
    /* Show the sidenav */
    display: block;
    /* For display purposes, can be omitted */
    border: 2px solid green;
  }
  .Header {
    /* Hide the header */
    display: none;
  }
}

Conclusion

Is this truly the ultimate sidenav layout? Probably not. But it works really well and is easy to extend, and that is why I like it so much. It was less than 30 lines of css to implement, and has really good browser support. It ticks a lot of boxes for me.

Final code

Below is the final code with the border properties removed.

<div class="Wrapper">
  <div class="Header">Header</div>
  <div class="Sidenav">Sidenav</div>
  <div class="Content">Content</div>
</div>
.Wrapper {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto 1fr;
  min-height: 100vh;
}

.Sidenav {
  display: none;
}

@media (min-width: 768px) {
  .Wrapper {
    grid-template-columns: auto 1fr;
    grid-template-rows: none;
  }
  .Sidenav {
    display: block;
  }
  .Header {
    display: none;
  }
}