In Praise of the Unambiguous Click Menu

Avatar of Mark Root-Wiley
Mark Root-Wiley on

Take your JavaScript to the next level at Frontend Masters.

I still remember my excitement when I learned how to build a hover-triggered submenu with just CSS. (It was probably after reading this 2003 article from A List Apart.) At the time, it was a true CSS trick. Seriously. Wild times.

That went a little something like this:

<ul class="my-menu">
  <li>
    <a href="page-a.html">Page A</a>
    <ul>
      <li><a href="page-b.html">Page B</a></li>
      <li><a href="page-c.html">Page C</a></li>
      <li><a href="page-d.html">Page D</a></li>
    </ul>
  </li>
  <!-- etc... -->
</ul>
/* Position submenus relative to parent list item */
.my-menu li {
  position: relative;
}

.my-menu ul {
  /* Hide my submenus by default */
  display: none;
  /* Position submenus, when open */
  position: absolute;
  left: 0;
  top: 100%;
}

/* Look, Ma! No onclick handler! */
.my-menu li:hover > ul {
  display: block;
}

These days, we can improve the accessibility of CSS-only menus with a newer trick! Menus can open and close when navigating them with a keyboard, thanks to :focus-within.

/* No IE11 support */
.my-menu li:focus-within > ul {
  display: block;
}

Try using both your mouse and the TAB key to move through the demo.

But times have changed from when I first learned these tricks, and so have I. Since then, I’ve built a bunch of websites and learned a lot more about usability, accessibility, and content strategy. Now, I find hover-triggered menus lacking on all those fronts. So, a few years ago, I quit building hover-triggered submenus and switched to click-triggered submenus. (From here, I’ll just call them “hover menus” and “click menus.”)

I think you should should stop building hover menus too. I’m here to tell you why.

Hover menus are inconsistent

Take a look at this real menu from a site I built:

Simple enough, right? The arrow icons show us there are submenus for each item except “Home.” But if those submenus appear on hover, there are at least four ways the menu might work, and you’ve probably experienced all four of them.

  1. The top “parent” menu item links to a page and each submenu item links to another page. For the example above, “Services” would be a unique page and so would every link in the “Services” submenu.

But somewhere along the way, a second very common pattern arose.

  1. The parent item has href="#"— or even no href at all 😱and the only functional links are in the submenus. In our example, “Services” is still a link, but nothing happens when you click it.

This inconsistency — is the parent item a link or not? — leads to lots of confusion when I watch people use websites. Some people skip right past helpful top-level pages, assuming those items aren’t links. Yet others assume the top-level links are pages and try to click them.

This leads to the third and fourth not-so-great patterns you’ll encounter. My guess is that these evolved from attempts to compensate for the confusion caused by the first two setups.

  1. The parent item and first submenu item link to the same page. Making matters worse, the parent item and first submenu links having different link text violates a WCAG 2.1 Level AA accessibility standard.
  2. The parent item links to a page containing useless fluff content or only the links in the submenu. The page itself serves no real purpose for anyone visiting it.

These last two configurations waste time for people who do know the parent items are links with redundant or useless content.

Here’s a diagram showing all four possible hover menu setups.

Image of a white menu with four menu items going from left to right. The menu is against a gradient background that goes from a burnt orange to a deep purple horizontally. Each menu item corresponds to one of the usability issues that were described.
When first seeing a hover menu, a visitor can reasonably wonder which of these four ways the menu might work.

Visitors are reasonably confused by hover menus

No matter how we implement hover menus, our visitors can reasonably wonder:

  1. Can I click the parent items?
  2. Will the parent item be a link to the same page as the first submenu link?
  3. Even if the parent item is a unique link, is it worth my time to view?

That leaves us with no good options. It makes it impossible to satisfy Jakob’s law of usability that “users prefer your site to work the same way as all the other sites they already know.” There is no standard implementation when it comes to hover menus, so we need to do something different to provide a consistent user experience.

What about “Split Button” menus?

Probably the least common type of menu I see uses a “split button” design where the parent item is a link and a separate drop-down icon opens and closes the menu. The Twenty Fifteen default WordPress theme uses that pattern. Because it’s so uncommon, I find that visitors often overlook the top-level page link, and research suggests that users don’t perceive a label and icon as being separately clickable.

A “split button” menu item from the Twenty Fifteen WordPress Theme
Until someone hovers or focuses the arrow button, they probably won’t guess it’s independent of the link.

So, what’s the better option? Enter the click-triggered submenu!

Click menus to the rescue

Instead of relying on the hover interaction or some other “creative” (and confusing) solution, let’s build menus where parent items are buttons that show and hide submenus when clicked. This instantly solves the hover menu problem because click menus work unambiguously.

  • Site visitors must click the parent item to view its submenu.
  • All links are contained in submenus except for top-level items that have no submenu (e.g. “Home”). We’ll deal with what happens to those top-level pages in a moment.

When you think about it, click menus are actually what we expect already in most other contexts:

  • Using a touch device? Hover isn’t really a thing there.
  • Using an application menu (e.g. File, Edit, etc.)? Those almost never appear on hover!
  • Using anything other than a mouse? Pressing ENTER or activating a link with any type of switch control is more equivalent to clicking than :focus is equivalent to :hover.

Regardless of your device or input mode, a “click” is a more universal and solid interaction. Let’s use it to make our website menus awesome!

Switching to click menus

My gut feeling says that a lot of sites have recently switched to click menus. Join the party! As more and more sites make the change, people will again develop simple and useful expectations of “how websites work” (thereby satisfying Jakob’s law).

When you first make this change, it’s true that some visitors might still expect hover menus. They may even say they prefer them if you ask. What I can tell you from watching people use click menus, though, is that everyone figures it out quickly and adjusts.

And don’t just take my word for it! The U.S. Web Design System’s (USWDS) navigation patterns use click menus. Here’s what they have to say:

Avoid using hover to expand dropdown lists. Hover is difficult for some users and won’t work on touch screens. Dropdowns should expand on click or with keyboard navigation.

Bootstrap uses click menus, too, for these same reasons:

What it really boils down to is user intent. The purpose of a hover state is to indicate something is clickable (underlined text)… The purpose of a click is to actually do something, to take an explicit action. Opening a dropdown is an explicit action and should only happen on click.

From the same article, there’s this great nugget:

The caret in a dropdown link is the equivalent of underlining a link: it provides some affordance for what will happen when you click this element. Don’t mistake that for providing enough information to pop the dropdown on hover though.

So it’s not like we’re exploring uncharted territory here. And, the UK.gov design system has another good reminder: Maybe you don’t even need submenus at all! Their menus are just a list of links, using on-page grids of links and accordions to help visitors navigate. Heck, you won’t find submenus on CSS-Tricks either!

Click menus come with bonus benefits!

The more you work with click menus, the more benefits you discover:

  • You decide whether you need a category/overview/landing page… or not! Instead of forcing content to match a menu’s structure with links that are parents of other links, your content strategy and information architecture dictates what types of pages you need and how they are labeled. If an overview of your services is helpful for your visitors, put “Services Overview” or “All Services” as the first item in your “Services” submenu.
  • Submenus stay open until they are closed. Hover menus have a nasty way of disappearing when people bump their cursors or even just try to click a submenu link. This is especially the case for submenus that “fly out” rather than below the parent item. The persistence of click menus makes for a more “solid” experience so users trust the interface and don’t get frustrated.
  • The persistent submenu behavior is even more crucial for megamenus. When visitors need more time to take in the submenu contents, they can’t afford to have the menu close unexpectedly.
  • The JavaScript is the same for “mobile” and “desktop” menus. Whether the menu is hidden behind a hamburger or visible on mobile, the interaction is always the same. I only need to change my CSS to make a responsive click menu.

Building click menus

When I set out to build my own accessible click menu script, I found there wasn’t a single standard for how to do it. My own thoughts and code were most heavily influenced by:

The key takeaways from my research on implementation:

  • The element you click to show the submenu should be a <button> since it doesn’t link to a page.
  • Use aria-expanded (on the <button>!) to communicate the submenu’s open and closed states.
  • Use display: none or visibility: hidden so that keyboard users can’t get to submenus when they are closed.
  • aria-controls is poop, but you might as well add it.
  • Do not use role="menu" (and the whole ARIA menu pattern) or aria-haspopup. Those feel related but they aren’t for building navigation menus.
  • Close an open submenu when:
    • Another submenu opens
    • The user clicks outside the menu
    • The user presses the ESC key when focus is inside an open submenu. (Not all users expect this, but I think it’s a nice touch.)

Since click menus require JavaScript, we should consider how this menu can be progressively enhanced in case JavaScript fails for any reason. Our classic hover CSS trick is still good for something after all!

I start building my click menu as a CSS-only hover menu that uses li:hover > ul and li:focus-within > ul to show the submenus. Then, I use JavaScript to create the <button> elements, set the aria attributes, and add the event handlers. This means the menu still mostly works without JavaScript and plays nicely with link-only menus built in WordPress, my CMS of choice.

You can check out the script I use below, but I’ll be the first to admit there are probably better ones. What’s important is that you test it with real users… and stop using hover menus. 😃