Playing with Hamburger Icon Animations

I find myself using hamburger icons a lot (see my previous tutorial, if you don't believe me) lately. Let's ignore the debate on using them for a second, and realize that they present a fun opportunity to play with CSS animations - and I've done just that. Let's play!

We're going to be doing 4 different possible animations today, all using the same exact visual design. There's a ton of different ways that these animations could be done, but these are four that I haven't seen done yet.

First off, we're going to be using a little jQuery, so you'll need to include this in your head. I'll leave it up to you which CDN you choose to use.

Our html is very simple. Each toggle is a div with specific id, and each span within the individual divs is either one of the lines of the "hamburger" icon, or a part of one of them (in the case of #nav-icon2, where each line is comprised of two spans).

html
<div id="nav-icon1">
  <span></span>
  <span></span>
  <span></span>
</div>
<div id="nav-icon2">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
<div id="nav-icon3">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
<div id="nav-icon4">
  <span></span>
  <span></span>
  <span></span>
</div>

See the Pen Menu "Hamburger" Icon Animations by Jesse Couch (@designcouch) on CodePen.

The CSS is notably longer, but not much more complex. We're using CSS transitions to animate our icons (no more JS than necessary, folks), but otherwise it's straight-forward styling.

Each icon begins with its container. They're all 45 pixels tall and 60 pixels wide; this accommodates for the typical touch-target size on mobiles (usually a minimum of 40x40 pixels). They all also have relative positioning, so that we can make each span absolutely positioned, and control exactly where they are within their respective parents.

css
* {
  margin: 0;
  padding: 0; 
}

/* Icon 1 */

#nav-icon1, #nav-icon2, #nav-icon3, #nav-icon4 {
  width: 60px;
  height: 45px;
  position: relative;
  margin: 50px auto;
  -webkit-transform: rotate(0deg);
  -moz-transform: rotate(0deg);
  -o-transform: rotate(0deg);
  transform: rotate(0deg);
  -webkit-transition: .5s ease-in-out;
  -moz-transition: .5s ease-in-out;
  -o-transition: .5s ease-in-out;
  transition: .5s ease-in-out;
  cursor: pointer;
}

#nav-icon1 span, #nav-icon3 span, #nav-icon4 span {
  display: block;
  position: absolute;
  height: 9px;
  width: 100%;
  background: #d3531a;
  border-radius: 9px;
  opacity: 1;
  left: 0;
  -webkit-transform: rotate(0deg);
  -moz-transform: rotate(0deg);
  -o-transform: rotate(0deg);
  transform: rotate(0deg);
  -webkit-transition: .25s ease-in-out;
  -moz-transition: .25s ease-in-out;
  -o-transition: .25s ease-in-out;
  transition: .25s ease-in-out;
}

#nav-icon1 span:nth-child(1) {
  top: 0px;
}

#nav-icon1 span:nth-child(2) {
  top: 18px;
}

#nav-icon1 span:nth-child(3) {
  top: 36px;
}

#nav-icon1.open span:nth-child(1) {
  top: 18px;
  -webkit-transform: rotate(135deg);
  -moz-transform: rotate(135deg);
  -o-transform: rotate(135deg);
  transform: rotate(135deg);
}

#nav-icon1.open span:nth-child(2) {
  opacity: 0;
  left: -60px;
}

#nav-icon1.open span:nth-child(3) {
  top: 18px;
  -webkit-transform: rotate(-135deg);
  -moz-transform: rotate(-135deg);
  -o-transform: rotate(-135deg);
  transform: rotate(-135deg);
}

/* Icon 2 */

#nav-icon2 {
}

#nav-icon2 span {
  display: block;
  position: absolute;
  height: 9px;
  width: 50%;
  background: #d3531a;
  opacity: 1;
  -webkit-transform: rotate(0deg);
  -moz-transform: rotate(0deg);
  -o-transform: rotate(0deg);
  transform: rotate(0deg);
  -webkit-transition: .25s ease-in-out;
  -moz-transition: .25s ease-in-out;
  -o-transition: .25s ease-in-out;
  transition: .25s ease-in-out;
}

#nav-icon2 span:nth-child(even) {
  left: 50%;
  border-radius: 0 9px 9px 0;
}

