Navigation
Menu and Menubar
AvailableAn application-style menu built with role="menu" and related roles, navigated using arrow keys.
What is a Menubar?
A menubar is the horizontal menu bar typically found at the top of an application window, displaying items like "File / Edit / View…". Opening each item reveals a submenu (a list of commands). It is used to provide a rich command structure similar to desktop applications.
Important: Do not use menu roles for site navigation
The most common mistake is applyingrole="menubar" / role="menu" / role="menuitem" tosite navigation links. These roles are intended for application commands (actions that trigger operations when executed).
- For a collection of links that navigate to pages, use
<nav>+<ul>+<a>. - When
role="menu"is used, screen readers announce "menu — navigate with arrow keys" as if the items are commands, but the actual content is just links, creating a mismatch that confuses users. - For site navigation with submenus, the recommended approach is a "disclosure navigation" pattern using
<button aria-expanded>to toggle visibility.
Why Does Accessibility Matter?
- Keyboard-only users. A menubar should be entered with a single Tab, navigated with ←→ across the bar, opened with ↓/Enter to reveal a submenu, items selected with ↑↓, and closed with Esc — all of these interactions are required.
- Screen reader users.
role="menubar"/role="menu"/role="menuitem"combined witharia-haspopup/aria-expandedcorrectly convey the structure and open/closed state.
Live Demo (Recommended Implementation)
Below is an application-style menubar (simulating a text editor). Since this executes commands, the menu role is appropriate. Try operating it with the keyboard.
- New
- Open
- Save
- Undo
- Redo
- Select All
- Zoom In
- Zoom Out
Try it: Tab into the bar → ← → to move between menus → ↓ / Enter to open a submenu → ↑ ↓ to move between items, Home / End to jump to edges → Within a submenu, press ← → to move to adjacent menus → Enter to execute → Esc to close and return to parent.
Keyboard Interactions
| Key | Action | Priority |
|---|---|---|
| Tab | Enter / leave the menubar (one tab stop for the entire bar) | Required |
| ← / → (on the bar) | Move to the previous / next menu | Required |
| ↓ / Enter / Space (on the bar) | Open the submenu and move focus to the first item | Required |
| ↑ / ↓ (in submenu) | Move to the previous / next item | Required |
| ← / → (in submenu) | Close the current menu and open the adjacent menu | Recommended |
| Home / End | Move to the first / last item | Optional |
| Enter / Space (on an item) | Execute the command and close the menu | Required |
| Esc | Close the submenu and return focus to the parent menu item | Required |
Required WAI-ARIA Roles & Properties
| Target | Attribute / Role | Meaning |
|---|---|---|
| Bar container | role="menubar" + aria-label | Conveys that this is a horizontal menubar and provides its accessible name. |
| Each bar item | role="menuitem" + aria-haspopup="menu" | Conveys that this is a menu item that opens a submenu. |
| Each bar item | aria-expanded + tabindex="0 | -1" | Indicates the expanded/collapsed state and roving tabindex (only one set to 0). |
| Submenu | role="menu" + aria-label | Conveys that this is a submenu and provides its accessible name. |
| Submenu item | role="menuitem" + tabindex="-1" | A command item. Focus is managed via JavaScript (roving tabindex). |
| Closed submenu | hidden | Removes the submenu from screen reader announcements and interaction while closed. |
Implementation: Recommended Pattern (Good)
Good / Recommended
Arrange role="menuitem" elements horizontally inside a role="menubar", using roving tabindex for a single tab stop. Submenus use role="menu", with open/close, Esc return, and ←→adjacent menu navigation implemented in JS.
Markup:
<div role="menubar" aria-label="Text Editor">
<button type="button" role="menuitem"
aria-haspopup="menu" aria-expanded="false" tabindex="0">
File
</button>
<ul role="menu" aria-label="File" hidden>
<li role="menuitem" tabindex="-1">New</li>
<li role="menuitem" tabindex="-1">Open</li>
<li role="menuitem" tabindex="-1">Save</li>
</ul>
<button type="button" role="menuitem"
aria-haspopup="menu" aria-expanded="false" tabindex="-1">
Edit
</button>
<ul role="menu" aria-label="Edit" hidden>
<li role="menuitem" tabindex="-1">Undo</li>
<li role="menuitem" tabindex="-1">Redo</li>
</ul>
</div>Key handling outline:
// Top-level (bar) menuitems use roving tabindex as a single tab stop.
// ← → to move within the bar, ↓/Enter to open a submenu and focus the first item.
// Within a submenu: ↑↓ to move, Esc to close and return to parent, ← → to move to adjacent menu.
top.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') moveTop(+1);
else if (e.key === 'ArrowLeft') moveTop(-1);
else if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ')
openSub(top, 0); // Open and focus the first item
else if (e.key === 'ArrowUp') openSub(top, 'last');
});
subItem.addEventListener('keydown', (e) => {
if (e.key === 'ArrowDown') focusNextItem();
else if (e.key === 'ArrowUp') focusPrevItem();
else if (e.key === 'Escape') { closeSub(); top.focus(); } // Return to parent
else if (e.key === 'ArrowRight' || e.key === 'ArrowLeft')
moveToAdjacentMenu(e.key); // Move to adjacent menu
else if (e.key === 'Enter' || e.key === ' ') run(subItem);
});Anti-pattern (Bad)
Below is an example of site navigation using role="menubar" with:hover-triggered submenus. It combines role misuse with hover dependency.
Try it: Hovering over 'Products' opens the submenu, but it cannot be opened via keyboard. The screen reader announces 'menu (navigate with arrow keys)' yet the content is just links — the behavior doesn't match.
<!-- ❌ Anti-pattern: Using menu role on site navigation with hover-triggered submenus -->
<nav>
<ul role="menubar">
<li role="menuitem">Products
<!-- Only opens on hover & using menu role on links breaks screen reader announcements -->
<ul role="menu" class="sub">
<li role="menuitem"><a href="/a">Feature A</a></li>
<li role="menuitem"><a href="/b">Feature B</a></li>
</ul>
</li>
<li role="menuitem"><a href="/price">Pricing</a></li>
</ul>
</nav>
<style>
.sub { display: none; }
li:hover .sub { display: block; } /* Hover-dependent */
</style>Bad / Avoid
Problems with this implementation:
- Role misuse — Applying
menubar/menuto a collection of page navigation links creates a mismatch between screen reader announcements (arrow-key commands) and reality (links). - Hover-dependent — The submenu only opens on mouse hover, breaking for keyboard and touch users.
- No keyboard support — ←→/↑↓/Esc and roving tabindex are missing.
- The correct approach is
<nav>+<ul>+<a>, with submenus toggled via<button aria-expanded>.
Implementation Checklist
- Confirmed the use case is application commands (not site navigation)
- Bar has role="menubar" + aria-label; items have role="menuitem"
- Items with submenus have aria-haspopup="menu" + aria-expanded
- Bar uses roving tabindex for one tab stop; Left / Right Arrow to navigate
- Submenus have role="menu"; items have tabindex="-1"
- Down Arrow / Enter opens submenu to first item; Up / Down to navigate; Home / End to jump to ends
- Escape closes submenu and returns to parent item; Left / Right moves to adjacent menu
- Not hover-dependent and focus is visible
Source (English):Menu and Menubar Pattern — W3C APG(opens in a new tab)