Skip to content

Make context menus keyboard and screen reader operable - #1376

Open
captbaritone wants to merge 2 commits into
masterfrom
a11y-menus
Open

captbaritone wants to merge 2 commits into
masterfrom
a11y-menus

Conversation

@captbaritone

Copy link
Copy Markdown
Owner

Webamp's click-to-open menus can't be opened, navigated or closed from the keyboard, and screen readers don't see them as menus. These are the main window's Winamp Menu (#option), the clutter bar's Options menu (O), the equalizer's Presets menu, and the playlist's Sort and Misc Options menus.

Menu button (ContextMenuTarget)

  • The trigger (its .handle) is now a focusable role="button" with aria-haspopup="menu" and aria-expanded.
  • It takes a new required label prop for its accessible name: "Winamp Menu" (matching #option's existing title), "Options", "Presets", "Sort List" and "Misc Options".
  • Enter, Space or ArrowDown opens the menu and focuses its first item, and Escape closes it.
  • Opening with the mouse is unchanged, and doesn't move focus into the menu.

Menu (ContextMenu)

Roles:

  • the list is role="menu"
  • items are menuitem, or menuitemcheckbox with aria-checked when they have a checked prop
  • <Hr> is a separator
  • <Parent> is a menuitem with aria-haspopup/aria-expanded and a nested menu
  • items get aria-label, so a parent's name doesn't include all of its children's text, and a checked item's name doesn't include the CSS "✓"

Keyboard (roving focus, items are tabIndex={-1}):

Key Action
↓ / ↑ Next / previous item, wrapping and skipping separators
Home / End First / last item
Enter / Space Activate the item, close the menu, return focus to the trigger
→ (or Enter/Space on a parent) Open the submenu and focus its first item
← / Escape inside a submenu Close the submenu and focus its parent
Escape / Tab Close the menu and return focus to the trigger
  • Handled keys call stopPropagation(), so with enableHotkeys on, the arrow keys don't also change volume or seek.
  • Submenus still open on hover via CSS. Keyboard-opened ones get an .expanded class.

Visuals: keyboard focus uses the existing hover highlight via :focus-visible. The menu is portalled outside #webamp, so this PR also suppresses the browser's own focus outline there. Mouse users see no change.

Not in this PR

  • Right-click menus (ContextMenuWrapper) get the same roles, and Escape closes them, but there's still no keyboard way to open them. Everything in them is also reachable from the Winamp Menu or as a button.
  • The playlist window's sprite popups (Add/Remove/Select/Misc/List) are separate components and remain mouse-only.

Testing

  • New ContextMenuTarget.test.tsx (12 tests) covers:
    • trigger semantics
    • opening with Enter, Space or ArrowDown (focus moves into the menu) vs the mouse (focus doesn't move)
    • item roles and state
    • arrow, Home and End navigation that skips separators
    • Enter activating an item and returning focus
    • Escape returning focus
    • opening/closing submenus, Escape inside a submenu, activating submenu items
    • navigation keys not reaching document-level hotkey listeners
  • tsc, eslint, prettier --check and vitest run (220 passed) pass locally.

@netlify

netlify Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for tourmaline-kringle-c98715 ready!

Name Link
🔨 Latest commit 3a6739c
🔍 Latest deploy log https://app.netlify.com/projects/tourmaline-kringle-c98715/deploys/6ad10b3d9121a0b075d212b3
😎 Deploy Preview https://deploy-preview-1376--tourmaline-kringle-c98715.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Size Change: +1.21 kB (+0.25%)

Total Size: 492 kB

📦 View Changed
Filename Size Change
packages/webamp/built/webamp.bundle.min.js 299 kB +599 B (+0.2%)
packages/webamp/built/webamp.lazy-bundle.min.js 193 kB +614 B (+0.32%)

compressed-size-action

@captbaritone
captbaritone force-pushed the a11y-menus branch 3 times, most recently from 4f3ab25 to d68a5d8 Compare October 15, 2026 17:00
Menu buttons (ContextMenuTarget): render the trigger as a focusable, labelled
role="button" with aria-haspopup="menu" and aria-expanded. Enter, Space or
ArrowDown opens the menu and focuses its first item. Opening with the mouse is
unchanged and does not move focus.

Menus (ContextMenu): role="menu", with menuitem / menuitemcheckbox (with
aria-checked) / separator items and roving focus. Up/Down/Home/End move
between items, Enter/Space activate, Right/Left (or Enter/Escape) open and
close submenus, and Escape/Tab close the menu and return focus to the trigger.
Activating an item from the keyboard also returns focus to the trigger.
Handled keys don't propagate, so global hotkeys don't also act on them.

Keyboard focus is shown with the existing hover highlight, via :focus-visible,
so pointer users see no change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant