This tutorial will teach you how to create and animate a hamburger menu icon and then attach an event listener through jQuery for triggering a drop-down menu.
I will be using Jade (Pug) and Sass instead of vanilla HTML and CSS. So you should at least have a basic knowledge of these templating engines.
Creating the Playground
We will start with the implementation of a simple playground. I will only be providing the Jade template along with Sass styling, since this is not the scope of the tutorial. You can take it and use it or you can come up with your own design.
Jade file:
1 |
body
|
2 |
#container
|
3 |
#header
|
4 |
#body
|
5 |
.content |
6 |
.left |
7 |
.right |
8 |
- for (i=1; i <= 5 ; i++ ) |
9 |
div( id="text" + i ) |
10 |
.content |
11 |
.left |
12 |
.right |
13 |
- for (j=6; j <= 10 ; j++ ) |
14 |
div( id="text" + j ) |
15 |
.content |
16 |
.left |
17 |
.right |
18 |
- for (k=11; k <= 15 ; k++ ) |
19 |
div( id="text" + k ) |
Sass file:
1 |
=flex() |
2 |
display: -webkit-box |
3 |
display: -moz-box |
4 |
display: -ms-flexbox |
5 |
display: -webkit-flex |
6 |
display: flex |
7 |
|
8 |
=transition($time) |
9 |
-webkit-transition: all $time ease |
10 |
-moz-transition: all $time ease |
11 |
-ms-transition: all $time ease |
12 |
-o-transition: all $time ease |
13 |
transition: all $time ease |
14 |
|
15 |
html, body |
16 |
margin: 0 |
17 |
padding: 20px 0 |
18 |
+flex() |
19 |
justify-content: center |
20 |
|
21 |
//----------------------------------// |
22 |
|
23 |
#container
|
24 |
width: 320px |
25 |
height: 550px |
26 |
background-color: #ebebeb |
27 |
overflow: hidden |
28 |
|
29 |
#header
|
30 |
height: 45px |
31 |
background-color: #9b9b9b |
32 |
position: relative |
33 |
|
34 |
|
35 |
#body
|
36 |
padding: 0 20px |
37 |
padding-top: 40px |
38 |
+flex() |
39 |
|
40 |
flex-direction: column |
41 |
justify-content: flex-start |
42 |
|
43 |
.content
|
44 |
+flex() |
45 |
|
46 |
flex-direction: row |
47 |
justify-content: flex-start |
48 |
margin-bottom: 25px |
49 |
|
50 |
|
51 |
.left
|
52 |
width: 100px |
53 |
height: 100px |
54 |
margin-right: 15px |
55 |
background-color: #e1e1e1 |
56 |
|
57 |
.right
|
58 |
@for $i from 1 through 15 |
59 |
#text#{$i} |
60 |
margin-top: 10px |
61 |
width: 50 + random(100) + px |
62 |
height: 10px |
63 |
background-color: #e1e1e1 |
Note: Here I created two mixins named flex
and transition
. Mixins make it easier to reuse some CSS rules by grouping them. Whenever I need to add display:flex
with all vendor prefixes, I can just use +flex()
instead, thanks to the mixin.
We will use this structure and build upon it for the rest of the tutorial.
The final result should look like this:



Hamburger Menu Icon
Now it is time to create a simple yet attractive hamburger menu and animate it through CSS.
Add a new div inside #header
and name it #hamburger
. Then create two children divs inside #hamburger
. They should have a common class and individual IDs.
1 |
#hamburger
|
2 |
.strip#top |
3 |
.strip#bottom |
Now we need to style the parent #hamburger
div and the children divs with the common class of .strip
.
1 |
#hamburger
|
2 |
height: 100% |
3 |
width: 45 |
4 |
+flex() |
5 |
|
6 |
flex-direction: column |
7 |
justify-content: space-between |
8 |
padding-left: 20px |
We set the height of the div equal to its parent div, which is #header
, by defining the height: 100%
. Also, we set a width value for this parent div, which will define its "clickable" region.
Next, we add flexbox with all vendor prefixes by using mixins, which we created earlier.
Since we want our .strip
divs to be positioned vertically, we set flex-direction: column
and then use justify-content: space-between
in order to put space in between the .strip
divs.
Then we need to push those divs towards each other by adding bottom and top padding to the respective divs.
1 |
#top |
2 |
margin-top: 17px |
3 |
|
4 |
#bottom |
5 |
margin-bottom: 17px |
We also added padding-left: 20px
in order to move the .strip
divs further to the right.
The next thing is to style the strips. This is relatively easy by just defining the size and the color of the divs.
1 |
.strip
|
2 |
width: 25px |
3 |
height: 2px |
4 |
background-color: #ffffff |
The final result with the hamburger menu icon should look like this:



