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.
It’s a cool demo of what can be achieved with flex but the submenus as they are are actually unusable since the toggle button is moving when you click on it. Cool anyway :)
I see what you mean about the toggle menu (sometimes) moving when you open a submenu. I’m not sure that makes them “actually unusuable”.
I’m with alxscms – they aren’t unusable – but they are trickier to use. It’s a bit jarring how the page content moves too (if you have scrolled). It’s a really cool demo of what can be done with Flexbox, but not sure about it from a usability point of view.
Great idea! Might put this to use if I come across a project where it may fit in.
Really looking forward to your article on animating to an unknown height. It’s an issue I have hit several times and it’s super annoying.
If I remember correctly in Bootstrap 3, they tackled that using the scrollHeight of the zero-height container. Not css-only solution though.
Technically very nice, but I don’t see how the stretching actually improves the usability. If the menu items are tall enough to hit accurately, they don’t need to be any taller. Maybe they could stretch between a “useable” size and a “better” size? That is, have an upper limit to the stretching.
Thanks for showing us how you’d build something cool that you found on the web! I find that reading articles like these helps make me a better designer and coder. Taking things apart and rebuilding them is the best way to figure out how and why they tick – and every time I read an article like this, I grow! Thank you for finding this cool navigation, and taking the time to show us how to make our own. I think this is AWESOME!
This works well on Chrome, but the shrinking back down does not work on Safari 10.0.3.
I haven’t investigated the reason, but I have found that Safari tends to be a bit snippy about cascading Flexbox—so it might be related to that.
c