Creating awesome hamburger menu icon animations

5 min read

The hamburger we're going to be making:

When it comes to creating responsive navigation bars, utilizing hamburger menu icons tied with a side menu is one of the most well-known methods available. A good rule of UX is to reward the user with different states as they click buttons throughout your website to help them visualize different interactions within the application, this can be achieved really well with a click effect on a hamburger.

The awesome thing about a hamburger icon is that it can be smoothly & easily transitioned into an X, which is a great icon for a close button.

The HTML Structure

The most simple method to produce this, would be to create a button, with three <span> elements as seen below:

<button class="hamburger">
  <span class="bar"></span>
  <span class="bar"></span>
  <span class="bar"></span>
</button>

Styling the Hamburger

Once this is created, we can convert the button to an adequate shape which can fit 3 bars vertically aligned on top of each other using the below CSS declarations:

/* remove default button styling  */
button {
  outline:none;
  background-color:transparent;
  border:none;
}

.hamburger {
  width:60px;
  height:40px;
  background-color:transparent;
  position:relative;
}

.bar {
  height:8px;
  width:100%;
  position:absolute;
  left:0;
  
  background-color:#000;
  border-radius:4px;
}

/* style each bar */
.bar:nth-of-type(1) {
  top:0;
}
.bar:nth-of-type(2) {
  top:calc(50% - 4px);
}
.bar:nth-of-type(3) {
  top:calc(100% - 8px);
}

What we've got so far:

Planning the Animation

Now we have the initial state setup, we need to do a few things:

  1. Decide what event will handle the button animation (hover, click, ect)
  2. Choose a way to handle the event
  3. Animate the button when the event is called

I'll be showcasing in this blog, the two most common methods to handle events. The event I will be using will be a click event. But before we do anything, we have to create an active class and style that bad boy!

In order to convert the hamburger into an X, we have to undertake a couple of steps. Firstly, the middle bar has to go. I'll be using opacity:0 to hide the element, as that will eliminate any possibility for it to shift around any other elements. I will access the 2nd bar, by using the nth-of-type(n) pseudo-element.

.hamburger--active .bar:nth-of-type(2) {
  opacity:0;
}

Now, we have to style the other two bars. Both bars will need to be vertically centered. In order to achieve this, we must set the top property to 50%.

This doesn't quite achieve what we want, however, as our transform-origin is set by default to the top left of the element. This means the property top: 50% will not account for the 8px of thickness the bar has. Which will result in a bar slightly further down than we want, and no one likes an unsymmetrical cross!

In order to fix this, we need to transform the element on the Y-axis by 50% of its thickness (4px).

.hamburger--active .bar:nth-of-type(1),
.hamburger--active .bar:nth-of-type(3) {
  transform:translateY(-50%);
  top:50%;
}

Great! Our bars are vertically centered, on top of each other for now:

Now, to the rotation. It's quite simple! All we have to do is add a 45deg and -45deg rotation to the elements respectively:

.hamburger--active .bar:nth-of-type(1) {
  transform:rotate(45deg);
}
.hamburger--active .bar:nth-of-type(3) {
  transform:rotate(-45deg);
}

Handling Events & States

Awesome, to the methods of event handling, the first is using Javascript! This method requires an event listener to be attached to the button, then adding a class to the button to change the styling.

const button = document.querySelector('.hamburger');

const handleClick = (e) => button.classList.toggle('hamburger--active');

button.addEventListener('click', handleClick);

The next method is called the checkbox hack and works with no Javascript, which consists of adding a transparent checkbox above the button, and styling the button according to the input:checked pseudo-element, using the ~ adjacent sibling selector to target the individual bar elements.

The full code to achieve this is below:

<button class="hamburger">
  <input class="checkbox" type="checkbox">
  <span class="bar"></span>
  <span class="bar"></span>
  <span class="bar"></span>
</button>
/* remove default button styling  */
button {
  outline:none;
  background-color:transparent;
  border:none;
}

.hamburger {
  width:60px;
  height:40px;
  background-color:transparent;
  border:none;
  position:relative;
}

.checkbox {
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  z-index:2;
  opacity:0;
  
  cursor:pointer;
}

.bar {
  height:8px;
  width:100%;
  position:absolute;
  left:0;
  
  background-color:#FF70EC;
  border-radius:4px;
  
  transform-origin:center;
  transition:all .3s;
}
.bar:nth-of-type(1) {
  top:0;
}
.bar:nth-of-type(2) {
  top:calc(50% - 4px);
}
.bar:nth-of-type(3) {
  top:calc(100% - 8px);
}

.checkbox:checked ~ .bar:nth-of-type(2) {
  opacity:0;
}

.checkbox:checked ~ .bar:nth-of-type(1),
.checkbox:checked ~ .bar:nth-of-type(3) {
  transform:translateY(-50%);
  top:50%;
}
.checkbox:checked ~ .bar:nth-of-type(1) {
  transform:rotate(45deg);
}
.checkbox:checked ~ .bar:nth-of-type(3) {
  transform:rotate(-45deg);
}

And there we go! You've added a hamburger animation to your awesome website! If you'd like to check out some more awesome menu icon hover animations, check out our Menu Icon components page.