Building a Morphing Hamburger Menu with CSS

Draft updated on Invalid Date

Luis Manuel

Building a Morphing Hamburger Menu with CSS

This tutorial is out of date and no longer maintained.


Recently I found this awesome dribbble shot by Vitaly Rubtsov, and I could not resist the urge to try to implement it.

In this tutorial, I’ll explain the entire process of how to do it with CSS only, without using a single line of JavaScript. So, we will be seeing some CSS (and SCSS) tricks that will allow us to achieve an animation almost as smooth as the animated gif.

Here’s the CodePen of what we’ll build:



Let’s start with the HTML structure we’ll be using. See the comments for a better understanding.

<div class="container">
    <!-- This checkbox will give us the toggle behavior, it will be hidden, but functional -->
    <input id="toggle" type="checkbox">

    <!-- IMPORTANT: Any element that we want to modify when the checkbox state changes go here, being "sibling" of the checkbox element -->

    <!-- This label is tied to the checkbox, and it will contain the toggle "buttons" -->
    <label class="toggle-container" for="toggle">
        <!-- If menu is open, it will be the "X" icon, otherwise just a clickable area behind the hamburger menu icon -->
        <span class="button button-toggle"></span>

    <!-- The nav menu -->
    <nav class="nav">
        <a class="nav-item" href="">Dashboard</a>
        <a class="nav-item" href="">History</a>
        <a class="nav-item" href="">Statistics</a>
        <a class="nav-item" href="">Settings</a>

Initial styles

Now let’s add some basic styles to start getting the look and feel we want. The code is pretty straightforward.

/* Basic styles */

* {
  box-sizing: border-box;

html, body {
  margin: 0;

body {
  font-family: sans-serif;
  background-color: #F6C390;

a {
  text-decoration: none;

.container {
  position: relative;
  margin: 35px auto 0;
  width: 300px;
  height: 534px;
  background-color: #533557;
  overflow: hidden;

Toggle functionality

Before you start building the rest of the interface, let’s add the toggle functionality to easily change from one state to another.

The HTML we need is already in place. And the style to make it works could be something like this:

// To hide the checkbox
#toggle {
  display: none;

// Styles for the 'open' state, if the checkbox is checked
#toggle:checked {
  // Any element you need to change the style if the menu is open goes here, using the sibling selector (~) as follows

  // Styles for the open navigation menu, for example
  & ~ .nav {

Building the closed state

To build the closed state, we need to transform the menu items into lines to make up the hamburger menu icon. We can come up with several ways to accomplish this transformation. We have decided to do it this way:

And this is the code to get it working:

$transition-duration: 0.5s;

// Showing nav items as lines, making up the hamburger menu icon
.nav-item {
  position: relative;
  display: inline-block;
  float: left;
  clear: both;
  color: transparent;
  font-size: 14px;
  letter-spacing: -6.2px;
  height: 7px;
  line-height: 7px;
  text-transform: uppercase;
  white-space: nowrap;
  transform: scaleY(0.2);
  transition: $transition-duration, opacity 1s;

  // Adjusting width for the first line
  &:nth-child(1) {
    letter-spacing: -8px;

  // Adjusting width for the second line
  &:nth-child(2) {
    letter-spacing: -7px;

  // Adjusting from the fourth element onwards
  &:nth-child(n + 4) {
    letter-spacing: -8px;
    margin-top: -7px;
    opacity: 0;

  // Getting the lines for the hamburger menu icon
  &:before {
    position: absolute;
    content: '';
    top: 50%;
    left: 0;
    width: 100%;
    height: 2px;
    background-color: #EC7263;
    transform: translateY(-50%) scaleY(5);
    transition: $transition-duration;

Note that here we have posted only the main styles for the nav items, which is the most important part. You can find the full code on GitHub.

Building the open menu

To build the open menu we need to restore the nav items from lines to normal text links, among other minor changes. Let’s see how to do it:

$transition-duration: 0.5s;

#toggle:checked {

  // Open nav
  & ~ .nav {

    // Restoring nav items from "lines" in the menu icon
    .nav-item {
      color: #EC7263;
      letter-spacing: 0;
      height: 40px;
      line-height: 40px;
      margin-top: 0;
      opacity: 1;
      transform: scaleY(1);
      transition: $transition-duration, opacity 0.1s;

      // Hiding the lines
      &:before {
        opacity: 0;

The magic is in the details

If we look closer at the gif, we can see that all the menu items do not move at the same time, but in a staggered way. We can do that in CSS too! Basically, we need to select each element (using :nth-child) and set a progressively increased transition-delay. But that is certainly repetitive work. And, what if we have more items? Don’t worry, we can do it better with a bit of SCSS magic:

$items: 4;
$transition-delay: 0.05s;

.nav-item {

  // Setting delays for the nav items in close transition
  @for $i from 1 through $items {
    &:nth-child(#{$i}) {
      $delay: ($i - 1) * $transition-delay;
      transition-delay: $delay;
      &:before {
        transition-delay: $delay;

Here we are using a loop, a variable interpolation, and some basic arithmetic operations. You can learn more about these and more awesome features in the Sass documentation site.

Please note that with this code we’ll get the desired staggered behavior for the close animation. We need to calculate the $delay slightly different for the open animation, to get an inversely staggered transition. Just like this:

$delay: ($items - $i) * $transition-delay;


And we are done with our fancy menu! We have also included some dummy elements, like in the animated gif, and you can see the final demo here.

So far, we have built a cool and functional menu just with CSS. However, if you are not in favor of using the CSS toggle system, it can be perfectly replaced by a few lines of JavaScript without too much effort.

You can find the full code on the GitHub repository, and also play with it directly on CodePen.

We hope this tutorial has been to your liking and you have found it useful!

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Luis Manuel


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
Leave a comment

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel