Squeezy Stretchy Flexbox Nav

Avatar of Chris Coyier
Chris Coyier on

I saw an interesting take on off-canvas navigation the other day over on The New Tropic. It wasn’t the off-canvas part so much. It was how the elements within the nav took up space. They stretched out to take up all the space, when available, but never squished too far. Those are concepts that flexbox makes pretty easy to express! Let’s dig in a little.

Here’s the nav, a video showing what I mean:

My favorite part is how there are submenus. When a submenu is toggled open, the same rules apply. If some stretching has happened, the nav items will shrink in height, making room for the submenu. But never shrink too far. If there isn’t room, the menu will just scroll.

Standard Two-Level Nav HTML

Pretty easy to mock out with Emmet:

<nav class="main-nav">
  <ul class="nav-list">
    <li><a href="">Lorem ipsum.</a></li>
    <li>
      <button class="submenu-toggle-button">+</button>
      <a href="">Explicabo, perspiciatis.</a>
      <ul class="submenu nav-list">
        <li><a href="">Lorem ipsum.</a></li>
        <li><a href="">Culpa, qui!</a></li>
        <li><a href="">Repudiandae, eaque.</a></li>
      </ul>
    </li>
    <li><a href="">Sit, dolor.</a></li>
    <li><a href="">Dicta, possimus?</a></li>
    
    <!-- etc -->

  </ul>
</nav>

Flexbox the Column

Let’s make sure that list is as tall as the browser window, which is easy with viewport units. Then make sure each of the list items stretch to fill the space:

.main-nav > ul {
  height: 100vh;
  display: flex;
  flex-direction: column;
}
.main-nav > ul > li {
  flex: 1;
}

We’ve already gotten almost all the way there! Stretching works great, only when there is room, like we want:

Quick Toggles

We have a <button> in place to toggle the submenus (arguably, we should probably place those buttons with JavaScript, since they don’t do anything without). Here’s how they could work. The submenus are hidden by default:

.submenu {
  max-height: 0;
  transition: 0.5s;
  overflow: hidden;
}

We can open it with a class:

.submenu.open {
  max-height: 200px; /* non-ideal magic number */
}

We’re animating to an unknown height here, which is tricky. We hope to have a good article addressing this out soon (there are options).

Toggling classes is plenty easy:

var buttons = document.querySelectorAll('.submenu-toggle-button');

[].forEach.call(buttons, function(button) {
  button.addEventListener('click', function() {
    var submenu = button.parentNode.querySelector('.submenu');
    submenu.classList.toggle('open');
  });
});

That gets those submenus behaving like we want:

Demo

You’ll probably need to pop over to the Pen to play with the vertical stretching stuff.

See the Pen Squeezy Stretchy Nav by Chris Coyier (@chriscoyier) on CodePen.