Enable
>

Accessible Flyout/Hamburger Hybrid Menus

This is the best solution to use, especially when building from scratch.
If you are trying to fix an existing menu system, please go through the the code walkthrough of how this was implemented.
This solution described below is available as an NPM module. (Module installation instructions)

This is the component that the most development and testing time was spent on. On many sites I have done accessibility audits on, there is a main navigation that appears as a traditional flyout menu on the desktop breakpoint, and a mobile hamburger menu on the tablet and mobile breakpoints. More often than not, this component would have several accessibility issues:

  1. On desktop, when the user opened a menu flyout and tabbed through the flyout to the next flyout button, the flyout wouldn't close.
  2. On mobile, there wouldn't be a focus loop around the hamburger menu when opened.
  3. On mobile, when opening up a submenu, focus wouldn't go the the back button/close button of the new submenu.
  4. Links and collapsable buttons were not marked up correctly.

I created this menu system to address all of the above issues. I have tested with both mobile and desktop devices with and without screen readers. The visual layout of the mobile breakpoint is inspired by this hamburger menu by the talented Hayley Tong, the code running it is original work.

Mobile Hamburger Menu

If you are in the mobile breakpoint (i.e. a viewport width less than ), then a hamburger menu icon will appear in the upper right-hand corner of this page.

Screenshot of the banner on the top of this page in the mobile breakpoint
Figure 1. The hamburger menu icon appears on the upper right hand side of the page. It is denoted by three horizontal lines that has become the standard.

Clicking on it with either a mouse or keyboard will result in a standard hamburger menu appearing on the right hand side of the page, and keyboard focus will be applied to the first interactive element inside it (the close button).

Screenshot of the hamburger menu when opened.
Figure 2. When the hamburger menu icon is clicked, the black menu above appears. It has a close button (that gains keyboard focus when first opened) and few CTAs stacked on top of each other.

The user can choose any item inside that menu with either a mouse or keyboard. Menu subcategories are visually indicated by a right pointing chevron, and to assistive technologies as collapsible/expandable buttons. Clicking on these subcategory buttons will show the subcategory menu appearing, with keyboard focus being applied to the back button that will take users back to the previous menu.

Keyboard users experiencing a focus loop that keeps the current menu panel until the menu is closed. If the user either uses a mouse to click outside the menu or hits the Escape key, the menu will close.

Desktop Mega Menu

If you are in the desktop breakpoint (i.e. a viewport width greater then or equal to ), then a mega menu will appear in across to top of the page underneath the Enable logo in the global header.

Screenshot of the mega menu when the page is first loaded.
Figure 3. The mega menu is a horizontal bar with the top level CTAs appearing inside it next to one another.

Users can either click the CTAs in the menu with either a mouse or keyboard. Menu subcategories are visually indicated by a downwards pointing chevron, and to assistive technologies as collapsible/expandable buttons. Clicking on these subcategory buttons will show the subcategory menu appearing below the button. Keyboard users can then tab immediately into the subcategory menu with a keyboard, while mouse users can click on any of the submenu items inside.

Screenshot of the mega menu when one of the submenus opened.
Figure 3. When a submenu category is clicked with either, a mouse or keyboard, the submenu will appear. Clicking again makes it disappear.

Keyboard users will note that when they apply focus to an interactive element outside of the subcategory menu, the menu will close automatically. Mouse users will notice this happening if they click anywhere outside the subcategory menu as well. Mobile screen reader users will experience a focus loop inside the menu until they close the menu with the CTA that opened it.

How can I use this script in my own site

  1. Follow the instructions below in order to learn how to download the hamburger menu library.
  2. You can either code the HTML for the menu by hand, but since the code can be rather redundant and repetitive, it may be easier to use a JSON to structure your menu and have the library do it for you.

Coding HTML by Hand

If you wanted to code this menu's HTML by hand, you can use the following guide. Just keep in mind that each level is basically a list of buttons and links. The links will go to new pages; the buttons will open up a new section.

☜ Scroll to read full source ☞

                    
                

Using JavaScript (recommended)

If you look at the four steps given above, they are basically HTML templates. You could then take those HTML templates and modify them to work with favourite HTML templating system (e.g. Handlebars, Moustache, React, etc).

So ... What Makes This Accessible

Let's walk through the front-end code of the Hamburger Flyout Menu on the Enable site to show the code that makes this accessible.

Code Walkthrough of the Above Example

Below is the HTML of the above example. Use the dropdown to highlight each of the individual steps that makes the example accessible.

☜ Scroll to read full source ☞

                    
                

Installation Instructions

You can load this JavaScript library into your application in serveral ways:

If you haven't done so already, choosing which you should use is obviously a major architectural decision. Here are a few articles that will help you decide:

Important Note On The CSS Classes Used In This Module:

This module requires specific CSS class names to be used in order it to work correctly. These CSS classes begin with enable-flyout__. Please see the documentation above to see where these CSS classes are inserted.

Using NPM/Webpack to load ES6 Modules:

  1. Install the enable-a11y NPM project.
  2. Edit your webpack.config.json file to resolve the ~ modifier by adding the following:
    ☜ Scroll to read full source ☞
    module.exports = { ... resolve: { extensions: ['.js', '.jsx', '.scss', '.css', '*.html'], modules: [ path.resolve('./src/js'), path.resolve('./node_modules') ], alias: { '~enable-a11y': path.resolve(__dirname, 'node_modules/enable-a11y') }, ... }, ... }
  3. You can use the module like this:
    ☜ Scroll to read full source ☞
    // import the JS module import enableHamburger from '~enable-a11y/js/modules/enable-hamburger'; // import the CSS for the module import '~enable-a11y/css/enable-hamburger'; // How to initialize the enableHamburger library enableHamburger.init();
  4. Alternatively, if you are using LESS you can include the styles in your project's CSS using:
    ☜ Scroll to read full source ☞
    @import '~enable-a11y/css/enable-hamburger';
    (If you are using it in your CSS, you will have to add the .css suffix)

Using NPM/Webpack to Load Modules Using CommonJS Syntax

  1. Install the enable-a11y NPM project.
  2. You can import the module using require like this:
    ☜ Scroll to read full source ☞
    var enableHamburger = require('enable-a11y/enable-hamburger').default; ... enableHamburger.init();
  3. You will have to include the CSS as well in your project's CSS using:
    ☜ Scroll to read full source ☞
    @import '~enable-a11y/css/enable-hamburger';

Using ES6 modules natively.

This is the method that this page you are reading now loads the scripts.

  1. Grab the source by either using NPM, grabbing a ZIP file or cloning the enable source code from github.
  2. If you want to load the module as a native ES6 module, copy js/modules/enable-hamburger.js , and css/enable-hamburger.css from the repo and put them in the appropriate directories in your project (all JS files must be in the same directory).
  3. Load the CSS in the head of you document:
    ☜ Scroll to read full source ☞
    <html> <head> ... <link rel="stylesheet" href="path-to/css/enable-hamburger.css" > ... </head> <body> ... </body> </html>
  4. Load your scripts using the follwing code (NOTE: you must use <script type="module">):
    ☜ Scroll to read full source ☞
    <script type="module"> import enableHamburger from "path-to/enable-hamburger.js" enableHamburger.init(); </script>

Using ES4

Just do the same as the ES6 method, except you should get the JavaScript files from the js/modules/es4 directory instead of the js/modules/:
☜ Scroll to read full source ☞
<script src="path-to/es4/enable-hamburger.js"></script>