A full page menu, that replaces the current content by pushing it off the screen.

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 ).

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%;

and 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);

(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