We recently came across the beautiful Hello Monday redesign. One thing that captured our interest was the full size navigation: it replaces the current content entirely, by pushing it out. That inspired us to create todays nugget!
Here is a quick prototype of the final effect we put together:
👋 A new version of this component is available. Download now →.
Creating the structure
The HTML is structured in 3 main elements: a <main>
- containing the visible content, a div.cd-nav
- wrapping the navigation, and a .cd-nav-trigger
action button.
The .cd-nav
is composed of 2 div
elements, the first containing the navigation (.cd-nav__primary
) and the second the contact info (.cd-nav__contact
).
<div id="cd-nav" class="cd-nav">
<div class="cd-nav__content">
<div class="container max-width-sm">
<h2 class="cd-nav__title">Navigation</h2>
<div class="grid">
<div class="col-6@md">
<nav>
<ul class="cd-nav__primary">
<li><a href="#0" class="cd-nav__link cd-nav__link-selected">The team</a></li>
<!-- list items here -->
</ul>
</nav>
</div> <!-- .col-6@md -->
<div class="col-6@md">
<address>
<ul class="cd-nav__contact">
<li><a href="mailto:[email protected]">[email protected]</a></li>
<!-- other contact info here -->
</ul>
</address>
</div> <!-- .col-6@md -->
</div> <!-- .grid -->
</div>
</div> <!-- .cd-nav__content -->
</div> <!-- .cd-nav -->
The .cd-nav-trigger
contains a span.cd-nav-trigger__icon
, which is used to create the hamburger icon (the element itself is the central line, while its ::after
and ::before
pseudo-elements are used to create the upper and lower lines), and a svg
element, which is used to create the loading circle.
<a href="#cd-nav" class="cd-nav-trigger js-cd-nav-trigger text-replace">
Menu
<span class="cd-nav-trigger__icon" aria-hidden="true"></span>
<svg viewBox="0 0 54 54" aria-hidden="true">
<circle fill="transparent" stroke-width="1" cx="27" cy="27" r="25" stroke-dasharray="157 157" stroke-dashoffset="157"></circle>
</svg>
</a>
Adding style
When user clicks the .cd-nav-trigger
, the .nav-is-open
class is added to the body: this class triggers the hamburger icon animation and the menu entrance.
As for the hamburger icon, the animation can be divided into 3 different parts:
- the transformation of the hamburger icon into an arrow: the
.cd-nav-trigger__icon::after
and.cd-nav-trigger__icon::before
pseudo-elements are both rotated of 45/-45deg (using as transform-origin the right edge of the element) and their width is reduced to 50%; - the rotation of the entire
.cd-nav-trigger__icon
(180deg); - the circle loading effect: the
stroke-dashoffset
value of the circle element is set to zero (initially, it has a stroke-dasharray="157 157" and a stroke-dashoffset="157", where 157 is the circumference value).
CSS3 Transitions have been used to smooth the animation steps.
Here is a quick animation to show the whole process:
.cd-nav-trigger {
transition: transform 0.5s;
circle {
stroke: var(--cd-color-3);
transition: stroke-dashoffset 0.4s; // circle border animation
}
}
.nav-is-open .cd-nav-trigger {
transform: rotate(180deg); // rotate trigger when navigation becomes visible
circle {
stroke-dashoffset: 0; // animate circle stroke
transition: stroke-dashoffset 0.4s 0.3s;
}
}
.cd-nav-trigger__icon { // menu icon created in CSS
width: 22px;
height: 2px;
background-color: var(--color-white);
transition: transform 0.3s;
&::before, &:after { // upper and lower lines of the menu icon
width: 100%;
height: 100%;
transition: transform 0.5s, width 0.5s, top .3s;
}
&::before {
transform-origin: right top;
transform: translateY(-6px);
}
&::after {
transform-origin: right bottom;
transform: translateY(6px);
}
}
.nav-is-open .cd-nav-trigger__icon::before,
.nav-is-open .cd-nav-trigger__icon::after { // animate arrow --> from menu to arrow
width: 50%;
}
.nav-is-open .cd-nav-trigger__icon::before {
transform: rotate(45deg);
}
.nav-is-open .cd-nav-trigger__icon::after {
transform: rotate(-45deg);
}
For the menu entrance effect, the .cd-main__content
and .cd-nav__content
elements are both translated (along the X axis) by 100%. In order to create a more realistic pushing effect, we used a cubic bezier curve as transition timing function. This curve allows you to set 4 parameters (the curve control points) to create the optimal acceleration curve for the property that changes during your transition.
There are tools (like cubic-bezier.com) which allows you to customize your curve and preview the effect before using it into your code (you can also easily export the final curve parameters).
Here is a quick animation to show you the difference between a custom cubic-bezier timing functions and a linear one:
Events handling
We used JavaScript to listen to the click event on the .cd-nav-trigger
and add/remove the .nav-is-open
class consequently.