Responsive Animated Navigation Menu

In the previous post we built an animated hamburger button, but we didn't have something to show (like a menu) when the button was pressed. In this post we'll build a responsive navigation menu which will show/hide when the button is pressed.

This is how it will look on desktop:

demo

And on mobile:

demo2

The HTML markup:

<nav class="menu">
    <ul>
        <li><a href="#">Home</a></li>
        <li><a href="#">About</a></li>
        <li><a href="#">Services</a></li>
        <li><a href="#">Blog</a></li>
        <li><a href="#">Contact</a></li>
    </ul>
</nav>
<button class="hamburger">
    <span class="line"></span>
    <span class="line"></span>
    <span class="line"></span>
</button>

As you can see, we have a nav tag which hold's a list of links. This is the basic structure you would have for a navigation menu. We also have the .hamburger button which will be used to show/hide the menu.

The CSS:

We already styled the .hamburger menu in the previous post, but we'll want to have it fixed on the page (in the top-right corner), and we'll make it a white circle with black lines. Also, we will shrink the lines and decrease the margins (because it will make it look better), that means we'll have to change the transform: translateY property as well so we don't break the X.

Let's add that extra CSS for that:

.hamburger {
    background-color: #fff;
    border-radius: 50%;
    position: fixed;
    width: 40px;
    height: 40px;
}

.hamburger .line {
    background-color: #000;
    margin: 4px auto;
    height: 2px;
    width: 20px;
}

.hamburger.open .line:nth-child(1) {
    transform: translateY(6px) rotate(45deg);
}

.hamburger.open .line:nth-child(3) {
    transform: translateY(-6px) rotate(-45deg);
}

Now, for the .menu styling...

First, we'll remove the ul's default margin, padding and list-style-type. Then we set it to be a flex container with the children elements (the lis) centered both vertically and horizontally:

.menu ul {
    /* Overwriting the default properties */
    padding: 0;
    margin: 0;
    list-style-type: none;

    /* General flex container with centered child elements */
    display: flex;
    justify-content: center;
    align-items: center;
}

Next, we'll position it fixed on the page, and as we want the menu to be hidden initially, we set a negative top value of -100vh. Setting the width to be 100vw will make it spawn fully across the screen on the X axis:

.menu ul {
    /* Overwriting the default properties */
    padding: 0;
    margin: 0;
    list-style-type: none;

    /* General flex container with centered child elements */
    display: flex;
    justify-content: center;
    align-items: center;

    /* Positioning and sizing */
    position: fixed;
    top: -100vh;
    left: 0;
    width: 100vw;
}

For the li items, we'll have a linear-gradient background-image and it will be also a flex container, because the inner links will be also centered.

For the size, will have a height of 100vh (full height) and a width of 20vw (20% of the total width of the page):

.menu li {
    /* Background gradient style */
    background-image: linear-gradient(60deg, #29323c 0%, #485563 100%);

    /* General flex container with centered child elements */
    display: flex;
    justify-content: center;
    align-items: center;

    /* Sizing */
    height: 100vh;
    width: 20vw;
}

In order to make it appear on the screen when the hamburger menu is clicked, we need to have a different state for the .menu.

If you remember from the previous post, we added the .open class programmatically using JavaScript to the hamburger button and this way we triggered the style for that state. Let's re-use this approach.

The styling for the open state:

.menu.open li {
    transform: translateY(100vh);
}

Because the .menu ul's top property is set to a negative value of -100vh, we need to push the li into the view by setting the translateY property to 100vh. Basically we have a simple calculation for the Y axis positioning of the element: -100vh + 100vh = 0vh.

The JavaScript:

const hamburger = document.querySelector('.hamburger');
const menu = document.querySelector('.menu');

hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('open');
    menu.classList.toggle('open');
});

Besides toggling the .open class for the hamburger button (as shown in the previous post), we toggle it for the .menu too. Simple as that! :D

The animation:

For the animation we need to add one line of CSS code to the .menu li:

.menu li {
    transition: transform 0.3s ease-in-out;
}

We can also add a nice effect by adding a transition-delay to the individual li tags using the :nth-of-type selector:

.menu li:nth-of-type(2) {
    transition-delay: 0.025s;
}

.menu li:nth-of-type(3) {
    transition-delay: 0.05s;
}

.menu li:nth-of-type(4) {
    transition-delay: 0.075s;
}

.menu li:nth-of-type(5) {
    transition-delay: 0.1s;
}

Everything looks great except that it's not yet responsive, and for that we'll have to change the layout a bit.

Basically we switch the axis... Instead of the Y axis (as we have on desktop), we'll use the X axis (left instead of top and width instead of height to be 100) on the mobile:

@media (max-width: 768px) {
    .menu ul {
        flex-direction: column;
        top: 0;
        left: -100vw;
    }

    .menu li {
        height: 20vh;
        width: 100vw;
    }

    .menu.open li {
        transform: translateX(100vw);
    }
}

The @media rule is used to define different style rules for different media types/devices, in our case we have only one (when the screen's max width is 768px), but you can set multiple ones if you want/need.

The flex-direction property specifies the direction of the items in the .menu ul. Setting it to column will display them vertically (stacked).

Finally, lets style the links within the li tags:

.menu li a {
    color: #fff;
    letter-spacing: 2px;
    text-decoration: none;
    text-transform: uppercase;
}

.menu li a:hover {
    text-decoration: underline;
}

That's all folks! You now know how to create an attractive responsive navigation with a cool animation!

cool

The entire code is available on Codepen.

If you enjoyed the post, make sure you subscribe below and if you have any questions let me know and I'll get back to you. ^_^

Tagged with html5, css3, animation, flexbox, javascript, hamburger, button