Navigation

Menu and Menubar

Available

An 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?

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.

Accessible Menubar

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

KeyActionPriority
TabEnter / leave the menubar (one tab stop for the entire bar)Required
/ → (on the bar)Move to the previous / next menuRequired
/ Enter / Space (on the bar)Open the submenu and move focus to the first itemRequired
/ ↓ (in submenu)Move to the previous / next itemRequired
/ → (in submenu)Close the current menu and open the adjacent menuRecommended
Home / EndMove to the first / last itemOptional
Enter / Space (on an item)Execute the command and close the menuRequired
EscClose the submenu and return focus to the parent menu itemRequired

Required WAI-ARIA Roles & Properties

TargetAttribute / RoleMeaning
Bar containerrole="menubar" + aria-labelConveys that this is a horizontal menubar and provides its accessible name.
Each bar itemrole="menuitem" + aria-haspopup="menu"Conveys that this is a menu item that opens a submenu.
Each bar itemaria-expanded + tabindex="0 | -1"Indicates the expanded/collapsed state and roving tabindex (only one set to 0).
Submenurole="menu" + aria-labelConveys that this is a submenu and provides its accessible name.
Submenu itemrole="menuitem" + tabindex="-1"A command item. Focus is managed via JavaScript (roving tabindex).
Closed submenuhiddenRemoves 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.

Site nav with menu roles + hover submenus

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/menu to 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


Source (English):Menu and Menubar Pattern — W3C APG(opens in a new tab)