#nav-icon2 span:nth-child(odd) {
  left:0px;
  border-radius: 9px 0 0 9px;
}

#nav-icon2 span:nth-child(1), #nav-icon2 span:nth-child(2) {
  top: 0px;
}

#nav-icon2 span:nth-child(3), #nav-icon2 span:nth-child(4) {
  top: 18px;
}

#nav-icon2 span:nth-child(5), #nav-icon2 span:nth-child(6) {
  top: 36px;
}

#nav-icon2.open span:nth-child(1),#nav-icon2.open span:nth-child(6) {
  -webkit-transform: rotate(45deg);
  -moz-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  transform: rotate(45deg);
}

#nav-icon2.open span:nth-child(2),#nav-icon2.open span:nth-child(5) {
  -webkit-transform: rotate(-45deg);
  -moz-transform: rotate(-45deg);
  -o-transform: rotate(-45deg);
  transform: rotate(-45deg);
}

#nav-icon2.open span:nth-child(1) {
  left: 5px;
  top: 7px;
}

#nav-icon2.open span:nth-child(2) {
  left: calc(50% - 5px);
  top: 7px;
}

#nav-icon2.open span:nth-child(3) {
  left: -50%;
  opacity: 0;
}

#nav-icon2.open span:nth-child(4) {
  left: 100%;
  opacity: 0;
}

#nav-icon2.open span:nth-child(5) {
  left: 5px;
  top: 29px;
}

#nav-icon2.open span:nth-child(6) {
  left: calc(50% - 5px);
  top: 29px;
}

/* Icon 3 */

#nav-icon3 span:nth-child(1) {
  top: 0px;
}

#nav-icon3 span:nth-child(2),#nav-icon3 span:nth-child(3) {
  top: 18px;
}

#nav-icon3 span:nth-child(4) {
  top: 36px;
}

#nav-icon3.open span:nth-child(1) {
  top: 18px;
  width: 0%;
  left: 50%;
}

#nav-icon3.open span:nth-child(2) {
  -webkit-transform: rotate(45deg);
  -moz-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  transform: rotate(45deg);
}

#nav-icon3.open span:nth-child(3) {
  -webkit-transform: rotate(-45deg);
  -moz-transform: rotate(-45deg);
  -o-transform: rotate(-45deg);
  transform: rotate(-45deg);
}

#nav-icon3.open span:nth-child(4) {
  top: 18px;
  width: 0%;
  left: 50%;
}

/* Icon 4 */

#nav-icon4 {
}

#nav-icon4 span:nth-child(1) {
  top: 0px;
  -webkit-transform-origin: left center;
  -moz-transform-origin: left center;
  -o-transform-origin: left center;
  transform-origin: left center;
}

#nav-icon4 span:nth-child(2) {
  top: 18px;
  -webkit-transform-origin: left center;
  -moz-transform-origin: left center;
  -o-transform-origin: left center;
  transform-origin: left center;
}

#nav-icon4 span:nth-child(3) {
  top: 36px;
  -webkit-transform-origin: left center;
  -moz-transform-origin: left center;
  -o-transform-origin: left center;
  transform-origin: left center;
}

#nav-icon4.open span:nth-child(1) {
  -webkit-transform: rotate(45deg);
  -moz-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  transform: rotate(45deg);
  top: -3px;
  left: 8px;
}

#nav-icon4.open span:nth-child(2) {
  width: 0%;
  opacity: 0;
}

#nav-icon4.open span:nth-child(3) {
  -webkit-transform: rotate(-45deg);
  -moz-transform: rotate(-45deg);
  -o-transform: rotate(-45deg);
  transform: rotate(-45deg);
  top: 39px;
  left: 8px;
}

See the Pen Menu "Hamburger" Icon Animations by Jesse Couch (@designcouch) on CodePen.

As you can see, we're styling each slightly differently, but the overall concept remains the same for all. Also, in order to keep our markup clean, we're using nth-child instead of id's for each span.

I'm not going to go into overly long descriptions of each different icon; rather, if you have any questions at all, just ask in the comments section.

