Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Dean Conway
Dean Conway
3,301 Points

Animated Hamburger Menu

Hi

I have been working on some responsive navigation that collapses into a stacked menu at smaller mobile sizes and displays as a list of links at larger resolutions like laptops and desktops. To achieve this technique I used a combination of flexbox and the checkbox css trick to get this to work without using javascript - up until this point.

Now what I would like to work is what would I need to change or add to both my html and css code to get the hamburger menu icon to animate into a spinner x and then back again into a hamburger icon?

Below is my html and css code so far:

    <div class="navBar">
      <div class="container-fluid">

        <nav class="nav-wrap">

            <div class="site-logo">
                <a href="../index.html">
                    <img src="img/logo-main.jpg" alt="Bassdrop Logo">
                </a>
            </div>

            <input type="checkbox" id="menu-toggle" />
            <label for="menu-toggle" class="label-toggle">&#9776;</label>

            <div class="main-menu">

                <a href="../_news/index.html">News</a>
                <a href="../_reviews/index.html">Reviews</a>
                <a href="../_events/index.html">Events</a>
                <a href="../_about/index.html">About</a>
                <a href="../_contact/index.html">Contact</a>

            </div>



        </nav>
        </div>
    </div>
.navBar{
    background-color: #000;
/*
    position: fixed;
    width: 100%;
*/

}

/*these values match what the container-fluid and row are set to*/
.nav-wrap {
    display: flex;
    justify-content: space-between;
    padding: 2% 0;
    align-items: center;
}

.site-logo {
    width: 165px;
    height: auto;
    display: inline-block;
}


#menu-toggle {display: none;}
.label-toggle {display: none;}


.main-menu {
    display: flex;
    justify-content: space-around;
    width: 50%;
}

.main-menu a {
    color: #fff;
    text-transform: uppercase;
    transition: all .5s ease;
}

.main-menu a:hover {
    color: orange;
}


/*Media Query Changes*/

@media only screen and (max-width: 768px) {

    .nav-wrap {
    flex-wrap: wrap;
    }

    .label-toggle {
        display: block;
        color: #fff;
        font-size: 30px;
        cursor: pointer;
    }

    .main-menu {
        display: block;
        height: 0;
        visibility: hidden;
        opacity: 0;
        text-align: center;
        width: 100%;
        transition: all 1s ease;
    }


    .main-menu a {

        display: block;
        font-size: 1.5em;
        padding: 1em 0;
    }


    #menu-toggle:checked ~ .main-menu {
        opacity: 1;
        height: 100vh;
        visibility: visible;
        margin-top: 100px;
    }
}

2 Answers

David McNeill
David McNeill
44,438 Points

Hey Dean,

So I had a look at your code and thought I'd send you over a solution that I use a lot.

I took out the trigram HTML entity you were using, since it's static and could not be animated in the way you wanted. The best you could do there is to swap it out for the × entity, which makes an 'X'. However, this method wouldn't allow for any animation in between and could be tricky without using JS.

So the solution I propose is to take out the HTML entity and replace it with an empty span element, which we will combine with pseudo elements to create the bars of the hamburger icon. If you look at the CSS, we can add the main bar using a pseudo element to create a line, then make identical lines above and below it using :before and :after pseudo elements positioned absolutely.

Having these 3 separate elements allows us to do some cool things when the input checkbox is :checked. We can make the central bar transparent, then rotate and shift the top and bottom bars using transforms to form an 'X'. Adding a transition to the elements allows us to make this happen gradually, creating the desired animation. If you look around the web, you'll see a huge range of ways to animate these bars!

Here is the HTML code:

<div class="navBar">
      <div class="container-fluid">

        <nav class="nav-wrap">

            <div class="site-logo">
                <a href="../index.html">
                    <img src="img/logo-main.jpg" alt="Bassdrop Logo">
                </a>
            </div>

            <input type="checkbox" id="menu-toggle" />
            <label for="menu-toggle" class="label-toggle"><span></span></label>

            <div class="main-menu">

                <a href="../_news/index.html">News</a>
                <a href="../_reviews/index.html">Reviews</a>
                <a href="../_events/index.html">Events</a>
                <a href="../_about/index.html">About</a>
                <a href="../_contact/index.html">Contact</a>

            </div>



        </nav>
        </div>
    </div>

and the styling:

.navBar{
    background-color: #000;
/*
    position: fixed;
    width: 100%;
*/

}

/*these values match what the container-fluid and row are set to*/
.nav-wrap {
    display: flex;
    justify-content: space-between;
    padding: 2% 0;
    align-items: center;
}

.site-logo {
    width: 165px;
    height: auto;
    display: inline-block;
}


#menu-toggle {display: none;}
.label-toggle {display: none;}


.main-menu {
    display: flex;
    justify-content: space-around;
    width: 50%;
}

.main-menu a {
    color: #fff;
    text-transform: uppercase;
    transition: all .5s ease;
}

.main-menu a:hover {
    color: orange;
}


/*Media Query Changes*/

@media only screen and (max-width: 768px) {

    .nav-wrap {
    flex-wrap: wrap;
    }

    .label-toggle {
        display: block;
        cursor: pointer;
    }

    .label-toggle span {
      position: relative;
    }

    .label-toggle span:before,
    .label-toggle span:after {
      position: absolute;
    }

    .label-toggle span,
    .label-toggle span:before,
    .label-toggle span:after {
          cursor: pointer;
          height: 3px;
          width: 30px;
          background: #fff;
          display: block;
          content: '';
          transition: all 0.4s;
      }

    .label-toggle span:before {
        top: -8px;
    }

    .label-toggle span:after {
        bottom: -8px;
    }

    #menu-toggle:checked ~ .label-toggle span {
      background-color: transparent;
    }

    #menu-toggle:checked ~ .label-toggle span:before,
    #menu-toggle:checked ~ .label-toggle span:after {
      top: 0;
    }

    #menu-toggle:checked ~ .label-toggle span:before {
      transform: rotate(45deg);
    }

    #menu-toggle:checked ~ .label-toggle span:after {
      transform: translateY(-6px) rotate(-45deg);
      top: 6px;
    }

    .main-menu {
        display: block;
        height: 0;
        visibility: hidden;
        opacity: 0;
        text-align: center;
        width: 100%;
        transition: all 1s ease;
    }


    .main-menu a {
        display: block;
        font-size: 1.5em;
        padding: 1em 0;
    }


    #menu-toggle:checked ~ .main-menu {
        opacity: 1;
        height: 100vh;
        visibility: visible;
        margin-top: 100px;
    }
}

Have a look at it in action with this CodePen!

Hope that helps!

Dean Conway
Dean Conway
3,301 Points

Hey David!

Thanks so much for putting this together for me!! I know this feature is common on quite a few websites but I still think it looks really cool and I'm really happy with how this has turned out - small things please small minds ha!! Plus I now have my very own version of it now which i plan to use on this project and I will be dropping this feature into my new portfolio website as well.

The only thing I noticed though was that the area in and around the icons was not entirely clickable, the pointer icon flickers a bit between the bars in the hamburger and the gaps in the X icon, despite seeing you have set the label-toggle class to block - any ideas?

Is it ok to add in some margin or padding to push the icon away from the right edge when using the flexbox model of spacing things out or will that screw with the flexbox spacing?

Also, is there a way to put code pen in responsive mode so I can test at the different media query break points but to keep the code windows full size along the top?