CodeinWP CodeinWP

Accessible and Keyboard-Friendly Hamburger Menu + Slide Out Navigation

This week I did some research to try to build a hamburger menu that opens a slide-out navigation panel, a common design pattern nowadays. But I wanted to ensure the whole thing was keyboard-friendly and as accessible as possible.

I’m not 100% sure what I’ve come up with is the most accessible solution, but I did consult a number of decent sources on building accessible navigation menus like these. I also did some rudimentary testing using the free NVDA screen reader, to ensure there are no major problems.

You can view the demo embedded below on CodePen:

See the Pen
Keyboard-friendly and Accessible Hamburger Menu
by Louis Lazaris (@impressivewebs)
on CodePen.

If you want to try a non-CodePen version to do some accessibility testing on it, you can view it via the button below:

Features of the Hamburger Menu

While I did my best to make this as accessible as possible, I’m more than happy to hear any suggestions. Here are the features of the component:

  • It’s keyboard-friendly, so you can use the tab key to navigate around the page including tabbing to the hamburger menu itself
  • Uses the UTF-8 symbol referred to as trigram for heaven: or you can use ☰
  • Uses the multiplication sign UTF-8 symbol (× or ×) for the ‘close’ button inside the navigation panel.
  • The hamburger menu and the close button are wrapped inside a <button> element
  • Each button has an aria-label that defines what each one does
  • The navigation menu is hidden using width: 0 and visibility: hidden to ensure it’s not keyboard accessible when it’s closed
  • When you open the navigation panel, the page focus goes immediately to the close button; when the panel is closed, focus goes back to the hamburger menu
  • The hamburger menu and panel are nested inside a <nav> element, which seems to be the best option for screen reader users
  • When the navigation panel is opened, a semi-transparent overlay covers the main content (added via JavaScript so no extra HTML)
  • The navigation panel can be closed by clicking on the close button, cllicking on the overlay, or by using the ESC key on the keyboard
  • The hamburger menu uses aria-expanded, which changes to true or false via JavaScript when the panel is opened and closed
  • The <nav> element has its aria-labelledby attribute set to the ID of the hamburger button, which helps catalog elements for navigation (credit Birkir for his suggestion in the comments)

Additionally, I’ve added a tabindex value to the heading and paragraphs elements in the demo so you can see more realistically how to tab around. You wouldn’t normally do this with paragraphs and headings, it’s just for the demo.

The only slightly hacky part of the code is the fact that I’m using window.setTimeout() to delay some stuff so that the JavaScript behaviour matches the transition timing that’s used in the CSS. The panel takes 400ms to open or close, so I’m using the same timing in the JavaScript to ensure certain actions occur after the transition has completed.

I’m aware that I could use the transitionend event for this, but I find that event is extremely non-intuitive and it’s just easier to hard-code the timing according to what’s in the CSS. If you can correct this, feel free to fork the CodePen and I’ll add it to the code.

I should also point out that my JavaScript and CSS are quite dependent on the HTML structure, so this wouldn’t necessarily be something you could just drop into your own project without any modifications.

Sources for Building Accessible Navigation

As I mentioned at the start, this isn’t necessarily a perfect solution. Maybe it’s satisfactory, but could use some improvement. For example, another similar menu system I found online forces keyboard navigation to stay inside the navigation panel when it’s open, using JavaScript. This doesn’t seem to be the easiest task, so I didn’t give it much of a try in my script.

Here are some of the sources I consulted for building this (though I didn’t necessarily follow all the recommendations in these sources):

And once again, here are the demo links:

I’d be happy to hear any suggestions on how to improve the accessibility or anything else in the code to help make this a more complete solution.

7 Responses

  1. TenTen71 says:

    I don’t understand why you wouldn’t set it up to allow keyboard users (including the blind) to automatically open the slide out while tabbing around and start the focus at the first link. Why make them have to click the menu icon and then jump to the first link? In my opinion it should work like a Skip Navigation link that is hidden when inactive and visible and in focus when using the keyboard.

    • Ok, thanks for the suggestion. I guess I’m mainly focused on keeping it usable for all users, not just those with a screen reader. Are you saying that all users should see the menu open when the hamburger receives focus? Won’t that annoy most people? So I don’t know if that’s a valid option unless there’s a way to do that only for assistive devices.

  2. Birkir Gunnarsson says:

    This is looking very good. A couple of usability suggestions (from a screen reader user). 1. Change the aria-label from “open navigation menu” to just “navigation menu” or “show navigation menu”. (the aria-expanded attribute tells screen reader user if it is collapsed or expanded, so the aria-label should not have the words open/close in it, those could be confusing. The most beneficial use of the <nav> element is around the list of links (+ the close button), basically marking the content that gets displayed when you click the hamburger menu button. It should have aria-labelledby pointing to the id of the trigger button.

    <button id="snm" aria-label="show navigation menu" aria-expanded="true"> </button>
    <nav aria-labelledby="snm">
    close button
    list of links.

    By doing this you clearly mark the boundries of the content that is displayed when you click the button and associate that content with the button using aria-labelledby. This example doesn’t sound so enthralling with the placeholder label of the hamburger button, (a screen reader will read this as “show navigation menu navigation” but for real world usage where the hamburger menu buttons will have names like “our services” or “accounts” this connection will create a meaningful grouping element for someone using a screen reader.

    • Thank you for the suggestions, especially coming from a screen reader user!

      I’ve made both corrections, and I’ll add something in the notes about it.

  3. Azizul says:

    Not works in Gatsby JS. However thanks for Sharing the Idea.

    If anyone following the content for React JS or Gatsby JS. You can Use className = ” classname” to change the Classname for an Element. Before that you need to declare the class variable:
    let main = document.getElementById(‘id’);
    main.className = ” the wanted class name”;

  4. Thanks for this informative blog, the information provided for the hamburger menu slide-out navigation is helpful for a website but I want to know if I want to get it for Blogspot blog then how I have to proceed.

  5. Alex says:

    Why not use a skip navigation link, keep nav links visible to screen readers (not visually), and visually display the nav whenever one of the links is focused? Clicking the hamburger icon would be invisible from screen readers and keyboard navigation, but the nav would still be accessible by keyboard and screen readers.

    I’m not sure I understand why screen readers or keyboard users need to care about whether the nav menu is expanded or collapsed.

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field and deep URLs are not allowed in the "Website" field. If you use keywords or deep URLs, your comment or URL will be removed. No foul language, please. Thank you for cooperating.

Markdown in use! Use `backticks` for inline code snippets and triple backticks at start and end for code blocks. You can also indent a code block four spaces. And no need to escape HTML, just type it correctly but make sure it's inside code delimeters (backticks or triple backticks).