DEV Community

Ady Ngom
Ady Ngom

Posted on

How to create an off-canvas navigation menu with CSS

Popularized by apps like Facebook on mobile, the off canvas menu allows to save valuable real screen estate by toggling the navigation into view only when needed. We will dive and explore one way of achieving the effect using some overlooked CSS properties.

Goals and Challenges

  1. Create additional screen real estate by hiding the navigation 
  2. Easily access the menu via clicking off canvas icon and having it slide into place 
  3. Only CSS will be used for the effect
  4. Support a many browsers as possible

The markup

Starting with a wrapper div, we will use some of the new HTML5 elements to host the two main elements : the navigation and the content

<div class="wrapper"> 
 <aside> 
  <nav> 
   <ul> 
    <li>nav_1</li>
    <li>nav_2</li>
    <li>nav_3</li>
    <li>nav_4</li>
    <li>nav_5</li>
   </ul> 
  </nav> 
 </aside> 
</div>

We will then wrap the main content in a section tag

<section>
  <div class="container">
    <div class="row">
      <h2>Lorem ipsum dolor sit amet.</h2>
      <p>Lorem ipsum......</p>
    </div>
    <!--- add as many rows needed --->
  </div>
</section>


The container div is not really necessary and is the result of my habit of coding responsive sites, which we'll do a little of - just enough to stay within the scope.

The CSS

I have kept the CSS inline to keep it concise but it can easily be part of an external script.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html {
  font-size: 63.5%;
  font-family: "Roboto Slab", serif;
  height: 100%;
}
body {
  font-size: 1.6rem;
  line-height: 2.8rem;
  height: 100%;
}
.wrapper {
  height: 100%;
}
.container {
  margin: 0 15px;
}
.row {
  padding: -15px;
  margin: 15px 0;
}
.row:before,
.row:after {
  content: "";
  display: table;
}
nav li {
  padding: 8px;
}

Now that we have the initial styles in place let's take care of the two main elements.
So for the sidebar we will set an initial width of 40%, float it to the left and position it off the screen with a margin-left of -40%, also we'll add a position absolute to avoid having it stacked on the main content.

aside {
  background-color: #333;
  color: #fff;
  width: 40%;
  margin-left: -40%;
  float: left;
  height: 100%;
  position: absolute;
}


Now for the section element that houses the main content we will float it to the right with a width and height of 100% and a margin-right of 0.

section {
  width: 100%;
  height: 100%;
  float: right;
  margin-right: 0;
}

The Toggle

Now lets' add a checkbox at the top of our markup just before the wrapper div.

<input type="checkbox" id="offcanvas" class="toggle" />
<div class="wrapper"><!--- wrapper content here ---></div>

We gave it and id of offcanvas this will be used later by labels to trigger the toggle. 

Now we essentially want to react to the checked state of the input and bring the navigation to view when checked and hide again when uncheked.

We will use the adjacent selector in our CSS and target the aside element first

.toggle:checked + .wrapper > aside {
  margin-left: 0;
}


and the same for the section element

.toggle:checked + .wrapper > section {
  margin-right: -40%;
}

Make It Pretty

The main goal of hiding and showing the navigation has already been taken care of. We will just make it a little nicer by adding the infamous "hamburger" button and make the transition smooth.

Let's add a label that will contain the button and that will trigger the toggle. At the top of the section element let add this code:

<div class="container">
  <div class="row">
    <label for="offcanvas" class="toggler">
      <span class="navicon"></span>
    </label>
  </div>
</div>


The label for attribute has a value of offcanvas which is the same as the id of our input button.

This allow us to trigger the toggle from anywhere on the page where the label code exist. Now let's add some styles

.toggler {
  display: inline-block;
  cursor: pointer;
}
.navicon {
  width: 28px;
  height: 28px;
  background: url("navicon.png") no-repeat;
  background-size: cover;
  display: block;
}


We can now hide the input button

.toggle { display: none; }

A Big Hit

When the navigation is in view the only way to hide it again is by precisely hitting the menu button.

This might be ok on a desktop screen but can be challenging on smaller resolutions.

We will make the entire content clickable during that state to alleviate that issue.

So back to the html before the first label we will add another one

<section>
  <label for="offcanvas" class="biglabel"></label>
  <!--- section content --->
</section>


We gave it a class of biglabel. In our CSS we will hide it when the navigation is hidden and have it cover the entire content as a big button.

.toggle,
.biglabel {
  display: none;
}
.biglabel {
  width: 100%;
  height: 100%;
  position: absolute;
  cursor: pointer;
}
.toggle:checked + .wrapper > section .biglabel {
  display: block;
}


When we refresh the browser we can see now that the entire content becomes clickable when the navigation is in view. Perfect!! Well almost.

Smooth Like a Moonwalker

The last step is to handle the transition and make the navigation slide in nicely.

Using CSS3 transition makes it very easy. We will target both the sidebar and the main content

aside,
section {
  -webkit-transition: margin 0.5s ease-in-out;
  -moz-transition: margin 0.5s ease-in-out;
  -ms-transition: margin 0.5s ease-in-out;
  -o-transition: margin 0.5s ease-in-out;
  transition: margin 0.5s ease-in-out;
}


And Voila!! Our menu now kicks out nicely the content when coming to view and leaves without a noise.

Hopefully you found it useful and enjoyed it as much as I did. Please share and let me know your thoughts in the comments.

Cheers

Top comments (5)

Collapse
 
healeycodes profile image
Andrew Healey

Very cool and very useful. Great stuff, Ady 👍

Collapse
 
adyngom profile image
Ady Ngom

Thanks Andrew, glad you found it useful 😀 Cheers

Collapse
 
pacificregmi profile image
Pacific Regmi

Awesome html off canvas menu design

Collapse
 
mattbag00 profile image
Matt Bagni

Neat! The only thing I would change, is using transforms translate over margin for the show transition. CSS rulez

Collapse
 
navjot50 profile image
Navjot

One downside of this solution is that if the user accidentally clicks on the content, then the menu will open.