How To: A CSS-Only Mobile Off Canvas Navigation

Before we get started, I’d like to state for the record: I like JavaScript. There’s nothing wrong with using the third language of the browser. It’s a great tool for many solutions. But for simple interactions, why complicate matters?

There’s so much HTML and CSS can do on their own for presentation. Take for instance, the mobile nav icon and slide-out navigation. The “Hamburger” nav design pattern. 

There is a lot of writing on the usability side of things. For development, though, we see things happening the same way over and over again. 

Click events and state management being built in JavaScript.

That’s not unusual. These tasks are perfect for JavaScript. If we’re hiding the navigation on mobile, though, should we be adding another dependency to the page to display it?

Why don’t we go old school? The only thing keeping us from a JavaScriptless mobile navigation is state management. How can we trigger a “show” state on tap or click, right? 

Promo Image for Practical CSS Grid 50% off!

Practical CSS Grid 50% off!

Whether you're new to CSS Grid or have played with it, finding practical examples of this new layout mechanism is the best way to learn it's power. Sign up below to learn more about my Practical CSS Grid course and get 50% off when it comes out!

Sign Up Now

The original “stated” element

As it turns out, we have states in HTML that are accessible by CSS. <input type="checkbox"> has a checked and unchecked state. This functionality has been in the browser since HTML 2.

We need to structure our HTML to make the best use of out CSS. 

<nav class="navigation">
    <input type="checkbox" id="nav-toggle">
    <label for="nav-toggle">Menu</label>

    <ul class="nav-list">
        <li><a href="#">Nav Item 1</a></li>
        <li><a href="#">Nav Item 2</a></li>
        <li><a href="#">Nav Item 3</a></li>
    </ul>

</nav>

First, it’s imporant for our label to have a “for” attribute. This will allow it to function in place of the checkbox itself later. Also notice in this example, our <ul> for our navigation is a sibling to our #nav-toggle checkbox. We’re going to use this relationship to select it in CSS.

Reading the State in CSS

Gif of checkbox toggling state

Now that we’ve got a stated element on the page, we need to read that in CSS.

Here’s the simplest CSS example to use the state:

We start with our initial state. In the case of a mobile nav it shouldn’t be shown. So .nav-list is display: none. When we select .nav-list, we want to couple it to its stateful element. In this case, we’ll use the CSS sibling selector ~.

To check the state of #nav-toggle, we’ll use the :checked pseudo class.

#nav-toggle ~ .nav-list {
    display: none; // Initial state
}
#nav-toggle:checked ~ .nav-list {
    display: block; // Toggled State using the :checked pseudo-class
}

Now, we have state built into our CSS for our navigation. All we have left is to style elements to look like we expect a mobile navigation to look.

This is where the “for” attribute on the label comes in handy. I’ve never seen a mobile navigation button with a checkbox. So first, we’ll hide the checkbox and do our main styling on the label. This work is usually done on an anchor tag.

This is where preference you can deviate. Make it the mobile nav style you like best. You will need to change the <label>’s display property from “inline” to “block.” Past that, I put my money on the word “Menu” and not a hamburger Icon, with a simple border style.

#nav-toggle ~ label {
    display: block;

    // Styling
    background-color: white;
    padding: 15px 0;
    border: 1px solid grey;
    border-radius: 3px;
    width: 100px;
    text-align: center;
}

“Off-canvas” Navigation

While a show/hide navigation would work, “off-canvas” navigation is still in vogue. So, we’ll take our actual navigation and move it to the right out of the visible viewport.

.nav-list {
    position: fixed; // Absolute positioning would work as well
    right: -100%;
    top: 0;

    height: 100%;
    width: 250px; // In most sites, I’d use a VW instead of a hard pixel value
}

Animating

Gif of nav toggling

I’m not great with animation, but I know animation is key to a good user interface. Instead of having the off-canvas nav magically show up on page, we’ll have it slide in.

To do this, we’ll use CSS transform’s translateX function and a nice bouncy transition. For consistency, you’ll want to use the same movement and transition on the label AND .nav-list.

#nav-toggle ~ .nav-list, #nav-toggle ~ label {
	// Nice bouncy transition
    transition: .5s transform;
    transition-timing-function: cubic-bezier(.38,.52,.37,1.27);
}

#nav-toggle:checked ~ .nav-list, #nav-toggle:checked ~ label {
    transform: translateX(-$flyout);
}

Finished Product

That’s it. You now have a functioning mobile navigation with no JavaScript necessary.

See the Pen Mobile Nav no JS by Bryan Robinson (@brob) on CodePen.


I created the mobile navigation on this site that way. Una Kravets’ “Power of CSS” slides and youmightnotneedjs.com inspired me to create this for my site and share the process.

You May Also Enjoy

Top 3 uses for the ::before and ::after CSS pseudo elements

It’s no secret that I'm a fan of ::before and ::after pseudo-elements. I use them to great effect for creating darkened overlays in this previous post. They have so many uses beyond that, though. Here are my top 3 uses for them in my every-day development process.

How To: Use CSS Grid to Mix and Match Design Patterns

In a previous tutorial, I described how to create a simple fluid card grid with CSS Grid. In this tutorial, we'll take it a step farther and create promotional spaces that morph in interesting ways.

How To: Use CSS :after pseudo-elements to create simple overlays

Use :after elements to create the simplest HTML possible to render useful and fun overlays on top of background images. Then extend them with blend-modes!

My Side Projects

Web Workers Logo Web Workers Logo Web Workers Logo Web Workers Logo