Building an accessible dropdown menu with keyboard navigation in 2026 is no longer about copying a desktop application widget into the browser. The most sustainable approach uses fewer roles, more semantic HTML, and deliberate focus management. This article walks through the modern pattern that passes WCAG, helps screen-reader users, and still feels smooth for people who prefer the keyboard over the mouse.
Decide First: Is It a Disclosure or a Real Menu?
The biggest accessibility mistake is reaching for role="menu" before understanding what the dropdown is for. A disclosure is a button that shows or hides a group of links, like a “Resources” section in a site navigation. A menu widget is a more application-like control where items perform commands, such as a sort menu or an edit menu inside a rich text editor.
When users encounter links in a disclosure, they expect standard web behavior: Tab moves through links, Enter opens them, and Shift+Tab moves backwards. When users encounter a true menu widget, they expect arrow-key navigation, Home and End shortcuts, and Escape to close. The clearest advice in 2026 is this: if the dropdown contains normal links that go somewhere, build a disclosure. Reserve the full ARIA menu pattern for command-like items that do not behave like ordinary page links.
The Small Set of ARIA and HTML That Does the Work
For the majority of website dropdowns, two ARIA properties on the trigger are enough. The trigger itself should be a real <button>, never a styled <div>.
aria-haspopup="true"communicates that activating the button opens a popup.aria-expandedcommunicates the current open or closed state.- The popup container should be a plain
<ul>with<li>items, so assistive technology naturally presents a list of options.
Do the links inside need role="menuitem"? No, when they are genuine links. Changing them to menuitem removes familiar link semantics and often makes screen-reader announcements more confusing. If the trigger opens a list where each item is a button command, then role="menu" and role="menuitem" may be justified. Better yet, check whether the new Popover API can handle the open-and-close mechanics and reserve JavaScript for focus management alone.
Focus Management: The Heart of the Pattern
An accessible dropdown menu with keyboard navigation succeeds or fails based on one question: where does focus go? Focus should never disappear into the background. When you press the trigger, focus should respond immediately. When you dismiss the dropdown, focus should return to the same trigger so the next Tab press starts from a predictable place.
Opening with the Keyboard
If the user opens the dropdown with Enter or Space, move focus into the first item. Do not leave focus on the trigger while the dropdown silently appears; a keyboard user cannot see the trigger and the newly opened panel at the same point of regard. Moving focus also informs screen-reader users that the popup is now the active context.
Mouse users can be treated differently. If someone clicks the trigger, the dropdown can open without moving focus, because the click has already established a clear visual context. This avoids a jarring focus jump for users who simply want to point and choose.
Roving Tabindex Without Trapping Users
Inside the dropdown, avoid trapping users. One clean way is to use a roving tabindex. Only one link should be focusable through natural Tab order at any given time, usually the currently active item. The rest are set to tabindex="-1". JavaScript listens for arrow-key events and calls the .focus() method on the next item.
This prevents a long dropdown from producing dozens of Tab stops. It also gives the arrow keys a clear, consistent job: Arrow Down goes to the next item, Arrow Up goes to the previous one, Home jumps to the first, and End jumps to the last. When the user presses Tab from inside the dropdown, the menu should dismiss and the next element on the main page receives focus.
Key-by-Key Behavior to Implement
Keep the interaction map short and predictable. Users should not need a manual to understand the widget.
- Enter or Space: open the dropdown and move focus to the first item.
- Arrow Down: move focus to the next item, wrapping to the first item at the end.
- Arrow Up: move focus to the previous item, wrapping to the last item at the beginning.
- Home: move focus to the first item.
- End: move focus to the last item.
- Escape: close the dropdown and return focus to the trigger button.
- Tab: close the dropdown and move to the next meaningful element on the page.
One subtlety is outside clicks. When a user clicks outside the open dropdown, close it, but do not move focus unless the click landed on another interactive element. The browser already handles focus for clicked elements, so adding your own focus calls can cause undesirable jumps.
Submenus and Flyouts: Proceed with Caution
Many dropdowns fail when someone adds a second level. Flyout submenus are difficult for keyboard users because they require a diagonal movement path that the keyboard cannot naturally replicate. If the design demands a nested menu, open the submenu with Arrow Right and close it with Arrow Left or Escape. The parent item should clearly expose its expanded state with aria-expanded, and the submenu should open immediately after focus moves to its trigger.
Hover-only flyouts are not accessible. A user who moves focus from a top-level item to a submenu item should not be forced to move the mouse along a invisible corridor to keep the menu open. Introduce a short open delay and always pair hover behavior with keyboard opening.
Visible Focus That Respects WCAG 2.2
Managing focus invisibly is not enough; users must see it. The current WCAG 2.2 Success Criterion 2.4.7 and 2.4.11 require that keyboard focus is always visible and not fully obscured by another element. A dropdown menu can accidentally cover the trigger button or overlap the focused item. If a dropdown opens and closes in a tight layout, check every open state for overflow and stacking contexts.
Use the modern :focus-visible pseudo-class instead of removing outlines indiscriminately. A strong 2-pixel outline with a contrasting offset works across operating systems. Dropdown menus are small components, so avoid relying on color alone to show where focus currently sits.
Test with a Keyboard First
Before opening a screen reader, test the dropdown with nothing but a keyboard. Try all seven interactions above, then reset focus and repeat. The most common failure is not missing event listeners; it is losing track of focus after the menu closes. A useful debugging trick is to add a temporary log of document.activeElement with every keypress. That will reveal exactly when focus falls back to the <body>.
After the keyboard test, turn on a screen reader and verify the announced states. When the trigger opens, the screen reader should announce the button and its expanded state. When the first item receives focus, the screen reader should announce its label and link role. Build these checks into every release, because a one-line regression can silently return the component to an inaccessible state.
Conclusion
An accessible dropdown menu with keyboard navigation does not require a mountain of ARIA boilerplate. The most resilient pattern starts with semantic HTML, uses aria-expanded and aria-haspopup on a real button, and puts the remaining energy into focus movement, Escape handling, and visible indicators. By keeping the widget close to native web behavior, you create a component that is easier for users, simpler for developers, and far more likely to stay accessible as the page evolves.
