How to Build an Off-Canvas Navigation With CSS Grid

The off-canvas pattern is a classic approach to responsive navigation. When the viewport is small enough to warrant it, bulky navigation is hidden “off-canvas” and only brought into view when toggled.

How to Build an Off-Canvas Navigation With CSS Grid

Today we’re going to build an off-canvas navigation, using CSS to do the toggling (no JavaScript required) and our good friend Grid to form the page structure. Here’s the full page demo of what we’re working towards.

Basic Page Structure

Let’s begin by building a basic page; we’re aiming for something like this:

This is a fairly typical semantic page structure; you’ll see that everything remains in a single column for small viewports, then the aside moves across on larger screens. The <nav> element is highlighted in blue for clarity.

Here we’re declaring that the container should be display: grid;, that it should have a single column of one fractional unit (not strictly necessary at this point, but we’re added it to be thorough), and that the gap between all grid items should be 10px.

So now, on larger screens, the grid declaration changes to grid-template-columns: repeat(4, 1fr);. This gives us four columns of equal width, so we then have to declare how wide we want each of our structural elements. The header, nav, and footer will all span 4 (span across four columns) whilst the section will span across just three, leaving a gap of one column for the aside to fill automatically.

Venturing Off-Canvas

This is a perfect example of how CSS positioning can still work on structural elements, even within a declared Grid. We’re going to grab our nav, remove it from the document flow, and position it off-canvas. The other grid items will fall into place just fine.

Begin with another media query. We already have our min-width query, but this time we only want to style elements up to a max-width. Up until our viewport reaches that magic 600px we want the nav to be positioned off-canvas:

We’ve given the nav a fixed width, positioning it left by enough to hide it completely. We used position fixed too, though you can also use absolute depending on whether you want the nav to scroll with the window or not.

You’ll also notice the transition rule, which will take effect once we build some toggle controls.

Toggling

Having made our nav disappear, we now need some controls to bring it back when needed. Let’s add a link to trigger it, and a link to close it again.

Add this to the header:

<a class="toggle open" href="#nav">open</a>

and this to the nav:

<a class="toggle close" href="#">close</a>
<!-- you might also want to use a “×” -->

We don’t need the open link to be visible on larger screens, so we’ll hide the .toggle elements within our min-width media query:

.toggle {
display: none;
}

:target

What’s important in the above links is the presence of a “fragment identifier” (the #nav in the href). These identifiers are used by browsers to navigate directly to specific elements on a page. In this case, we’re targeting whichever element matches the id “nav”, and once it’s been targeted we can style it using the :target pseudo class. No JavaScript necessary!