Home Blog Create Another Responsive Hamburger Menu (There are Many Like It, But This One Is Mine)

DESIGN

Create Another Responsive Hamburger Menu (There are Many Like It, But This One Is Mine)

Posted by justin on June 13, 2016, 9:16 a.m.

blog-hamburger-header.png

Introduction

In this article, we’ll create a basic off-canvas navigation.  Yes, there are many ways to do this, and yes, this is essentially built-in to pretty much every framework. But if you’re interested in building your own lightweight, robust, and derp-simple hamburger nav, you’re in the right place. Or, if you’re reading this near Halloween, the fright place.

We’re going to leverage some helpful, ubiquitous tools to accomplish this more easily: Sass instead of vanilla CSS, jQuery instead of vanilla Javascript, and FontAwesome instead of vanilla words. Because who likes vanilla?

What we're building:

See the Pen GqpbMJ by Justin Stollsteimer (@justinstollsteimer) on CodePen.

1. The HTML

The Header

Hooray for semantic HTML! We’ll start with a simple <header> element, and fill with all sorts of goodies. Well, two goodies. A logo, and a <nav> element with a list of links.

Header HTML:

1
2
3
4
5
6
7
8
9
10
11
12
13
<header>
  <a class="logo" href="#page">
    <img src="http://www.placehold.it/80x80?text=Logo" alt="Logo" />
  </a>
  <nav>
  <ul class="links">
    <li><a href="#">Products</a></li>
    <li><a href="#">Services</a></li>
    <li><a href="#">About Us</a></li>
    <li><a href="#">Contact Us</a></li>
  </ul>
  </nav>
</header>

The Page

Most likely, you’ll have more than just a logo and navigation on your website. Those cat videos have to go somewhere, don’t they? Let’s set up a basic container for the page content and fill it with your choice of -ipsum.

Page HTML:

1
2
3
4
5
...
<article>
  <h1>Page Title</h1>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus luctus mi eu elit egestas blandit pharetra sed risus.</p>
</article>

The Wrapper

By wrapping the entire page in an element, we’ll gain some finer control over how the navigation gets positioned off-canvas.

Wrap It Up:

1
2
3
<div class="page">
...
</div>

2. The CSS

“Hey, that’s a nice looking website!” is what your adorable grandmother would say, but you know better. She does too -- she’s just being nice. Let’s make it pop! Some of this is purely aesthetic, but there are a few functional bits.

The Header

We’re going to make the header position fixed, so it sticks to the top of the page while you scroll. Why? Because that’s what we’re doing now, keep up, Terry! Let’s also give it a little visual flair by making nav items full-height (with hover state!) and anchoring the logo to the top left.


Header Styles:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
header {
  font-family: sans-serif;
  background: #2DA6D1;
  position: fixed;
  top: 0;
  left: 0;
  height: auto;
  width: 100%;
}

a.logo {
  display: inline-block;
  float: left;
 
  img {
    display: block;
    height: auto;
  }
}

nav {
  ul.links {
    padding: 0;

	li {
  	  display: inline-block;

  	a {
    	  color: white;
    	  display: block;
    	  padding: 10px 15px;
    	  font-size: 1.5rem;
    	  font-weight: bold;
    	  line-height: 60px;
    	  text-decoration: none;

    	  &:hover,
    	  &:active {
          background: rgba(white, 0.15);
    	  }

  	}
    }
  }
}

The Page

Even though this page is a placeholder, let’s dress it up a tad.


Page Styles:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
html,
body {
  background: darken(#2DA6D1, 20%);
  font-family: sans-serif;
  overflow-x: hidden;
 
  * {
    box-sizing: border-box;
  }
}

html {
  font-size: 62.5%;
}

body {
  font-size: 1.3rem;
  line-height: 1.5;
}

div.page {
  background: #eeeeee;
  padding: 100px 25px 25px 25px;
  position: relative;
  right: 0px;
}

h1 {
  font-size: 3.0rem;
  margin-bottom: 1.5rem;
}

p {
  margin-bottom: 1.5rem;
}

Diatribe: Mobile-First vs. Desktop-First

Angry Mob: “Say, Justin, we couldn’t help but notice that despite all the awesomeness, you’re still designing like it’s 1999.”

Justin: “What do you mean? This is the future of web design! THE FUTURE!”

Angry Mob: “We mean, you’re not doing it mobile first!”

Pitchforks and torches rise from the crowd as it quickly surrounds the hapless designer.

--Scene--

Truth-bomb: I build my CSS in a desktop-first frame of mind.

Despite the onslaught of mobile-first methodologies, in my own head it just makes sense to write code for the desktop version, then resize and remove elements as the viewport gets smaller.

That’s not to say that I don’t frequently design mobile-first. Starting with a mobile canvas is a great way to streamline user flows and determine the hierarchy of information. If we’re building an application that’s largely accessed by mobile device users, you bet your sweet bippy we’ll build mobile prototypes before desktop. But, when it comes time to create HTML and CSS, I’m still going to build it for large screens first, then scale down.

3. The Media Queries

Let’s make some magic! I’ve used this method to create nested media queries in the SCSS, and set up a mixin with a single breakpoint of 800px -- this is the viewport width at which the navigation will move off-canvas. Nesting media queries keeps all related styles together on the page -- rather than having to scroll up and down to locate and override styles in the traditional style.

The Mixin:

1
2
3
4
5
6
7
8
9
@mixin bp($point) {
  $bp-small: "(max-width: 800px)";
 
  @if $point==small {
    @media #{$bp-small} {
  	@content;
    }
  }
}