The next thing is to animate the menu icon so that when it is clicked, it should animate into a cross sign.
Animating the Hamburger Menu Icon
At this point, we are going to use basic jQuery to toggle some CSS classes.
Let's first create the CSS classes to be toggled.
We are going to utilize the translate and rotate settings of the transform
property of CSS along with the transition
property.
First, add transitions to both the #top
and #bottom
divs by using mixins with a specific timing parameter.
1 |
#top |
2 |
margin-top: 17px |
3 |
+transition(.25s) |
4 |
|
5 |
#bottom |
6 |
margin-bottom: 17px |
7 |
+transition(.25s) |
Now we need to define the styling of the classes to be toggled.
We will be rotating and translating each .strip
div individually, so we need to toggle different classes both for the #top
and #bottom
divs.
1 |
#top |
2 |
margin-top: 17px |
3 |
+transition(.25s) |
4 |
|
5 |
&.topRotate |
6 |
transform-origin: center |
7 |
transform: translateY(4px) rotateZ(45deg) |
8 |
|
9 |
#bottom |
10 |
margin-bottom: 17px |
11 |
+transition(.25s) |
12 |
|
13 |
&.bottomRotate |
14 |
transform-origin: center |
15 |
transform: translateY(-5px) rotateZ(-45deg) |
Here we defined the styling for two different classes named .bottomRotate
and .topRotate
, which will be added to and removed from their respective reference divs, #top
and #bottom
.
Note that different sizing of the .strip
class would result in a need for different translateY
and rotateZ
values in order to animate into a proper cross sign.
Class Toggling With jQuery
We defined how each .strip
div will animate when the topRotate
and bottomRotate
classes are present. However, we have yet to attach an event listener to toggle those classes.
Create a new JavaScript file and use the following code to toggle the topRotate
and bottomRotate
classes to divs with #top
and #bottom
IDs respectively.
1 |
$(document).ready(function(){ |
2 |
$("#hamburger").click(function(){ |
3 |
$("#top").toggleClass("topRotate"); |
4 |
$("#bottom").toggleClass("bottomRotate"); |
5 |
});
|
6 |
})
|
We put all our code inside $(document).ready(function(){})
in order to wait for the whole page to be loaded before taking any action.
When we click the #hamburger
div, it will toggle the classes for divs with specific IDs.
Note: Do not forget to add the jQuery source file into your project.
Creating the Menu List
The next step is to create a menu with list items.
Use the following structure under the #header
:
1 |
#dropDown |
2 |
#background |
3 |
ul |
4 |
li Home |
5 |
li Blog |
6 |
li Projects |
7 |
li Authors |
8 |
li Jobs |
9 |
li Contact |
So here we used the ul
tag as the parent in order to group the items with li
tags as children. Moreover, in order to create an expanding background animation, we also added a div with the ID of #background
.
Let's style the ul
and li
elements first.
1 |
ul
|
2 |
list-style: none |
3 |
padding: 0 |
4 |
margin: 0 |
Set the list-style
to none
in order to remove the bullets from the ul
elements and also set both padding
and margin
to 0 in order to remove all predefined values.
Now style the li
elements:
1 |
li
|
2 |
//display: none |
3 |
background-color: #9b9b9b |
4 |
color: #ffffff |
5 |
|
6 |
font-family: 'Quicksand', sans-serif |
7 |
font-weight: lighter |
8 |
font-size: 15px |
9 |
padding: 20px |
10 |
padding-left: 60px |
11 |
|
12 |
&:after |
13 |
position: absolute |
14 |
content: '' |
15 |
left: 60px |
16 |
width: 60% |
17 |
height: 1px |
18 |
bottom: 4px |
19 |
background: rgba(255, 255, 255, 0.25) |
20 |
|
21 |
&:last-child:after |
22 |
width: 0 |
Here I commented out the display:none
in order to be able to see the result. However, when animating, we will use it to hide the list elements initially.
I also added the after
pseudo-element and styled it accordingly in order to separate each li
element with a straight line. :last-child:after
removes this line for the last li
element.
Animating the Menu List
Now we are going to use some Sass control directives in order to add CSS keyframe animations with different attributes to each li
element.
1 |
@keyframes drop |
2 |
0% |
3 |
opacity: 0 |
4 |
transform: scale(1.3) |
5 |
|
6 |
100% |
7 |
opacity: 1 |
8 |
transform: scale(1) |
9 |
|
10 |
@keyframes fold |
11 |
0% |
12 |
opacity: 1 |
13 |
transform: scale(1) |
14 |
|
15 |
100% |
16 |
opacity: 0 |
17 |
transform: scale(0.7) |
Here we defined our keyframe animations drop
and fold
.
drop
is for animating the opening of the menu list. The initial scaling is 30% more, and it scales back down to the original size as the transparency goes from 0 to 1. The opposite action takes place in fold
.
Now we need to attach those keyframes to li
elements. This part is where the Sass comes in handy.
1 |
@for $i from 1 through 6 |
2 |
li:nth-child(#{$i}) |
3 |
animation: |
4 |
name: fold |
5 |
duration: 80ms*(6-$i) + 1ms |
6 |
timing-function: ease-in-out |
7 |
fill-mode: forwards |
8 |
|
9 |
li.anim:nth-child(#{$i}) |
10 |
animation: |
11 |
name: drop |
12 |
duration: 100ms*$i |
13 |
timing-function: ease-in-out |
14 |
fill-mode: forwards |
Here I used a for loop that goes from 1 to 6 with the index of $i
.
Now we need to use this index to attach each animation to li
elements with different durations.
First, consider the li.anim:nth-child(#{$i})
line.
Here we are grabbing the $i
th child of the li
element with class of anim
.
We will be toggling this anim
class. So, when it is added to li
elements, the keyframe animation with the name of drop
will take action. When it is removed, the fold
animation will take action.
The next important thing is the duration
attribute.
duration: 100ms*$i
for the drop
animation is extending the duration of the animation for each incrementing child number. So, when this code is compiled, the first li
child will have duration: 100ms
, and the last child will have duration: 600ms
.
This will give the sense of animating each element one after another.
We do the same thing for the fold
animation. This time, the last element should be animated faster, hence duration: 80ms*(6-$i) + 1ms
. The 1ms added to the duration is due to the fact that when you set the duration to 0, some problems are likely to occur, and your animation might not work properly.
When we were styling the li
element, I mentioned that we needed to use display:none
in order to avoid undesired animation playing. If you don't set it to none
, you will see that the fold
animation plays once when the page is loaded.
If we set the display
property to none
, we won't see that, and then we need to show the li
element before toggling the anim
class.
We want our animations to play when we click the hamburger icon. So let's use some jQuery to set the display
property of each li
item to block
and also toggle the anim
class.
1 |
$(document).ready(function(){ |
2 |
$("#hamburger").click(function(){ |
3 |
$("#top").toggleClass("topRotate"); |
4 |
$("#bottom").toggleClass("bottomRotate"); |
5 |
|
6 |
$("li").show(); |
7 |
$("li").toggleClass("anim"); |
8 |
|
9 |
});
|
10 |
})
|
You will notice that we can see the animation of each li
element individually. However, we'd rather have an expanding menu feeling.
In order to fix that, we simply need to expand the height of a div. That div is #background
, which we initially added when creating the ul
and li
elements.
1 |
#background
|
2 |
width: 100% |
3 |
height: 0 |
4 |
background-color: #9b9b9b |
5 |
position: absolute |
6 |
+transition(.45s) |
7 |
|
8 |
&.expand |
9 |
height: 550px |
We will be toggling the expand
class in order to set the height
attribute to 550px
within .45s
. Note that I used the transition
mixin to define the transition with a specific time parameter.
Conclusion
Throughout this tutorial, we practiced how to use for loops within HTML and CSS through the Jade and Sass templating engines. On top of that, we created CSS keyframe animations and attached them with different duration attributes to specific HTML elements. Then we toggled classes with jQuery to control those animations.