/ css

Off Canvas Navigation

I wanted to make one, so I did. With some CSS transitions, and just a few lines of jQuery, it's really quite simple.

A simple, yet common concept, I wanted a menu that would slide out from the left when a button is clicked. I wanted this menu to appear overtop of my content, as opposed to pushing my content aside. I started with some simple HTML. I have a nav element that contains an unordered list of menu items, a link with the id #nav-open, and I have included the current version of jQuery (at the time of writing), and I've included my own file 'offcanvas-nav.min.js'.

<!DOCTYPE html>
<html>
  <head>
    <title>Off Canvas Navigation</title>
    <link rel="stylesheet" href="includes/css/offcanvas-nav.min.css">
  </head>
  <body>
    <nav>
      <ul>
        <li><a href="#">Item 1</a></li>
        <li><a href="#">Item 2</a></li>
        <li><a href="#">Item 3</a></li>
        <li><a href="#">Item 4</a></li>
        <li><a href="#">Item 5</a></li>
      </ul>
    </nav>
    <a id="nav-open" href="#">Menu</a>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="includes/js/offcanvas-nav.min.js"></script>
  </body>
</html>

Next, I added some basic styles to control where the menu appears. As I've been doing lately, I wrote this in using LESS. I'm not actually making use of that quite yet, but once I start adding colours and things, you'll see why I did that.

nav {
    display: block;
    height: 100%;
    overflow: auto;
    position: fixed;
    left: -20em;
    font-size: 15px;
    top: 0;
    width: 20em;
    z-index: 2000;
}

.nav-expanded nav {
    left: 0;
}

The jQuery is very simple for this. It simply targets the click event on the #nav-open link, and toggles the class .nav-expanded on the body. Doing this changes the left position of the menu.

$(document).ready(function() {
    $('#nav-open').on('click',function(e) {
        e.preventDefault();
        $('body').toggleClass('nav-expanded');
    });
});

You can see in this jsFiddle, that it doesn't quite do what I want yet. You can click the menu link, and the menu appears as expected, but it's on top of the menu link. Meaning, I can't close it.

Personally, I don't like using ids in my CSS at all. Just preference. I like to use classes for CSS, and save ids for JavaScript. So I've added a class to the menu link called .nav-toggle. I've also renamed the id to be #nav-toggle as I will be using the same link for both opening and closing the menu. Then in my LESS I add a style definition to apply to the link when the menu is expanded, and adjusted the jQuery accordingly.

Great, now the menu appears and the link moves appropriatly. But this thing is ugly. So, first, add some styles to the menu link. At the top of my LESS:

@menu-background-color: #2d2f33;
@menu-text-color: #fff;

Then I add styles to what I have already defined for .nav-toggle:

.nav-toggle {
    background-color: @menu-background-color;
    color: @menu-text-color;
    padding: 10px;
    text-decoration: none;
    display: block;
    position: fixed;
}

And, add some styles to the menu and menu items:

nav ul {
    padding: 0;
}

nav ul li {
    list-style-type: none;
    padding-left:5px;
}

nav a {
    display: block;
    color: @menu-text-color;
    text-decoration: none;
    padding: 10px 0;
}

Now what I've got is much nicer! Still not perfect, but getting close.

Everything obviously needs to be styled according to the individual project. However, one last piece I want to add now, nicer transistions.

In my LESS I added some variables for the duration of transitions:

@ease-in-duration: 0.3s;
@ease-out-duration: 0s;

Then add transtion definitions to the <nav> element, the .nav-toggle class, and the body when the menu is expanded:

nav {
    ...
    transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -webkit-transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -moz-transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -o-transition: left @ease-in-duration ease-in-out @ease-out-duration;
}

.nav-toggle {
    ...
    transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -webkit-transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -moz-transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -o-transition: left @ease-in-duration ease-in-out @ease-out-duration;
}

.nav-expanded {
    margin-left: 0;
    transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -webkit-transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -moz-transition: left @ease-in-duration ease-in-out @ease-out-duration;
    -o-transition: left @ease-in-duration ease-in-out @ease-out-duration;
}

A few other little things, and this is the 'final' result:

That's it for now! A very workable proof of concept. I'll be using this as the basis for many menus in the near future!

The full source, including the LESS file, is available on GitHub.