Creating Accessible Menus and Mega Menus
Feb 9, 2018
Introduction
The meaning of the word “menu” within web technologies is variable and encompasses many different types of functionality.
A navigational menu:
- Is often used within headers or footers,
- Provides condensed site navigation functionality, and
- Can be dynamically expanded using direct interaction, or as part of automatic responsive design layouts.
An application type menu:
- Is often used to perform specific actions within the same page, and
- Often includes nested submenus, in the same manner as native popup or context menus that appear when right clicking the page.
More complex menus:
- Represent expanded behaviors within complex web pages, and
- Can include additional interactive controls (e.g., embedded form fields), supplementary textual content (e.g., headings and other active element types), and many other features.
As such, the concept of a menu is flexible, and entirely dependent on the environment where it is applied.
The topic of making dynamic menus accessible within web technologies has been brought up repeatedly over the years, and many conflicting resources claim to have solved this in differing ways. Some address historical accessibility issues with screen reader support. Others concentrate on bleeding edge solutions that have little support in practice by the general public. The number of documented variations is often a source of great confusion for mainstream developers who cannot easily identify which category these widget constructs fall into—or how well they are supported.
This guide will discuss the two primary methods for addressing the accessibility of menus, including the ARIA Menu paradigm and the native active element approach more broadly discussed in the WAI Menu Tutorial.
Generic Menu Features
Though visual styling differs, menus typically fit into two general categories, simple or complex.
Simple menus are also referred to as popup or context menus. The user activates a simple menu via a link, button, or other action (e.g., right-click, mouseover). A simple menu may include a limited number of submenus that tunnel down into sub-features and related options.
Complex menus are often referred to as menubars or megamenus. They come in infinite variations and can include many levels of nested submenus, vertical and horizontal layouts, partially-opened states, interactive form fields, and static constructs such as headings, text, and images. In addition, they can be responsive and change based on the user’s device or size of their display.
It’s difficult to define a best practice for menu accessibility because there is no single design pattern that applies to all menus equally. If a menu structure includes additional active element types or supplemental markup structures that are not supported within an ARIA Menu widget construct, then ARIA Menu markup cannot be used to represent the menu without impairing accessibility.
Natively Accessible Menus
No matter how a menu is visually styled, it will always behave in accordance with its markup. For example, if the triggering element is a standard link and each rendered menu item is a native link, these active elements are already keyboard accessible.
The notion that all menus that are visually styled to look like menus require ARIA Menu markup to ensure accessibility is entirely false. For a non-sighted screen reader user, the visual styling of a particular group of elements is completely irrelevant. There are only two primary requirements to ensure accessibility for non-sighted screen reader users:
- All active elements are keyboard accessible, and
- All focusable active elements include accessible roles, states, names, and descriptions.
There are many ways to accomplish those two requirements. The accessible role of an element does not have to always match its visual styling, especially when the use of specific ARIA roles will change the interactive behavior of screen readers. This is particularly true for menus, where the use of ARIA Menu roles will change the browse modality of screen readers.
Native Menu Syntax
- Heading with Level Start
- Native Button with Expandable State (via aria-expanded)
- Heading with Level End
- Expandable Named Region Start (via role=”region” plus aria-labelledby or aria-label)
- List of Native Links
- Expandable Named Region End
This syntax is both scalable and accessible across all devices, browsers, and platforms. It is fully configurable using the onClick handler upon the native buttons and links. The heading level indicates the structural level of the menu. When the button is activated, aria-expanded is toggled to reflect the displayed state of the associated named region, which is controlled by toggling the CSS display property between ‘none’ or ‘block’. Nested submenus can be added using the same syntax by incrementing the heading level and placing the same structure within the appropriate list item. CSS styling can be used to make the construct visually appear as a menu as expected.
Since all of the active elements are native and focusable, the horizontal or vertical display (as conveyed for sighted users) is completely irrelevant and has no impact on accessibility. Moreover, the addition of supplemental markup such as static text and editable form fields, can be added to this basic syntax without negatively affecting accessibility, because none of these implementations conflict with each other.
Native Menu Markup
<nav aria-label=”main menu”>
<ul class=”horizontal” style=”list-style: none;”>
<li><div role=”heading” aria-level=”2″>
<button aria-expanded=”true” id=”supportBtnId”> Support </button>
</div>
<div role=”region” aria-labelledby=”supportBtnId” class=”submenu”>
<ul class=”vertical” style=”list-style: none;”>
<li><div class=”external”>
<a href=”url1.htm” title=”Read our frequently asked questions”> FAQ </a>
</div>
</li>
<li><div class=”external”>
<a href=”url2.htm” title=”Download drivers and applications”> Drivers </a>
</div>
</li>
<li><div role=”heading” aria-level=”3″>
<button aria-expanded=”true” id=”knowledgeBtnId” title=”Search for How To articles”> Knowledge Base </button>
</div>
<div role=”region” aria-labelledby=”knowledgeBtnId” class=”submenu”>
<ul class=”vertical” style=”list-style: none;”>
<li><div class=”external”>
<a href=”url3.htm” title=”Chat with one of our specialists”> Live Chat </a>
</div>
</li>
<li><form action=”url4.htm”>
<label for=”searchTermId”> Ask Question</label>
<input type=”text” id=”searchTermId” />
<button type=”submit”> Find Answer </button>
</form>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
<li><div role=”heading” aria-level=”2″>
<button aria-expanded=”false” id=”aboutBtnId”> About Us </button>
</div>
<div role=”region” aria-labelledby=”aboutBtnId” class=”submenu” style=”display: none;”>
<ul class=”vertical” style=”list-style: none;”>
<li><div class=”external”>
<a href=”url5.htm” title=”Learn more about our team members”> About Us </a>
</div>
</li>
<li><div class=”external”>
<a href=”url6.htm” title=”Read our mission statement”> Mission </a>
</div>
</li>
</ul>
</div>
</li>
</ul>
</nav>
ARIA Menus
In contrast, the use of ARIA Menu markup involves the addition of role=”menu” or role=”menubar” upon a grouping container element, which must include child active elements that include role=”menuitem”, role=”menuitemcheckbox”, or role=”menuitemradio”. It may seem logical that these attributes can easily be applied to the prior menu structure, however, doing so:
- significantly changes the manner of interaction for screen reader users;
- destroys the relevance of embedded roles such as levelled headings and named regions, and
- if misapplied on the wrong elements, will completely destroy the accessibility of the menu.
ARIA Menu constructs have explicit rules that must be followed in order to ensure that they map correctly in the accessibility tree. When these rules are broken, the accessibility tree is broken, and thus the accessibility of the widget is broken for screen reader users.
- The triggering element that opens a Menu or Menubar, must include aria-haspopup=”true” or aria-haspopup=”menu”.
- The Menu or Menubar role must be used to group all associated child roles.
- All child roles of Menuitem, Menuitemcheckbox, or Menuitemradio must be focusable or referenced directly via aria-activedescendant from the focusable Menu or Menubar role.
- No other focusable active elements besides those containing the roles Menu, Menubar, Menuitem, Menuitemcheckbox, or Menuitemradio, are allowed within the construct.
- The Menu or Menubar structure must have only one tab stop, by allowing focus to one of the valid ARIA Menu roles as a starting point.
- If focus is set upon the Menu or Menubar role, aria-activedescendant must be used to simulate focus movement between the child ARIA Menu roles when pressing the arrow keys.
- If instead, focus is set on one of the child ARIA Menu roles, then programmatic focus must be moved between each Menuitem, Menuitemcheckbox, or Menuitemradio role when pressing the arrow keys.
- When a Menu or Menubar is rendered, focus must be moved into the menu.
- When a Menu or Menubar is closed, focus must be moved back to the triggering element.
When implemented correctly by following all of the focus management and role usage requirements, an ARIA Menu widget acts and sounds like a native popup or context menu when using a screen reader. However, the syntax for implementing an accessible ARIA Menu has very little margin for error.
Simple ARIA Menu Syntax
- Button Role with Attached Menu (via aria-haspopup=”true” or aria-haspopup=”menu”)
- Dynamically Rendered Menu Start (via role=”menu” or role=”menubar”)
- List of Menu Items (via role=”menuitem”, role=”menuitemcheckbox”, or role=”menuitemradio”)
- Dynamically Rendered Menu End
ARIA Menu Demos
- Simple Context Menu from Menu Button
- Simple Right Click Context Menu from Simulated Editor
Visual ARIA can be used to visually observe the ARIA attributes used in these examples when rendered.
Although this ARIA Menu syntax is also scalable, there are strict requirements that must be observed to ensure accessibility for screen reader users.
When the menu is activated, the associated menu container element is controlled by setting the CSS display property between ‘none’ or ‘block’ to toggle visibility, and focus must be moved into the menu when rendered.
Nested submenus can be added using the same syntax by placing the same structure within the appropriate list item. However, when adding a nested submenu, the triggering element that includes aria-haspopup must include role=”menuitem”, and not a button role. Focus management is critical and must be strictly observed.
The entire widget construct, starting with the top-level Menu or Menubar role, must have only one tab stop.
The only focusable elements that are allowed within such a widget are elements that include:
- role=”menu”
- role=”menubar”
- role=”menuitem”
- role=”menuitemcheckbox”
- role=”menuitemradio”
If the element that receives focus includes role=”menu” or role=”menubar”, then aria-activedescendant must be used on the same element to reference the ID of each role=”menuitem”, role=”menuitemcheckbox”, or role=”menuitemradio” element within the menu when using the keyboard to move between each menu item.
If instead, focus is set to an element with role=”menuitem”, role=”menuitemcheckbox”, or role=”menuitemradio”, then aria-activedescendant must not be used, and focus must be programmatically moved between each of these elements when using the keyboard to navigate. Since the arrow keys control movement between each Menuitem, the horizontal or vertical display must match the keyboard paradigm for that layout. Left and Right must move focus between Menuitems within a horizontal menu, as opposed to Up and Down, which must move focus between Menuitems in a vertical menu.
As you can see, the requirements for using ARIA Menu attributes are much more restrictive. Unlike the prior native menu syntax, any addition of embedded supplemental markup, such as headings, static text, nested active elements, or the inclusion of any text input field with or without a Menuitem role, will decrease, or in some cases, even critically impair accessibility for screen reader users.
ARIA Menu Markup
<nav aria-label=”main menu”>
<ul role=”menubar” class=”horizontal” style=”list-style: none;”>
<li role=”presentation”><div role=”presentation”>
<button role=”menuitem” tabindex=”-1″ aria-haspopup=”true” aria-expanded=”true” id=”supportBtnId”> Support </button>
</div>
<div class=”submenu” role=”presentation”>
<ul role=”menu” class=”vertical” style=”list-style: none;”>
<li role=”presentation”><div class=”external” role=”presentation”>
<a role=”menuitem” tabindex=”-1″ href=”url1.htm” title=”Read our frequently asked questions”> FAQ </a>
</div>
</li>
<li role=”presentation”><div class=”external” role=”presentation”>
<a role=”menuitem” tabindex=”-1″ href=”url2.htm” title=”Download drivers and applications”> Drivers </a>
</div>
</li>
<li role=”presentation”><div role=”presentation”>
<button role=”menuitem” tabindex=”-1″ aria-haspopup=”true” aria-expanded=”true” id=”knowledgeBtnId” title=”Search for How To articles”> Knowledge Base </button>
</div>
<div class=”submenu” role=”presentation”>
<ul role=”menu” class=”vertical” style=”list-style: none;”>
<li role=”presentation”><div class=”external” role=”presentation”>
<a role=”menuitem” tabindex=”0″ href=”url3.htm” title=”Chat with one of our specialists”> Live Chat </a>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
<li role=”presentation”><div role=”presentation”>
<button role=”menuitem” tabindex=”-1″ aria-haspopup=”true” aria-expanded=”false” id=”aboutBtnId”> About Us </button>
</div>
<div class=”submenu” style=”display: none;” role=”presentation”>
<ul role=”menu” class=”vertical” style=”list-style: none;”>
<li role=”presentation”><div class=”external” role=”presentation”>
<a role=”menuitem” tabindex=”-1″ href=”url4.htm” title=”Learn more about our team members”> About Us </a>
</div>
</li>
<li role=”presentation”><div class=”external” role=”presentation”>
<a role=”menuitem” tabindex=”-1″ href=”url5.htm” title=”Read our mission statement”> Mission </a>
</div>
</li>
</ul>
</div>
</li>
</ul>
</nav>
This is marked up in strict accordance with the ARIA Menubar and ARIA Menu paradigms.
Notice the use of role=”presentation”. This is not actually required according to the ARIA specification for the use of ARIA Menu roles, however if aria-posinset and aria-setsize are not included as used in the ARIA Menu Demos section implementations, then mainstream browsers cannot currently calculate the “X of Y” positioning properties accurately if the Menuitem nodes are not first level children of their Menu or Menubar container in the accessibility tree. The same issue occurs on ARIA Tablist, ARIA Radiogroup, ARIA Listbox, ARIA Grid, ARIA Tree, and ARIA Treegrid implementations.
ARIA Menu Pitfalls
There are many accessibility issues associated with the use of ARIA Menu widgets at present, some of which are limitations in accessibility tree support, others in screen reader support, and others relating to expectation conflicts when dealing with complex functionality. The most common of these are listed below.
Orientation is still not reliably conveyed by screen readers within ARIA Menus.
Though aria-orientation is a supported attribute for role=”menu” and role=”menubar”, the horizontal or vertical layout of an ARIA Menu is not reliably conveyed, making it impossible for non-sighted screen reader users to intuitively identify when pressing Left/Right or Up/Down will navigate between associated menu items, or if these keystrokes will instead close or open associated submenus.
As a result, mega menu constructs that include nested combinations of horizontal and vertical submenu structures are nearly impossible to reliably navigate without sight, because the orientation of the currently focused menu is impossible to predict without the use of supplemental off-screen text.
Dual functionality is not a supported feature of ARIA Menus.
Many implementations of Menubar constructs include horizontally or vertically rendered triggering elements that, when clicked, will navigate to a different address. On mouseover, however, the same link will render an associated dropdown menu.
Though it is possible to add ARIA Menu markup so that screen readers will convey that there is an attached submenu when the link receives focus, there is no ARIA Menu attribute to convey that such a link will perform two different actions:
- When the link receives focus to simulate onMouseOver, and
- A different action when Enter is pressed to simulate onClick.
It also does not convey what is needed to navigate the submenu when pressing Enter will navigate to a different page instead of moving focus into the associated submenu as expected.
The use of ARIA Menu roles will limit discoverability within screen readers.
When any of the ARIA Menu roles are applied, the native roles of these elements are automatically remapped in the accessibility tree. This is intentional, so that simulated active elements can be mapped to the correct active element roles in the accessibility API and thus provide the correct hint and usage information for assistive technology users. This also means, when native links and buttons include ARIA Menu role attributes, they are no longer discoverable by assistive technologies as links and buttons. For many screen reader users that depend on quickly navigating by these active element roles to locate specific and commonly used triggering elements, this behavior can severely impair productivity within large and complex websites when encountered, because none of these controls are then discoverable using common quick navigation commands.
The use of ARIA Menu roles will limit screen reader browse modality.
When native headings, buttons, named regions, and links are used as shown in the Native Menu Markup section, screen reader users have many navigation commands available for traversing these differing control types. However, when ARIA Menu roles are applied, the correct browse modality for interacting with a native menu is automatically enforced.
Intuitive navigation within the ARIA Menu is only achievable in Forms Mode or Applications Mode, where it is possible to use the arrow keys to control focus movement within the menu construct, because keystrokes are then passed to the focused element instead of being processed by the screen reader. This causes all other content such as informative headings, named regions, and supplementary text content to become inaccessible for non-sighted screen reader users, because there is no way for them to know that these other element types exist during navigation.
The use of ARIA Menu roles prohibits the inclusion of any non-menu active element roles within the same structure.
Since the only focusable elements allowed within an ARIA Menu are those that include role=”menu”, role=”menubar”, role=”menuitem”, role=”menuitemcheckbox”, or role=”menuitemradio”, the embedding of any other active element types such as links, buttons, or other form fields will lead to significant accessibility issues for non-sighted screen reader users when attempting to interact with the menu.
The use of ARIA Menu roles causes specific menu related events to be fired in the accessibility tree.
These accessibility tree events are fired:
- When a menu is rendered,
- When focus is moved into a menu, and
- When focus is moved out of a menu after it is closed.
Assistive technologies such as screen readers use these events to identify when a menu is opened, when the user has moved into a menu, and when the user leaves a menu.
When ARIA Menu roles are used within static content on the page to represent menus that are always open, these events are fired regardless of when focus moves into the menu. This can sometimes cause navigation conflicts for screen reader users who cannot navigate normally because the screen reader is stuck in a mode where it thinks a menu is open and the user cannot identify a way to close it. These events are documented within section 5.8.4.Special Events for Menus of the User Agent Implementation Guide.
No one-size-fits-all solution”¦
As a result of these issues, one ARIA Menu design pattern can never be used as a one-size-fits-all solution for menus, because the ARIA roles and attributes that may be appropriate for one type of menu may cause critical accessibility issues if applied the same way in another.
More importantly, though many of these issues will likely improve as time goes on and support levels increase, present day development must account for these limitations within production environments where public consumption is a factor, because not doing so is a legal risk for the companies involved.
The bottom line? It does not matter if the ARIA spec says that you are allowed to add a particular role or attribute to a widget, if the use of that role or attribute prevents the user from being able to access that feature as a result. Practical usability and accessibility will always take precedence over spec conformance, because general users don’t care about how compliant something is, as long as they can intuitively use it.
ARIA Enhancements
As a general policy for mainstream development, complex ARIA widget markup should never be used unless there is a specific accessibility-related need to justify it being added. Most complex interactive behaviors can be broken down into simple components that use common active element controls, and native active elements should always be used for this purpose when it is possible to do so. Doing this will provide the greatest level of accessibility for all user types across all devices and platforms. This means avoiding the “Complex Interactive Widget Roles” listed here unless there is a specific accessibility-related reason for doing so.
In some cases, however, complex interactive behaviors can only be made accessible by using ARIA. When this is determined, it is critical to be familiar with the roles that are safe to use, and in which circumstances these apply. Downloading and reviewing the 2017 CSUN presentation “Static vs. Interactive Widget Roles – Ensuring Proper Functionality in ARIA” will help with this.
In the case of ARIA Menus, the native menu syntax should always be used as a starting point because it covers the widest range of implementations. If, however, ARIA Menu syntax is warranted, then strict adherence to focus management and role usage best practices must be correctly implemented to ensure accessibility. To do so properly, significant familiarity with the technologies involved is required.
Looking to advance your team’s knowledge of web accessibility best practices? Learn more about our expert-led onsite workshops and our e-learning portal.