Accessibility
Keyboard navigation in menus
A navigation menu is a list of links: Tab is enough. The role="menu" pattern with arrow keys belongs to application menus, and must be implemented in full.
Key points
- The site menu is navigation: links inside a nav, reachable with Tab, with no role="menu".
- role="menu" requires roving tabindex, arrows, Home, End and Escape; half of it is worse than a list of links.
- The trigger reports state with aria-expanded and the focus indicator stays visible, as 2.4.7 Focus Visible, level AA, requires.
A navigation menu is traversed with Tab, because it is a list of links inside a nav element; the role="menu" pattern with arrow keys belongs to application menus, the ones that run commands. Criterion 2.1.1 Keyboard, level A, requires everything to be reachable by keyboard, not every menu to use arrows.
Two different menus
The distinction decides the markup. A site menu takes the user to another page: those are links, and Tab is the natural way. An application menu runs commands on the current state, like the menu in an editor: there role="menu", role="menuitem" and the arrow keys are correct, because the screen reader switches to application mode. The WAI-ARIA Authoring Practices, W3C Document License, describe both patterns and keep them apart.
The disclosure pattern
Products
Analytics
Billing
The button reports state with aria-expanded and points at the list with aria-controls. Enter and Space open, Escape closes and returns focus to the button. The links stay links: no role="menuitem", no negative tabindex. Criterion 4.1.2 Name, Role, Value, level A, is met by the native button, which already has a name and a role.
If you choose role menu
Then five things arrive together: tabindex="-1" on every item but one, tabindex="0" on the active item, arrow keys that move focus, Home and End, and Escape closing back to the trigger. Criterion 2.4.3 Focus Order, level A, requires the order to make sense, and the roving tabindex keeps it. A half-built menu, with the role but no arrows, traps anyone not using a mouse.
The test
Walk the menu with Tab: every item must take focus in visual order. Open the submenu with Enter, close it with Escape and check that focus returns to the button. Then look at the screen: the indicator must stay visible on every item, as criterion 2.4.7 Focus Visible, level AA, requires.
npx --yes pa11y https://example.com --standard WCAG2AA
Pa11y, LGPL-3.0 licence, flags a role="menu" with no valid items or a button with no name. It does not press Tab and does not open the submenu: the sequence stays manual.
The EneUI catalogue
EneUI indexes the menus, navigation and the shadcn navigation menu record, MIT licence from the shadcn/ui project: it is built on Radix Navigation Menu, which handles arrows, Escape and focus. Checked on 1 November 2026 against 111,985 components.
The limitation
No keyboard pattern rescues a menu that is too long. If a submenu opens twenty links, keyboard users cross all of them before leaving, and navigation gets worse. Criterion 2.4.1 Bypass Blocks, level A, is still needed, and a mega-menu must be designed short before it is made accessible.
Where to continue
The command palette is in a command menu with shadcn; focus styles are in focus visible with Tailwind. The comparison of ready-made menu blocks is in the best menu blocks.
Frequently asked questions
Should I use role menu for the site menu?
No. The site menu is navigation: links inside a nav, reachable with Tab. role="menu" is for command menus and changes how the screen reader interprets the page.
Are arrow keys needed anyway?
Only in application menus with role="menu". In a list of links arrows are not required; Tab and Shift+Tab are enough and are the expected behaviour.
How do I report an open submenu?
With aria-expanded on the button that controls it, updated to true and false. If the button also has aria-controls pointing at the list, the reader knows the target too.
Must Escape close the menu?
Yes. Criterion 2.1.2 No Keyboard Trap, level A, requires a way out by keyboard, and Escape is the convention. On close, focus returns to the trigger.
Can I open the menu with CSS hover only?
No, because keyboard and touch users never hover over the element. Open with a button and :focus-within, or with a JavaScript state that answers Enter, Space and Escape.