What is aria-actions?
aria-actions is a new ARIA attribute. Authors point
it at a list of element IDs; each referenced element becomes a
secondary action that screen-reader users can invoke from the focused
item, without first moving focus to the action button.
The value is an IDREFS list (space-separated IDs). Each target needs an accessible name, a click handler, and a keyboard path. See the explainer for the full authoring requirements.
Reference: w3c/aria PR 1805 defines the attribute. The rendered preview has the current normative text, and the APG Tabs with Action Buttons example shows a working reference implementation.
Tabs with Actions
Each tab points at two action buttons. A screen reader can invoke Close or Pin from the tab itself; keyboard users press Delete on the focused tab as a shortcut for Close.
aria-actions="close-tab-N pin-tab-N"
Contents of index.html.
Contents of style.css.
Contents of app.js.
- Each tab's
aria-actionslists two button IDs - The action buttons sit alongside the tab inside a
.tab-groupwrapper (withrole="none"so the wrapper itself doesn't pollute the tablist's child roles).role="tab"is Children Presentational, so the buttons can't be inside the tab itself; a sibling wrapper inside the tablist is fine (the APG tabs-actions example uses the same pattern). axe-core flags this witharia-required-childrenbecause button descendants aren't a recognised tablist child role;aria-actionsis precisely what makes the co-location safe, so the warning is a known axe-vs-spec tension rather than a real defect - Each button also sets
aria-controlsback to its tab. That fits here because Close and Pin really do control the tab (the grid section omits the back-reference for that reason) - Delete on the focused tab triggers Close;
aria-keyshortcutstells AT about the shortcut - Pin is a true toggle button: it carries
aria-pressedfor persistent state and swaps its accessible name between Pin and Unpin so the label reflects the next action. The polite status line still narrates each transition - Arrow-key navigation uses the
focusgroup="tablist nomemory"attribute on the tablist when the browser supports it (declarative roving tabindex). The script keeps an arrow-key + roving-tabindex fallback for browsers without focusgroup. The action buttons carryfocusgroup="none"on their wrapper so they stay reachable by Tab but are skipped by the tablist's arrow keys
Grid with Row Actions
Each row exposes its action buttons via aria-actions set
on the <tr>. AT users invoke View details or
Archive from the row, without descending into the Actions cell.
aria-actions="view-row-N archive-row-N"
| Order | Customer | Status | Actions |
|---|---|---|---|
| #1024 | Alice Johnson | Shipped | |
| #1025 | Bob Chen | Processing | |
| #1026 | Carol Davis | Delivered |
aria-actionson each<tr>(implicitlyrole="row"inside the grid) exposes the row's actions- The buttons live in the Actions column. Only the focused row's
buttons are Tab stops; the JS swaps
tabindexas row focus moves - View opens a native
<dialog>viashowModal()showing the row's details. The dialog has implicitrole="dialog"for a clean AT announce on open. The browser handles the focus trap, Esc to close, and returning focus to the View button. A small JS shim adds backdrop-click-to-close so the prior light-dismiss UX is preserved - Esc from a button returns focus to its row (APG grid widget-mode)
- The set of roles that can carry
aria-actionsis defined by PR 1805 and targets child roles of composite widgets: tabs, options, rows, gridcells, treeitems, and similar - Arrow-key navigation uses
focusgroup="toolbar block nomemory nowrap"on the<tbody>when supported; the script keeps a roving-tabindex + arrow handler as a fallback otherwise. Each.row-actionsspan carriesfocusgroup="none"so the in-row buttons stay reachable by Tab from the focused row but are excluded from the row-level arrow nav