Line Menu Icon… That Is A Menu

Published by Chris Coyier

I had a dumb little idea the other night so I just coded it up (as you do). You know those little icons that have come represent navigation? We've called them Three Line Menu icons around here, but otherwise known as Navicon (clever) or Hamburger (dumb). The point of that icon is that it looks like a little menu, so hopefully it's obvious you click it to reveal a real menu. But what if that icon wasn't an icon at all, but the actual menu, just shrunken?

The Concept

Yep, it's a little cheezy.

The trick is very easy though. It largely relies on CSS transform and transition, and a few more basics.

The process is essentially:

  1. Design full size view first
  2. Shrink entire thing to icon size with transform: scale(0.X);
  3. Remove unnecessary bits with opacity: 0
  4. When in icon size, only react to clicks on the whole thing.
  5. When clicked, toggle to transform: scale(1), hidden bits to opacity: 1, and restore normal clicking ability.

Here's the design bits:

Changing State

Changing the "state" (expanded or not) of menu you could do in any number of ways. Perhaps the checkbox hack or real easy-like with jQuery:

$(".main-nav").on("click", function() {
  $(this).toggleClass("open");
  return false;
});

All we're doing is changing a class here, which is the way to go for sure.

The CSS

I particularly like Sass when dealing with states because you can nest the states within the same block.

.main-nav {
  /* Animate the state change */
  transition: 0.3s ease;
  transform: 
    /* Make mini */
    scale(0.1) 
    /* Hot performance 10x hack */
    translateZ(0);
  /* The open state */
  &.open {
    transform: scale(1.0);
  }
}

Handling Clicks

The clicking thing is a little funky. We need to make sure that

  • Clicking the icon version simply opens the menu
  • Clicking menu items inside while open work
  • There is a way to close it

We already have the jQuery in place that toggles the class when you click anywhere on the entire element. That can stay just like that. We just need to make sure that if you happen to click exactly one of those those tiny little lines inside the mini icon, you don't actually go to that link.

All those links are in a <ul>, so one way to handle that is to use CSS to make that entire thing un-clickable by default (minified), but clickable when in the open state.

.main-nav {
  ul {
    pointer-events: none;
  }
  &.open {
    ul {
      pointer-events: auto;
    }
  }
}

While pointer-events is awesome, a noticeable gap in support is IE 10 (11 does support it though). You may instead want to use an additional <div> that you use to cover the entire thing when in the default (minified) state and just remove it when in the open state.

Closing It

The way we handle closing is to add a close button:

<button class="close-button">
  <b class="visually-hidden">Close</b>
  <span aria-hidden="true">
    ×
  </span>
</button>

We wanted to use the × character there as essentially an icon. But that has no meaning when read (or if it does, it's "multiply"). So we hide that from being read and include a more readable word that is visually hidden (kicked off page with positioning). Standard procedure for using icons.

In our simple example, we don't even need to bind an event handler directly to this button, because we already have one attached to the entire parent element that toggles that open state. If you click this button, that event will bubble up and fire that event and do the job. That might be weird, so feel free to adjust and bind directly to the element.

Semantics?

While this <button> doesn't exactly make my skin crawl, we could endless debate about it. (example conversation). It's meaningless without JS enabled, so should we insert it with JS? Should we have made it an anchor link with an href back to the top of the page? Is <button> still the correct element?

Progressive Enhancement?

While we're just having fun here, we might consider tackling this via progressive enhancement. That is, start with a menu that is completely functional as markup alone. Then level up all the fancy stuff as support is available.

Fortunately, our markup already mostly in good shape. I do think <button> is the correct element for our close button, since there is no "meaningful href", but it should be inserted with JS since it only has functionality with JS. Very simply, that might look like:

$('<button class="close-button">\
    <b class="visually-hidden">Close</b>\
    <span aria-hidden="true">\
      ×\
    </span>\
  </button>').appendTo(".main-nav");

Another concern is that we approached the CSS in such a way that the default state is with the menu closed. And that "closed" state essentially relies on a CSS transform shrinking it. If CSS transforms aren't supported, the menu will appear open, overlapping content and unable to be closed (bad). If our menu didn't overlap content, we might be OK, but since it does we would be better off only applying our fancy styling should be browser support our fancy CSS needs.

There are countless ways to approach this, but a fairly easy and standard way is to detect support of the relevant technologies via Modernizr. If what we need is there, there will be a class name telling us that on the <html> element, and we can use that to apply the fancy.

.main-nav {
  /* Default, visible, accessible styling */
}
.csstransforms .main-nav {
  /* Bring the fancy */
}

IE 8 doesn't support CSS transforms, so:

None the wiser.

There I go again, turning a dumb little demo into a lesson. Here's the final deal:

See the Pen Line Menu Icon that Expands Into Actual Menu by Chris Coyier (@chriscoyier) on CodePen

Other Ideas?

Perhaps this exact idea is cheezytown, but I do like the idea of the thing you click on being a part of what is revealed. It's kind of like how animations can be used to make an action more clear, having a single point of focus during those animations can also help. For instance:

See the Pen owgLF by Chris Coyier (@chriscoyier) on CodePen