Accessible navigation menu
Updated October 14, 2026 · 6 min read
The main navigation is the first thing every visitor uses, and on a shop it often grows into a mega-menu with categories, brands and promotions. The most common accessibility mistake is not missing ARIA but the wrong ARIA: role="menubar" copied from a component library. This page explains what the APG recommends instead. It is part of the accessible UI patterns series.
Menu or disclosure? What the APG says
The APG has a Menu and Menubar pattern for application menus — "a list of choices, such as actions or functions" that behaves like menus in desktop software. Its navigation menubar example carries an explicit caution:
"The menubar pattern requires complex functionality that is unnecessary for typical site navigation" … "A pattern more suited for typical site navigation with expandable groups of links is the Disclosure Pattern."
The APG adds that the disclosure pattern "is better suited for most web sites because few sites need the additional keyboard functionality". Its disclosure navigation example does not use the menu role because it "does not provide the complex functionality that assistive technologies expect". A role="menu" is a promise of desktop-style menu behaviour; if the site does not deliver it, the menu is harder to use than plain links.
Recommendation: a nav landmark, nested lists of links, and disclosure buttons for the groups.
Relevant WCAG criteria
- 2.1.1 Keyboard (A): every sub-menu opens with the keyboard, not only on hover.
- 1.3.1 Info and Relationships (A): lists and nesting convey the hierarchy.
- 4.1.2 Name, Role, Value (A): toggle buttons expose
aria-expanded. - 1.4.13 Content on Hover or Focus (AA): fly-outs opened on hover are dismissible (Escape) and hoverable.
- 2.4.1 Bypass Blocks (A): a skip link or landmarks to get past a long menu.
- 2.4.3 Focus Order (A) and 2.4.7 Focus Visible (AA).
- 3.2.3 Consistent Navigation (AA): the same order on every page.
- 1.4.10 Reflow (AA) and 2.5.8 Target Size (Minimum) (AA) for the mobile menu.
Code example
<nav aria-label="Main">
<ul>
<li>
<button type="button" aria-expanded="false" aria-controls="nav-women">Women</button>
<ul id="nav-women" hidden>
<li><a href="/en/women/dresses">Dresses</a></li>
<li><a href="/en/women/shoes" aria-current="page">Shoes</a></li>
</ul>
</li>
<li><a href="/en/sale">Sale</a></li>
</ul>
</nav>
<script>
document.querySelectorAll('nav button[aria-expanded]').forEach((button) => {
const list = document.getElementById(button.getAttribute('aria-controls'));
button.addEventListener('click', () => {
const open = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!open));
list.hidden = open;
});
});
</script>
Notes:
- If the top item must also be a link to the category page, the APG has a variant, "Disclosure Navigation Menu with Top-Level Links": the link and a separate toggle button sit next to each other. Do not make one element both a link and a toggle.
- Add Escape: close the open group and return focus to its button, as the APG example does.
- On mobile, the "hamburger" button is the same disclosure:
aria-expanded, a name such as "Menu", and the panel follows it in the DOM.
Keyboard interaction
From the APG disclosure navigation example:
| Key | Function |
|---|---|
| Tab / Shift + Tab | Moves through the top-level buttons and links and, when a group is open, through its links |
| Enter or Space on a button | Opens or closes the group |
| Enter on a link | Follows the link |
| Escape | Closes the open group and returns focus to its button |
| Arrow keys, Home, End | Optional: move between buttons and links; they supplement Tab rather than replace it |
A true menubar would instead require arrow keys to move between items, Tab to leave the whole menubar and type-ahead by first letter — the "complex functionality" the APG means.
Common mistakes on online shops
- Mega-menus that open only on mouse hover; keyboard users Tab through the top links and never reach the sub-categories.
role="menubar"androle="menuitem"on ordinary links, often from a theme or plugin, without the arrow-key behaviour.- Hidden sub-menus that are still focusable: Tab disappears into invisible links (focus lost behind
opacity: 0). - The category name is both a link and the toggle, so touch and keyboard users cannot open the sub-menu.
- No
aria-expanded, or it never changes fromfalse. - Several
navlandmarks without labels (main, footer, account), so screen reader users cannot tell them apart. - Hamburger buttons without a name and mobile menus that do not trap or return focus when built as a full-screen overlay.
What automated checks catch vs manual testing
| Automated (axe-core rule) | What it finds |
|---|---|
link-name, button-name |
Menu links and toggle or hamburger buttons without a name |
aria-required-children, aria-required-parent |
menu/menubar without menuitem children, menuitem outside a menu |
aria-allowed-role |
Roles that do not fit the element (advice) |
aria-hidden-focus |
Links inside an aria-hidden sub-menu that can still receive focus |
list, listitem |
Broken list structure in the navigation |
landmark-unique |
Several navigation landmarks with the same or no name (advice) |
bypass, skip-link |
No way to skip the menu; a skip link with a broken target |
target-size |
Menu targets that are too small |
All are cards in our rule library. The scan sees the page as it loads, with sub-menus closed. Manual testing: open every group with Enter or Space, check that aria-expanded changes, press Escape, Tab through the open group, check that closed groups cannot receive focus, and repeat with the mobile menu at 320 CSS pixels width. The browser extension can check the page with a group open.
Run the free one-page check on your home page. It is an automated check, not a full audit.
Frequently asked questions
Should a website navigation use role="menu" or role="menubar"?
Usually not. The APG warns that the menubar pattern requires complex functionality that is unnecessary for typical site navigation and recommends the disclosure pattern for navigation with expandable groups of links.
What is disclosure navigation?
A nav element with a list of links, where each group with sub-links has a button with aria-expanded that shows or hides the sub-list. Users move through it with Tab, Enter and Space, like the rest of the page.
How do I mark the current page in the menu?
Put aria-current="page" on the link to the page the user is on, and show it visually as well, not by colour alone.
Does the menu need arrow-key support?
Not in the disclosure pattern. The APG example supports arrow keys, Home and End as an optional extra on top of Tab. A real menubar, in contrast, must implement the full arrow-key model.
Can an automated check test a navigation menu?
It finds links and buttons without names, menu roles without menu items, duplicate unlabelled navigation landmarks and hidden but focusable links. It cannot test whether sub-menus open with the keyboard or whether aria-expanded updates.
Sources
- W3C WAI, APG Navigation Menubar example (caution about site navigation): https://www.w3.org/WAI/ARIA/apg/patterns/menubar/examples/menubar-navigation/
- W3C WAI, APG Menu and Menubar pattern: https://www.w3.org/WAI/ARIA/apg/patterns/menubar/
- W3C WAI, APG Disclosure pattern: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
- W3C WAI, APG Disclosure Navigation Menu example: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/
- W3C WAI, APG Menu Button pattern: https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/