Lastly, we have our tiny little bit of jQuery. This is about as basic as it can possibly get, folks. We're simply toggling 'on' or 'off' the "open" class. That's all. If the current icon does not have a class of "open", it will add it. If it already has a class of "open", it will remove it. Dirt simple.

js
$(document).ready(function(){
	$('#nav-icon1,#nav-icon2,#nav-icon3,#nav-icon4').click(function(){
		$(this).toggleClass('open');
	});
});

See the Pen Menu "Hamburger" Icon Animations by Jesse Couch (@designcouch) on CodePen.

And now for the big reveal (if you haven't already clicked "Result" in one of the Codepens above, anyway)! Click away on any of the icons, and watch 'em morph. Totally unintentionally appropriate that I'm posting this as the new Transformers movie is about to come out. Have fun playing with these!

demo

Did I help you out?

Read More

←  Improving the Drawer Menu Pure CSS Drawer Menu  →

There are 10 comments. Add Yours.

A Note from the Moderator

Thanks for taking the time to comment! I'll respond as quickly as possible if necessary. In the meantime, please keep the following in mind:

  • All comments must be appropriate. I'll delete 'em if you get nasty.
  • Please allow for response time. I'm here as much as possible, but can't always respond as quickly as some would like.
  • Please stay on topic.
  • I don't work for free—if you want custom work, feel free to get in touch and I'll write up a quote!
  1. Dalton

    Dalton

    Jun 02, 2015

    Hey there, I saw this on CodePen and decided to use it in a project. It's a very cool, simple effect, but I noticed that there was some shifting after the animation was complete in mobile & desktop Safari. I tried translate instead of position on the spans and that seemed to do the trick. Here's my forked version: http://codepen.io/dalton/pen/YXZGry

    Reply to Dalton

    1. Jesse

      Jesse

      Jun 03, 2015

      Awesome - hope it works well for you in your project, Dalton. As I said in my response on Codepen, transforms are definitely smoother and more efficient than transitions. I my solutions, I used transforms for some aspects and transitions for others, so it definitely could be cleaned up to use only transforms. Thanks for the great feedback, and the improvement to my original!

  2. Joseph B

    Joseph B

    Jan 25, 2016

    Absolutely LOVE your work! I'm a 21-year old frontend developer from the UK.

    I'm implementing this in my project - however as the spans are positioned absolute I'm having some trouble aligning the icon vertically - any advise?

    Thanks a bunch!

    Reply to Joseph

    1. Jesse

      Jesse

      Jan 25, 2016

      Thanks, Joseph! The positioning of the spans shouldn't affect the positioning of the parent (the icon), as it has relative positioning. If you're trying to align something vertically within a box, I would either use the absolute positioning + negative top margin trick, or look into using display:table and display:table-cell, something like the example in this article: https://css-tricks.com/almanac/properties/v/vertical-align/

      Let me know if this helps; otherwise, feel free to give more detail or start a Codepen that I can fork.

  3. Yappko

    Yappko

    Feb 02, 2016

    Absolutely great work! I am using all of your hamburgers in my projects. Greetings from Poland!

    Reply to Yappko

  4. Alex

    Alex

    Apr 29, 2016

    I've been looking at a few different tutorials and examples for achieving these sort of effects with hamburger menus and yours is by far the simplest to understand and implement, which is great when time is limited.

    Thanks for the awesome work, and even more thanks for sharing :)

    Reply to Alex

    1. Jesse

      Jesse

      May 17, 2016

      Very glad to help out where I can! Glad you found it useful :)

  5. Doug

    Doug

    Jun 21, 2016

    Hi Jesse, I'm not sure what I'm doing wrong - the animation doesn't work when I copy the code on my end. I copied line for line and I'm failing. (I'm very new to code) Any suggestions?

    Reply to Doug

  6. Doug

    Doug

    Jun 21, 2016

    I figured it out! I didn't realize I needed to add a jquery source. Very cool, stuff!

    Reply to Doug

  7. Michael Smith

    Michael Smith

    Sep 14, 2016

    Hey. I loved what you came up with. Very cool. I just wanted to write to say that and to let you know that the embed-type span (I believe) is covering the links over to Codepen in your examples. Might be something you want to fix. Just a thought. - Michael

    Reply to Michael