Keyboard Shortcut Help Dialogs
Permalink to "Keyboard Shortcut Help Dialogs"A shortcut help dialog lists every keyboard shortcut an application supports, usually opened with ?. For data apps with grids, it is also where the grid’s own keys — edit mode, selection extension, copy and paste — are documented. It prevents the most common reason keyboard features go unused: users who would benefit from them never learn they exist, because the only documentation is a hover tooltip.
This page builds the dialog so that it is itself accessible — reachable without knowing a shortcut, structured as tables, and correct for the user’s platform. It belongs to keyboard shortcuts & command palettes.
Spec reference
Permalink to "Spec reference"There is no specific ARIA pattern for help dialogs; they combine a modal dialog with data tables.
- The dialog:
<dialog>opened withshowModal(), named by its heading. - The content: one
<table>per context (“Anywhere”, “In the queue grid”, “While editing a cell”), each with a<caption>, a column for the action and a column for the keys. Shortcut lists are genuinely tabular — action and key are related values — and tables let screen reader users read by column. - The keys:
<kbd>elements. Screen readers read their text content; they do not announce “keyboard input”, so the text must be readable: “Control”, “Shift”, “Up Arrow” rather than glyphs like ⌃ ⇧ ↑ alone.
Criteria: SC 3.3.2 Labels or Instructions and SC 2.1.1 Keyboard (the documented keys must work), SC 1.3.1 (the action–key relationship is programmatic), SC 2.1.4 (the setting to turn off single-key shortcuts is typically offered here).
When to provide a help dialog — and when not to
Permalink to "When to provide a help dialog — and when not to"Provide one whenever an application has more than a handful of shortcuts, or has a grid with its own keyboard model. The grid keys are not obvious even to experienced screen reader users, because every grid implements the optional parts differently.
A help dialog is not a substitute for making shortcuts discoverable where they apply: menu items should show accelerators, buttons should carry aria-keyshortcuts, and tooltips should mention the key. The dialog is the complete reference; those are the in-context hints.
The misapplication to name is opening the help only with ?. A user who does not know the shortcuts cannot find the page that lists them. Put a “Keyboard shortcuts” button in the help menu, the footer or the user menu.
Annotated code example
Permalink to "Annotated code example"<button type="button" id="kbd-help-btn" aria-keyshortcuts="Shift+?">Keyboard shortcuts</button>
<dialog id="kbd-help" aria-labelledby="kbd-help-title">
<h2 id="kbd-help-title">Keyboard shortcuts</h2>
<!-- SC 2.1.4: the setting lives with the shortcuts it controls -->
<fieldset>
<legend>Single-key shortcuts</legend>
<label><input type="radio" name="sks" value="on" checked> On</label>
<label><input type="radio" name="sks" value="off"> Off</label>
</fieldset>
<!-- SC 1.3.1: one captioned table per context -->
<table class="kbd-table">
<caption>In the queue grid</caption>
<thead><tr><th scope="col">Action</th><th scope="col">Keys</th></tr></thead>
<tbody>
<tr><th scope="row">Next row</th>
<td><kbd>J</kbd> or <kbd>Down Arrow</kbd></td></tr>
<tr><th scope="row">Select range</th>
<td><kbd><kbd>Shift</kbd> + <kbd>Down Arrow</kbd></kbd></td></tr>
</tbody>
</table>
<form method="dialog"><button>Close</button></form>
</dialog>
// Generated from the registry, with platform-correct modifier names
const isMac = /Mac|iPhone|iPad/.test(navigator.platform);
const KEY_NAMES = { Control: isMac ? 'Command' : 'Control', Alt: isMac ? 'Option' : 'Alt',
ArrowDown: 'Down Arrow', ArrowUp: 'Up Arrow', ' ': 'Space' };
function keyLabel(combo) {
return combo.split('+').map((k) => `<kbd>${KEY_NAMES[k] ?? k}</kbd>`).join(' + ');
}
function renderHelp(registry) {
for (const [context, commands] of Object.entries(groupByContext(registry))) {
const rows = commands.map((c) =>
`<tr><th scope="row">${c.label}</th><td>${c.keys.map(keyLabel).join(' or ')}</td></tr>`).join('');
helpBody.insertAdjacentHTML('beforeend',
`<table class="kbd-table"><caption>${context}</caption>
<thead><tr><th scope="col">Action</th><th scope="col">Keys</th></tr></thead>
<tbody>${rows}</tbody></table>`);
}
}
// ? opens help unless the user is typing; Shift is implied by the ? character
document.addEventListener('keydown', (e) => {
if (e.key === '?' && !e.target.closest('input, textarea, [contenteditable="true"]')) {
e.preventDefault(); document.getElementById('kbd-help').showModal();
}
});
Generating the tables from the registry is not a convenience; it is the only reliable way to keep documentation true. Hand-written shortcut pages go stale within a few releases.
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Event | Expected announcement | Notes |
|---|---|---|
| Activate “Keyboard shortcuts” | “Keyboard shortcuts, dialog” | Focus on the first control (the setting) |
T (NVDA/JAWS browse mode) |
“In the queue grid, table with 2 columns and 12 rows” | Jump between context tables |
Ctrl+Alt+Down in Keys column |
“Keys, J or Down Arrow” | Header read on column change |
? while typing in search |
Types “?” — help not opened | Guard for editable targets |
Escape |
Dialog closes; focus back to opener | Native dialog behaviour |
Integration context
Permalink to "Integration context"The same registry feeds aria-keyshortcuts on controls and the command palette. The setting in the dialog is the one required by SC 2.1.4 for single-character shortcuts.
For grids, list the navigation keys (arrows, Home, End, Page Up/Down), edit-mode keys (Enter, F2, Escape) and selection keys (Shift+Arrow, Ctrl+A) in their own tables — these are what users most often cannot guess.
Gotchas
Permalink to "Gotchas"? on non-US layouts. On some layouts ? requires Shift plus a different key, or AltGr. Checking e.key === '?' handles this; checking e.code does not.
Nested <kbd>. The HTML spec allows <kbd> inside <kbd> to mean “press these together”. Screen readers ignore the nesting, so include the “+” as text.
Huge dialogs. A help dialog with sixty rows needs headings or captions to navigate. Tables with captions give screen reader users T to jump between groups.
Design system notes
Permalink to "Design system notes"Provide the help dialog as a platform component fed by the shortcut registry, with context groups declared by the components that register shortcuts. The grid component registers its navigation, edit and selection keys under its own context; products add theirs. The dialog then documents exactly what is implemented, per page.
Testing checklist
Permalink to "Testing checklist"FAQ
Permalink to "FAQ"How should keyboard shortcuts be documented accessibly?
In a dialog reachable from a visible button, with one captioned table per context listing the action and its keys. Write keys as words such as “Control + Shift + E” inside kbd elements so screen readers read them correctly.
Should the shortcut list be a table or a list?
A table. Each row relates an action to its keys, and tables let screen reader users read down the Keys column or jump between context groups by caption.
Is the question mark shortcut enough to open the help?
No. Users who do not know the shortcuts cannot know that one either. Provide a visible Keyboard shortcuts button in a predictable place as well.
Should Mac and Windows users see different keys?
Yes. Detect the platform and show Command and Option to Mac users and Control and Alt to others, generated from the same registry so both are always accurate.
Related
Permalink to "Related"- Single-key shortcuts and SC 2.1.4 — the setting the dialog exposes
- aria-keyshortcuts — shortcuts announced on controls
- Table captions — naming each shortcut table