CONTRIBUTOR-DOCS / Project planning / Components / Action Menu / Action menu accessibility migration analysis
In this doc
This page is for 2nd-gen swc-action-menu in Spectrum Web Components. It is the ActionMenu-flavored host (often a “more” or ellipses control). The goal is WCAG 2.2 Level AA.
swc-action-menuis a full menu button host, likeswc-menu. It has a trigger,swc-popover(or similar) for the open layer, and arole="menu"list in shadow DOM—not on the custom element.- The menu slot holds only
swc-menu-item,swc-menu-group, andswc-menu-separator. Do not nest anotherswc-menuorswc-action-menuin that slot. Submenus use the menu itemsubmenuslot. - Mobile tray and checkbox/radio style rows are out of scope for now (Menu — Migration scope).
- Target: WCAG 2.2 Level AA. Menu items that work like checkboxes or radios are out of scope for now (Migration scope).
Popover only positions the layer. Menu roles and in-menu keys live in swc-action-menu / swc-menu (see Menu: What swc-menu is).
These items wait on a single product decision from design and accessibility:
- Mobile tray — small-screen / bottom sheet (covered in other docs here).
- Selectable menu items — checkboxes, radios,
aria-checked, and multi-select. This doc still points to the APG for when that work ships; do not treat those rows as final for 2nd-gen until that work closes.
- Action menu migration roadmap for DOM, CSS, and API.
- Menu a11y doc —
swc-menuas a parallel top-level host,FocusgroupNavigationController, Migration scope, link rows (Cards). - Popover a11y doc —
swc-popoverand layering; it does not replace the internalrole="menu"wiring in menu hosts.
- Same parts as
swc-menu(Menu doc): trigger,swc-popoverfor the open layer,role="menu"inside the shadow tree, menu slot for item / group / separator. Defaults follow React Spectrum's ActionMenu (ellipses / “more”); check 2nd-gen source for final DOM. - Not a combobox or Picker field (When to use something else on the menu doc).
- Use
swc-popoverfor placement, not the old overlay stack as the default. Tray and selection rows follow Menu — Migration scope.
- Picker with a text field — APG combobox;
swc-pickerorswc-combobox, not the menu. - Main site nav — disclosure navigation; e.g.
uec-sidenavorswc-sidenav. - Modal work — dialog (modal) pattern; you may still reuse popover-style chrome on the frame (Popover).
- A control that is not the ActionMenu / “more” look — use
swc-menuwith the right trigger.
- Not
swc-popoveralone: the action menu and menu host own shadow DOM ARIA and list keyboard navigation. Popover positions and may help dismiss; see Menu: What it is not. - Not slotted as a child of
swc-menu,swc-action-menu, orswc-menu-item. For nested menus, use the menu itemsubmenuslot. - Not a modal. Modal chrome is covered in the Popover doc — Overview.
- Menu a11y doc —
swc-menu, items, submenus, links, scope. - Menu group a11y doc —
swc-menu-groupunderswc-menu,swc-action-menu, orswc-menu-item(submenu list). - Menu separator a11y doc —
swc-menu-separatoras a direct list row, not under a group; in docs, prefer it between labelled groups (Overview). - Menu item a11y doc — rows,
submenuslot, link rows.
- Menu button / actions — trigger opens
role="menu"; plain items usemenuitem;menuitemcheckbox/menuitemradioand selection UX sync with Menu migration scope when that work lands. - Menubar — for submenu keystrokes and child
role="menu"surfaces in cascading menus where relevant (itemsubmenuslot; see Menu item and Menu).
| Idea | Plain meaning |
|---|---|
| Name, role, value (4.1.2) | Trigger has button semantics and a name; menu and items are exposed per Menu. Ellipses-only action menus still need a name. Disabled swc-menu-item rows use aria-disabled="true" when the row is disabled and cannot be activated, same as Menu item — ARIA: Disabled row and Menu — Guidelines that apply. |
| Focus order (2.4.3) / Focus visible (2.4.7) | Open moves focus into the menu; Escape returns focus to the trigger; roving tabindex in the list (FocusgroupNavigationController). |
| Keyboard (2.1.1) | All commands from the menu button pattern; no pointer-only submenus (1st-gen SWC-1332, SWC-671). |
| Non-text contrast (1.4.11) | Focus indicators on items and submenu parents (SWC-1517). |
Bottom line: swc-action-menu is a full menu-button host like swc-menu (trigger, swc-popover, role="menu" in shadow DOM, items / groups / separators in the menu slot). Submenus sit on swc-menu-item in its shadow tree, not a second swc-menu in the list. Row and submenu details are in the menu item doc and menu doc. This doc centers ActionMenu trigger defaults, wiring, and tests. Tray and selectable rows follow Migration scope.
| Jira | Type | Status (snapshot) | Resolution (snapshot) | Summary |
|---|---|---|---|---|
| SWC-617 | Bug | — | Fixed | Remove aria-activedescendant; roving tabindex / focus group |
| SWC-572 | Bug | — | Fixed | Action menu / picker VoiceOver |
| SWC-1488 | Bug | — | Fixed | Safari crash ActionMenu + VoiceOver |
| SWC-1448 | Bug | — | Fixed | Refactor Picker / action menu split |
| SWC-230 | Bug | — | Fixed | Arrow keys when opened by mouse |
| SWC-60 | Bug | — | Fixed | Keyboard should start at first item |
| SWC-923 | Bug | To Do | Unresolved | menu-item + href double link (see Menu) |
| SWC-1332 | Bug | To Do | Unresolved | Submenu custom content not keyboard accessible |
| SWC-1941 | Bug | To Do | Unresolved | Space/Enter on menu item in modal + popover |
| SWC-933 / SWC-932 | Bug | Blocked / To Do | Unresolved | Picker arrow in popover list |
| SWC-686 | Bug | — | Unresolved | iOS submenu + VoiceOver closes tray |
| SWC-671 | Bug | Blocked | Unresolved | Submenus on mobile visibility |
| SWC-1517 | Bug | To Do | Unresolved | Submenu item focus on hover Safari |
| SWC-1174 | — | To Do | Unresolved | Picker visible label (Menu-related) |
| SWC-89 | Story | — | Unresolved | Touch submenu UI |
| SWC-553 | Bug | To Do | Unresolved | i18n strings (Picker + action menu context) |
| SWC-577 | Story | — | Done | Research accessible menu navigation |
Item- and submenu-level rules live in Menu — Recommendations: swc-menu. Here: the swc-action-menu host and ActionMenu trigger defaults. Where role="menu" lives and how the submenu slot works match swc-menu—same menu slot rules (no nested swc-menu / swc-action-menu; only item, group, separator in the menu slot for current scope).
| Topic | What to do |
|---|---|
| Host placement | swc-menu and swc-action-menu are not slotted into swc-menu, swc-action-menu, or swc-menu-item. They are top-level menu button hosts. |
| Menu slot (current migration) | Intended children: only swc-menu-item, swc-menu-group, and swc-menu-separator. Do not depend on other types in that slot (check 2nd-gen source). |
| Trigger | Button: aria-haspopup="menu"; aria-expanded matches open/closed. Name the control (aria-label; icon-only needs a name) (ActionMenu defaults, e.g. “More...”). |
Internal role="menu" (not the CE host) |
The menu-button trigger and the internal role="menu" node are implemented together in swc-action-menu’s (or swc-menu’s) shadow DOM—role="menu" is not on the <swc-menu> / <swc-action-menu> custom element host, and the host design keeps the trigger and menu surface in that shadow tree together. Verify final markup in 2nd-gen source before shipping docs. |
Relation to swc-menu |
Both swc-action-menu and swc-menu hosts put role="menu" in shadow DOM, not on the custom element. swc-menu ~ Menu; swc-action-menu ~ ActionMenu (Menu doc). Do not use role="listbox" on the internal list for this pattern. |
| No shortcut | Action menu is for commands, not a combobox + listbox. |
Disabled swc-menu-item |
Each disabled item sets aria-disabled="true" and must not run its action when disabled (roving focus may still visit the row; see Menu item — ARIA: Disabled row and Menu — Recommendations: swc-menu. |
Host shell (stays in shadow): the ActionMenu trigger and the internal role="menu" node are implemented together in swc-action-menu’s shadow tree (same rule as Menu — Recommendations: swc-menu; verify in 2nd-gen source).
Slotted menu content does not have to sit inside the trigger’s shadow tree, but: do not slot swc-menu or swc-action-menu into swc-menu, swc-action-menu, or swc-menu-item. The menu slot is for swc-menu-item, swc-menu-group, and swc-menu-separator only. swc-menu-group and swc-menu-separator are direct list children of those three parents; see Menu group, Menu item, and Menu separator. Roving focus uses FocusgroupNavigationController, not IDREF to each item. See Menu: Shadow DOM for cross-root ID issues.
- Closed: focusable trigger; menu not in the tab order;
aria-expanded="false". - Open:
aria-expanded="true"; focus moves into the internalrole="menu"surface (shadow tree) and items as in the menu doc (same menu-button pattern forswc-menuandswc-action-menu). Disabledswc-menu-itemrows exposearia-disabled="true"as in the menu doc — Accessibility tree expectations and Menu item — ARIA: Disabled row.
Does not apply to swc-action-menu as a form field; it is not a form-associated control. Label the button for accessibility.
Does not apply by default on the action menu host. Item-level busy states or toasts are out of scope for this primitive.
Intentionally omitted. If popover open/close uses motion, treat it like other layered UI; document reduced motion in sibling guideline docs only.
-
Open from the trigger per the menu button pattern (Arrow Up/Down, Space, Enter); in-menu navigation in the open internal
role="menu"surface (swc-menu/swc-action-menushadow tree, not the CE host; or a test harness) via FocusgroupNavigationController (notaria-activedescendantin 2nd-gen; see SWC-617). Use the same item-collection model asswc-menu: the controller should look forswc-menu-itemas direct list children andswc-menu-itemunder each directswc-menu-group, e.g.:this.querySelectorAll(':scope > swc-menu-item, :scope > swc-menu-group > swc-menu-item');
(with
thisas the list context the controller binds to; verify in 2nd-gen source). Submenu keys on rows with a populatedsubmenuslot per Menu item — Keyboard and focus. -
Printable character navigation (optional; not typeahead):
swc-action-menuuses the same in-list keyboard model asswc-menu—arrows, Home, End, and the same optional printable-character rules for the openrole="menu". It is not combobox typeahead on a text field. Submenus insideswc-menu-itemshould match. See Menu — Printable character navigation (optional; not typeahead). -
Disabled items:
aria-disabled="true"and no activation on Enter/Space when aswc-menu-itemis disabled, per Menu item — ARIA: Disabled row and Menu — Keyboard and focus. -
Close: Escape returns focus to the trigger; see Menu: Testing and action menu stories for submenu cases.
| Kind of test | What to check |
|---|---|
| aXe / Storybook | Open/closed; aria-disabled on disabled items as implemented (Menu item — ARIA, Menu doc); no duplicate link activation for href items (SWC-923). |
| Playwright / ARIA snapshots | Trigger plus internal role="menu" in shadow DOM, not the CE host (SWC-1941, SWC-230, SWC-60); cascading menu via submenu slot (trigger + role="menu" in swc-menu-item shadow) (SWC-1332). |
| iOS / VoiceOver | Covered (see SWC-686, SWC-1488, SWC-572). |
Follow the 2nd-gen Storybook Keyboard testing guide on a fully composed path (swc-action-menu or swc-menu: trigger + internal role="menu" (shadow) + swc-popover, not the 1st-gen overlay stack). Cover the menu button pattern on the trigger (open/close, focus return) and in-menu keys on the open menu surface (FocusgroupNavigationController); if implemented, cover printable character navigation (not typeahead) on the top-level list and in submenus. Include Escape, submenu item / submenu slot cases, and small-viewport popover positioning as relevant. See Menu: Keyboard testing for menu surface detail, Menu item: Keyboard and focus for submenu rows, and Recommendations: Keyboard and focus here.
Mirror test cases from 1st-gen defects in the Jira table; add 2nd-gen stories for ellipsis, icon naming, and a fully composed Action menu (or Menu) path: trigger + top-level swc-popover. Mobile tray is out of scope for the current migration (no separate tray test branch here).
- macOS + iOS VoiceOver, NVDA; follow Keyboard testing and Screen reader testing on a fully composed
swc-action-menu(or equivalent Menu host) path (trigger +swc-popover, not the 1st-gen overlay stack). - Submenus and mobile/touch (SWC-686, SWC-89): tray-related issues may not apply until tray is in scope; still verify popover-positioned menus on small viewports where relevant.
Do not require checkbox or radio menu item stories or selection-mode coverage until Menu migration scope closes; the APG editor menubar example remains a future reference when that work ships.
If 2nd-gen supports cascading menus via swc-menu-item’s submenu slot (see Menu item — Testing, Menu: Testing, and SWC-671 / SWC-89), add ARIA and keyboard end-to-end tests for at least one two-level path (submenu trigger and child role="menu" in swc-menu-item’s shadow DOM + popover anchoring as implemented).
- ActionMenu trigger (ellipses, optional label) with a clear name on icon-only controls; keep separate from combobox / Picker stories (SWC-1174).
- Both
swc-menuandswc-action-menuare full menu-button hosts;role="menu"is in shadow DOM, not the CE host (Menu — Overview). Do not slotswc-menu/swc-action-menuas list children. Menu slot: only item, group, separator (check source). Submenus:submenuslot onswc-menu-itemin shadow, not a nestedswc-menuin the list. - Disabled
swc-menu-itemrows:aria-disabled="true"and no activation when disabled, aligned with Menu item — ARIA: Disabled row and Menu — Recommendations. - Open/close and keyboard wiring (see Menu doc; may split controller,
swc-popover, menu hosts).FocusgroupNavigationControllerfor in-list move;swc-popoveranchors the layer. If implemented, printable character navigation (not typeahead) onswc-action-menuand submenus matchesswc-menu. Tray and item selection: Migration scope. - Jira backlog (1st-gen table above) triaged alongside current migration work in Jira; tests for
swc-popover-positioned menus (no tray branch in this scope). - iOS, touch, and submenus (SWC-686, SWC-89, SWC-1332, SWC-671)—SWC-686 references 1st-gen tray; revalidate against
swc-popoverwhere applicable.
- React Spectrum: ActionMenu
- React Spectrum: Menu
- WAI-ARIA APG: Menu button (actions) example
- Menu accessibility migration analysis
- Menu group accessibility migration analysis
- Menu item accessibility migration analysis
- Popover accessibility migration analysis
- PR #6129 — Focusgroup navigation
- 2nd-gen Storybook: Keyboard testing
- 2nd-gen Storybook: Screen reader testing
- WCAG 2.2