aria-keyshortcuts for Grid Commands
Permalink to "aria-keyshortcuts for Grid Commands"aria-keyshortcuts tells assistive technology which keyboard shortcut activates an element. On a grid toolbar’s “Archive” button, aria-keyshortcuts="E" lets a screen reader announce “Archive, button, E” — so users learn the shortcut from the control itself, the way sighted users learn it from a tooltip. It prevents shortcuts from being a sighted-only feature, known only to people who hovered over a button.
It is also one of the most misunderstood attributes in ARIA, because it does nothing to the keyboard: it describes a shortcut that your code must implement. This page covers its syntax, placement, announcements and the sync problem with remappable shortcuts. It belongs to keyboard shortcuts & command palettes.
Spec reference
Permalink to "Spec reference"ARIA 1.2 defines aria-keyshortcuts as a string of one or more shortcuts separated by spaces. Each shortcut is a set of keys joined by +, with modifiers first. Key names follow the UI Events key values: Control, Alt, Shift, Meta, and printable characters or names such as Delete, ArrowDown, Enter. Examples: Control+Shift+E, Delete, Alt+ArrowUp Alt+K (two alternatives).
Rules that matter:
- It must be placed on the element the shortcut activates or focuses.
- It does not create any key handling; authors implement it.
- Authors should avoid shortcuts that conflict with browser, operating system or assistive technology keys.
- Use the
keyvalue that the user actually types;Shift+Emeans Shift and E, not uppercase E alone.
It is related to, but different from, accesskey, which does implement a browser-level shortcut with browser-specific modifiers and is generally avoided in applications.
Criteria: SC 4.1.2 Name, Role, Value (additional properties of the control), SC 3.3.2 Labels or Instructions (discoverability), and SC 2.1.4 for any single-character keys it describes.
When to use it — and when not to
Permalink to "When to use it — and when not to"Use it on every visible control that has a keyboard shortcut: toolbar buttons, menu items, the search field that / focuses. It is cheap, and it is the only way the shortcut reaches users who never see a tooltip.
Do not put it on the grid container to describe navigation keys. Arrow keys, Home and End inside a grid are part of the grid role’s expected behaviour; listing them in aria-keyshortcuts adds noise and they are not “shortcuts” to a control. Document them in the help dialog.
Do not use it without implementing the shortcut, and do not use it as a substitute for a visible hint. Many screen readers announce it only in certain verbosity modes, so it supplements rather than replaces documentation.
The misapplication to name is setting aria-keyshortcuts="E" on a button while the actual handler listens for Shift+E after a refactor. The attribute now teaches users the wrong key.
Annotated code example
Permalink to "Annotated code example"<div role="toolbar" aria-label="Queue actions" aria-controls="queue-grid">
<!-- SC 4.1.2: the shortcut is a property of the control it activates -->
<button type="button" data-command="archive" aria-keyshortcuts="E"
title="Archive (E)">Archive</button>
<button type="button" data-command="assign" aria-keyshortcuts="Alt+A"
title="Assign (Alt+A)">Assign…</button>
<!-- two alternatives: Delete, or Control+Backspace -->
<button type="button" data-command="delete" aria-keyshortcuts="Delete Control+Backspace"
title="Delete (Delete)">Delete…</button>
</div>
<input type="search" aria-label="Search queue" aria-keyshortcuts="/">
// One registry drives the handler AND the attribute, so they cannot drift apart
const registry = {
archive: { keys: ['E'], run: archiveSelected },
assign: { keys: ['Alt+A'], run: openAssign },
delete: { keys: ['Delete', 'Control+Backspace'], run: confirmDelete },
};
function syncAttributes() {
document.querySelectorAll('[data-command]').forEach((el) => {
const cmd = registry[el.dataset.command];
el.setAttribute('aria-keyshortcuts', cmd.keys.join(' ')); // ARIA syntax
el.title = `${el.textContent.trim().replace('…', '')} (${cmd.keys[0].replace('Control', 'Ctrl')})`;
});
}
function matches(e, combo) {
const parts = combo.split('+');
const key = parts.pop();
const mods = { Control: e.ctrlKey, Alt: e.altKey, Shift: e.shiftKey, Meta: e.metaKey };
const wanted = new Set(parts);
return e.key.toLowerCase() === key.toLowerCase()
&& Object.entries(mods).every(([m, on]) => on === wanted.has(m));
}
document.addEventListener('keydown', (e) => {
for (const cmd of Object.values(registry)) {
if (cmd.keys.some((k) => matches(e, k))) { e.preventDefault(); cmd.run(); return; }
}
});
// After the user remaps a shortcut in settings:
function remap(command, keys) { registry[command].keys = keys; syncAttributes(); }
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Reader | Announcement for the Archive button | Notes |
|---|---|---|
| NVDA + Chrome | “Archive, button, E” | Read as part of the element’s properties |
| JAWS + Chrome | “Archive, button” (shortcut on request with Insert+Tab / verbosity) |
Depends on verbosity settings |
| VoiceOver + Safari | “Archive, button” then hint may include the shortcut | Varies by version |
| TalkBack + Chrome | Not announced | Keep a visible or help-dialog route |
Any, title tooltip |
Often read as description | The title duplicates the shortcut for sighted mouse users |
Integration context
Permalink to "Integration context"aria-keyshortcuts is one of three places a shortcut should appear, alongside a visible hint (tooltip or menu accelerator text) and the shortcut help dialog in keyboard shortcut help dialogs. Generating all three from one registry is what keeps them honest.
Single-character values like E still need SC 2.1.4 handling — a setting to turn them off or scoping — covered in single-character shortcuts and SC 2.1.4. When the setting turns them off, remove or update the attribute too.
Gotchas
Permalink to "Gotchas"Mac modifiers. Write Meta for Command. If the shortcut differs by platform (Control+K versus Meta+K), set the attribute per platform rather than listing both.
Shift and character case. Shift+E and E are distinct in the ARIA syntax. If your handler lower-cases e.key, it treats them the same; decide and make the attribute match.
Conflicting reader keys. Insert and CapsLock are screen reader modifiers; avoid shortcuts that use them. Avoid Alt+letter combinations that open browser menus on Windows where possible.
Design system notes
Permalink to "Design system notes"Buttons and menu items in the design system should accept a shortcut prop that sets aria-keyshortcuts, renders the accelerator text in menus, and appends it to tooltips — all from the same value. Pair it with a platform shortcut registry so the prop is populated from the registry rather than typed by hand in each product.
Testing checklist
Permalink to "Testing checklist"FAQ
Permalink to "FAQ"Does aria-keyshortcuts make a keyboard shortcut work?
No. It only tells assistive technology that a shortcut exists. You must implement the key handling yourself and keep the attribute in sync with it.
Where should aria-keyshortcuts go?
On the element the shortcut activates or focuses — the Archive button, the search field — not on the grid or the page. That way the shortcut is announced when users reach the control.
How do I write multiple shortcuts in aria-keyshortcuts?
Separate alternatives with spaces, and join keys within one shortcut with plus signs, modifiers first — for example “Delete Control+Backspace”.
Should I use accesskey instead?
Generally no. accesskey makes the browser implement a shortcut with browser-specific modifiers that often collide with browser and screen reader commands. Implement shortcuts in script and describe them with aria-keyshortcuts.
Related
Permalink to "Related"- Single-key shortcuts and SC 2.1.4 — which shortcuts need a setting
- Keyboard shortcut help dialogs — the full list in one place
- Toolbar pattern for grid actions — the buttons that carry the attribute