We’ll now employ the mixin in a few key places to re-style the page for viewports under the threshold:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
div.page {

  . . .

  @include bp(small) {
    padding: 100px 25px 25px 25px;
  }
}

a.logo {

  . . .

  @include bp(small) {
    position: absolute;
    top: 0;
    left: 0;
    width: 44px;
    height: 44px;
  }
}

nav {

  . . .

  @include bp(small) {
    position: absolute;
    right: -200px;
    width: 200px;
    z-index: 100;
  }

  ul.links {
    
  . . .

    li {
      
      . . .
      
      
  	@include bp(small) {
    	  display: block;
  	}
       
      a {

        . . .
        
    	  @include bp(small) {
          line-height: 1.5;
    	  }
      }
    }
  }

}

Tight! This is all well and good, but we’re missing a very important hamburger. We’ll add an anchor to the header with two icons -- one is the hamburger, and one is an indicator to close the menu (you’ll see later, chill out). We’ll hide the “close” icon with CSS for now.


Hamburger HTML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<header>


  . . .

  <a class="mobile-menu-trigger" href="#">
    <i class="menu fa fa-navicon"></i>
    <i class="close fa fa-arrow-right"></i>
  </a>
</header>

Hamburger CSS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
a.mobile-menu-trigger {
  display: none;
 
  @include bp(small) {
	display: inline-block;
	position: fixed;
	top: 18px;
	right: 18px;
	width: 44px;
	height: 44px;
	font-size: 1.8rem;
	line-height: 44px;
	overflow: hidden;
	text-align: center;
	text-decoration: none;
  }
 
  i.close {
	display: none;
  }
 
  i.menu {
	display: inline-block;
  }
}

4. The Javascript

My Hamburger Doesn't Do Anything

And traditionally they shouldn’t do anything other than “fill your belly” or “remain at rest.” But in our case, it needs to trigger the off-canvas menu to swoop into view. Rather than two separate actions for showing and hiding all the necessary navigation elements, I’ve found it easier to simply toggle a class on the <body> tag and let the CSS do the heavy lifting.


Hella-simple jQuery:

1
2
3
4
$("a.mobile-menu-trigger").on("click", function(e){
  e.preventDefault();
  $("body").toggleClass("show-navigation");
});

The Heavy Lifting

It’s not really that heavy. When the body contains the class “show-navigation”, we need to do a couple things: shift the page content over and swap the hamburger icon with a “close” icon.


Shift the Page and Swap the Icon:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@include bp(small) {
  body.show-navigation {
    
    div.page {
  	right: 200px;
    }
    
    nav {
  	right: 0px;
  	transition: all 0.25s ease 0.15s;
    }
    
    a.mobile-menu-trigger {
  	right: 220px;
 	 
  	i.close {
        display: inline-block;
  	}
 	 
  	i.menu {
    	  display: none;
  	}
    }
  }
}

5. The Animation

That’s all well and good, but feels a little… clunky, doesn’t it? We can add CSS transitions to the the moving parts to animate the page slide. Not only does this feel more polished, but offers clearer feedback to the user about what’s happening.


Animate the Page:

1
2
3
4
5
6
div.page {

  . . .

  transition: all 0.25s ease 0s;
}

Animate the Hamburger Icon:

1
2
3
4
5
6
7
8
a.mobile-menu-trigger {
  @include bp(small) {

    . . .

    transition: all 0.25s ease 0s;
  }
}

Animate the Nav List (slight delay on open, but close with the rest of the page):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
nav {
  @include bp(small) {
  
    . . .

    transition: all 0.25s ease 0s;
  }

  ul.links {
    a {

      . . .

      transition: all 0.25s ease 0s;
    }  
  }
}

@include bp(small) {
  body.show-navigation {
    nav {

      . . .

      transition: all 0.25s ease 0.15s;
    }
  }
}
    

Summary

By Jupiter, we’ve done it! A super-simple responsive navigation. A hamburger menu isn’t always the way to go, but it’s a recognized pattern for menus that simply have too many items to fit on a wee mobile viewport. Until next time, friend!

View Source

References:

: Breakpoint Mixins (https://responsivedesign.is/develop/getting-started-with-sass)

: Unitless line height (https://css-tricks.com/almanac/properties/l/line-height/)

: Example Codepen (http://codepen.io/justinstollsteimer/pen/GqpbMJ)