In this blog, you will learn how to create an animated hamburger icon when being hovered over by using Pure CSS.
Demo
Initial styling
Firstly, let's create the html
<div class="container">
<div class="menu-icon">
<div class="line line-1"></div>
<div class="line line-2"></div>
<div class="line line-3"></div>
</div>
</div>
Next is the css for initial styling. To make things organized, scss is used to be able to declare some reusable variables.
We will use flexbox to place the container in the middle of our screen. Keep in mind that to make it work, we need to set the height and weight of the body tag too since body tag is the container of our container div. In this case, we could set it to 100% of height and width of the viewport.
$container-size: 400px;
$container-bg: #3faf82;
$line-space: 13px;
$line-height: 8px;
$line-width: 80px;
body {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
.container {
box-shadow: 1px 2px 10px 0px rgba(0, 0, 0, 0.3);
background: darken($container-bg, 15%);
display: flex;
justify-content: center;
align-items: center;
width: $container-size;
height: $container-size;
}
.line {
width: $line-width;
height: 8px;
position: relative;
background-color: #fff;
border-radius: 3px;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.3);
}
.line-1 {
margin-bottom: $line-space;
}
.line-3 {
margin-top: $line-space;
}
Animate the icon with transition
What we want to achieve is to make the second line disappear,
the first and third lines move to the center and rotate 45deg and 135deg respectively. In this case,
we can use transition
to create the animation effect. This selector comes with a few properties in terms of effect, timing, delay and duration, which allows us
to create smooth effect.
To begin with, let's make the second line shrink in and disappear. We can achieve that by using scale and transform. Also, let's make the transition time into a variable to make it easier to reuse later
$transition-time: 0.2s;
.line-2 {
transition: all ease $transition-time;
transform: scale(1);
}
.menu-icon:hover {
.line-2 {
transform: scale(0);
}
}
Here is what we got
Next, let's move to line-1 and line-3. In general, these two lines share the same behaviors but with small difference in styling.
They both need to have two actions which are moving to line-2's place and rotating at a certain degree.
We can use two css selectors to make it happen. The first is top/botttom
to shift them to center of the icon and
use transform: rotate
to perform the later.
The distance to move line-1 and line-3 to line-2 is equal the total of line-height
and line-space
.
We can declare it into a variable called line-offsetY
.
$line-offsetY: $line-height + $line-space;
.menu-icon:hover {
.line-1 { top: $line-offsetY; }
.line-2 {
transform: scale(0);
}
.line-3 { bottom: $line-offsetY; }}
For transition
to work out for top/bottom
, we have to declare such properties in their un-hovered state as well.
.line-1 {
top: 0; margin-bottom: $line-space;
transition: top $transition-time ease;
}
.line-3 {
bottom: 0; margin-top: $line-space;
transition: bottom $transition-time ease;
}
Look promising! Now we can focus on making it rotate.
There is a tricky part here. The rotation happens after the shifting on the forwards transition, but it would happen before
the shifting on backward transition. This is known as reversed transition
. Luckily, transition
allows us to create effect with specific selectors
and also have a property what allows us to declare the delay of the movement.
Hence, we need to declare transition
in both un-hover and hover state with a suitable delay time. Enough with the writing, let's jump to the coding part
We add the rotate for line-1 and line-3 with transform: rotate
.menu-icon:hover {
.line-1 {
top: $line-offsetY;
transform: rotate(45deg); }
.line-2 {
transform: scale(0);
}
.line-3 {
bottom: $line-offsetY;
transform: rotate(135deg); }
}
Next, we will add transition for both states of the lines.
A variable called $transition-delay
is created for reusability
$transition-delay: 0.5s;
.line-1 {
top: 0;
margin-bottom: $line-space;
transition: top $transition-time ease $transition-delay, transform $transition-time ease;}
.line-3 {
bottom: 0;
margin-top: $line-space;
transition: bottom $transition-time ease $transition-delay, transform $transition-time ease;}
.menu-icon:hover {
.line-1 {
top: $line-offsetY;
transform: rotate(45deg);
transition: top $transition-time ease, transform $transition-time ease $transition-delay; }
.line-2 {
transform: scale(0);
}
.line-3 {
bottom: $line-offsetY;
transform: rotate(135deg);
transition: bottom $transition-time ease, transform $transition-time ease $transition-delay; }
}
The key here is to know where to put the transition-delay
.
Since we want the lines to rotate after the shift while the icon is being hovered, we need
to declare the delay for transform
in the hover state. When we un-hover the icon, the rotation would go before the lines shift back to their initial position,
we would need to delay the top/bottom
in their initial state.
This challenge has given me a hard time to figure it out that because I would typically
use transition
for the initial state and never think about it. It took me a few hours to have an aha moment but it was interesting to learn.
You can see the demo with the code here.
If you are not a fan of transition
, you can also use animation
selector to create the same result.
This is a challenge I found from 100dayscsschallenge. Give it a try if you want to level up your css skills.
That's it! Thank you for taking your time to read the blog.