Animated hamburger menu icon to x (close icon) in CSS CSS

You can make your website feel more interactive by adding transitions and animations throughout your user interface. Here is how to animate a hamburger menu icon into an x (close icon) with CSS.

First, we'll need to either build the menu icon with HTML and CSS or use an SVG to be able to animate it. You won't be able to animate a PNG or JPG icon. I'll use the code that I wrote for a hamburger menu icon in CSS and add the CSS to animate it on top of that.

So here is our HTML for our menu icon.

<a href="#" id="menu-icon">
  <div class="bar"></div>
  <div class="bar"></div>
  <div class="bar"></div>
</a>

And here are our styles for our menu icon.

a#menu-icon {
  display: inline-block;
  padding: 10px;
}
a#menu-icon .bar {
  margin: 3px;
  width: 25px;
  height: 3px;
  background: #000;
  border-radius: 1.5px;
}

The first thing we might want to animate is a hover state so that it seems more interactive. When a user hovers over the hamburger menu icon, we can add some simple styles to expand the lines just to make it feel more engaging.

We will also want to reduce the padding around the anchor in the hover state to the balance out the increased margin so the menu icon doesn't shift.

Thinking one step ahead, we will be using the class "close" when animating the menu icon into a close icon and we won't want to increase the margin when hovering over the x, only when it does not have the class "close" added. We can do this with the :not() pseudo-class.

a#menu-icon:not(.close):hover {
  padding: 6px 10px;
}
a#menu-icon:not(.close):hover .bar {
  margin: 5px 3px;
}

This selects the anchor and inner elements when the anchor doesn't have the class "close". It then reduces the vertical padding on the anchor to 6px without changing the horizontal padding from 10px which we defined before and increases the vertical margin on each .bar element of the menu icon to 5px without increasing the horizontal margin from 3px which we previously defined. These balance out so we will just see the lines move apart without the icon shifting.

But we need to make it happen smoothly, not instantly. So to make it a smooth transition, we'll add the transition property to our CSS rule for the inner elements (not the hover rule for the link). As well as transitioning the margin, we'll also want to transition the transform values that we add later.

We can do this by adding the following declaration to our a#menu-icon .bar rule.

transition: margin 0.2s, transform 0.2s;

/* same as above but with prefixes added */
-webkit-transition: margin 0.2s, -webkit-transform 0.2s;
transition: margin 0.2s, -webkit-transform 0.2s;
-o-transition: margin 0.2s, transform 0.2s;
transition: margin 0.2s, transform 0.2s;
transition: margin 0.2s, transform 0.2s, -webkit-transform 0.2s;

This tells the browser to animate the transition between the initially defined margin and transform values (or defaults) and any newly applied margin or transform values in 0.2 seconds.

That's our hover state done.

Next we will animate the hamburger menu icon into an x (close icon) when it is clicked. To do this, we will need to use JavaScript to check for a click event and toggle a class in the markup, then use CSS to select each bar/line of the menu icon individually and apply styles to move and rotate the first and last bars while hiding the middle bar.

This is the reason why I used 3 individual elements for the bars instead of using ::before and ::after pseudo-elements - because if we had generated the first and last lines based on the central line, we wouldn't be able to hide the central line without also hiding the other 2 and we want to just show 2 of them to form an x shape.

Before we write the CSS to convert the menu icon into an x (close icon), it would be helpful to add the JavaScript first, so we can quickly and easily test our new CSS just by clicking on the menu icon.

const menuButton = document.querySelector("a#menu-icon");

menuButton.addEventListener("click", function(e) {
  e.preventDefault();
  menuButton.classList.toggle("close");
});

This selects the anchor a element with the id "menu-icon", stores it in a variable, then adds an event listener for a click event and toggles (adds or removes) the class "close" each time it is clicked. We can then use the class "close" in our CSS to add styles only when the JavaScript has added that class.

Since the menu icon bars/lines are horizontal, we'll need to rotate one of the lines -45deg and rotate another line 45deg. We can do this with the rotate() transform value. We will also need to hide the middle line which we can do with opacity. We will also need to move the top line down and move the bottom line up so they are stacked with one on top of the other. We can move the lines vertically with the translateY() transform value.

All of our inner elements that respresent the lines of the menu icon have the same class and no unique class or id for each line because we don't need to differentiate between them in the HTML, we can select them individually with CSS.

We can select the first and last .bar elements of the menu icon with the :first-child and :last-child pseudo-classes. To target the middle line, we can use the :nth-child() pseudo-class and pass in the n number. The middle line is the second element within the icon, so in this case it will be :nth-child(2).

a#menu-icon.close .bar:first-child {
  -webkit-transform: translateY(6px) rotate(45deg);
      -ms-transform: translateY(6px) rotate(45deg);
          transform: translateY(6px) rotate(45deg);
}
a#menu-icon.close .bar:nth-child(2) {
  opacity: 0;
}
a#menu-icon.close .bar:last-child {
  -webkit-transform: translateY(-6px) rotate(-45deg);
      -ms-transform: translateY(-6px) rotate(-45deg);
          transform: translateY(-6px) rotate(-45deg);
}

This selects each of the 3 lines of the menu icon individually and rotates the first and last lines in different directions and moves them vertically in different directions (towards each other) while hiding the middle line. This makes our 3 line hamburger menu icon become 2 lines in the shape of an x (close icon).

The transition declarations we added earlier will smoothly animate the rotating motion and vertical movement from the inital values to the values defined here when the class "close" is added by the JavaScript.

Here is the animated hamburger menu icon we've created with this code. Hover over it to see the hover state and click on it to see the animation into an x (close icon). You can then click again to turn it back into the menu icon and move away to remove the hover state.

Thanks for reading.
Ryan

Published on 23 Jul 2022