Speech Recognition Accessibility Skill
SkillMediaLoad this skill whenever the project must support speech input or voice control — dictation, "click by voice" grammars, Dragon NaturallySpeaking, Voice Control (iOS/macOS), Voice Access (Android), or any interface where users activate controls by speaking. Under no circumstances let an aria-label override a visible label without keeping the visible text intact and in order. Absolutely always ensure the accessible name contains the visible label, and load alongside keyboard/SKILL.md since speech tools commonly emulate keyboard and pointer input.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Speech Recognition Accessibility Skill skill
What this skill tells your AI
The instructions your AI receives, as published by mgifford/accessibility-skills in skills/speech-recognition/SKILL.md and read by ahel’s review.
Canonical source:
examples/SPEECH_RECOGNITION_ACCESSIBILITY_BEST_PRACTICES.mdinmgifford/ACCESSIBILITY.mdThis skill is derived from that file. When in doubt, the example is authoritative.
Apply these rules when implementing or reviewing any interface that must
support speech input or voice control.
Load alongside keyboard/SKILL.md — speech tools often rely on keyboard
and pointer emulation, so keyboard requirements are a foundation, not a substitute.
Core Mandate
Speech recognition can dictate text, navigate interfaces, activate controls, emulate keyboard input, and operate a pointer. Speech recognition is not the same as speaker recognition — speech recognition interprets words and commands; speaker recognition tries to identify who is speaking.
Core principles:
- Use visible, persistent labels — people must be able to determine what to say without guessing a hidden name
- Match visible labels and accessible names — if a control has visible text, its accessible name must contain that text
- Use native semantics — correct HTML names/roles/states/values/keyboard behavior give speech tools reliable information
- Support more than direct label activation — keyboard emulation, name/number overlays, and pointer emulation vary by product
- Do not require fine pointer control — provide alternatives to dragging, path gestures, and precise spatial interaction
- Accept input from more than key events — dictation, paste, and autocomplete may change a field without firing expected keystrokes
- Provide clear feedback — expose changed state, errors, confirmations, and results visually and programmatically
- Test the supported environments — commands, overlays, and behavior vary by product, platform, and version
Who uses speech input: people with limited movement/strength/dexterity/ reach/endurance; repetitive strain injuries, pain, tremors, or fatigue; people using speech instead of or alongside a keyboard/pointer; people with cognitive/learning disabilities who find dictation easier than typing; temporary or situational limitations; people combining speech with a screen reader, magnifier, or switch. Speech users are not necessarily sighted — do not design or test on the assumption that everyone using speech can see a label, overlay, pointer, focus indicator, or visual result.
Severity Scale (this skill)
| Level | Meaning |
|---|---|
| Critical | A control cannot be activated by any speech-tool method (name, overlay, or keyboard/pointer emulation) |
| Serious | Accessible name doesn't contain the visible label; validation depends only on keydown and silently rejects dictated/pasted input |
| Moderate | Repeated controls are hard to disambiguate; icon-only control has no accessible name |
| Minor | Hidden context inserted before rather than after the visible label; inconsistent localization of names |
Critical: Visible Labels Must Be Contained in the Accessible Name (WCAG 2.5.3)
For a component with visible text or an image of text, the accessible name must contain that visible text — having it at the start of the name is documented best practice, not an additional normative requirement.
<!-- Prefer: native text as the name -->
<button type="submit">Save</button>
<!-- Wrong: accessible name is "Confirm changes", not "Save" -->
<button type="submit" aria-label="Confirm changes">Save</button>
<!-- Acceptable when extra context is genuinely needed -->
<button type="submit" aria-label="Save document">Save</button>
aria-label/aria-labelledby can override names derived from child content
— inspect the computed accessible name rather than assuming every
labeling source combines. Do not use placeholder text as the only label — it
can disappear during dictation and isn't a substitute for a programmatically
associated <label>.
Keep visible words contiguous and in order:
<!-- Good: visible label starts the accessible name -->
<button>Delete <span class="visually-hidden">quarterly report</span></button>
<!-- Avoid: hidden words interrupt the visible label -->
<button aria-label="Delete the quarterly report permanently">Delete report</button>
The second example contains "Delete" and "report" but inserts other words
between them — some speech tools may not match the phrase a user sees. A
hidden prefix (context appearing before the visible word) is not
automatically a 2.5.3 failure if the visible text still appears intact, but
it can still make direct label activation harder — prefer not to put hidden
words before the text a person sees. Put format hints/constraints in an
aria-describedby description, not the name.
Serious: Disambiguate Repeated Controls Without Renaming Them
Repeated labels ("Edit", "Delete", "Read more") can cause a speech tool to present several matches — not automatically a conformance failure, but it increases effort. Prefer specific visible labels when concise:
<a href="/services">Read about services</a>
<a href="/products">Read about products</a>
For compact repeated actions, add contextual text after the visible action:
<li>
Billing address
<a href="/account/billing/edit">Edit <span class="visually-hidden">billing address</span></a>
</li>
Do not make accessible names unique by replacing or reordering the visible label — "Modify billing address" is not a good hidden name for a visible "Edit" link.
Moderate: Icon-Only Controls
Still need an accessible name under WCAG 4.1.2 (2.5.3 doesn't apply when there's no visible text label):
<button type="button" aria-label="Close dialog">
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M6 6 18 18M18 6 6 18"></path>
</svg>
</button>
Persistent visible text is usually more discoverable than an icon alone. A tooltip appearing only on hover/focus is not a persistent visible label — a person must first discover or target the control before it helps. If an icon-only design is retained: use a concise localized name; use a familiar icon consistently; provide adequate target size/spacing; ensure name/number overlays identify it; don't use color or position as the only description; test with the supported speech tools.
Critical: Native Semantics and Keyboard Operation
Speech tools often use accessibility APIs, keyboard emulation, or pointer emulation — native HTML supplies reliable role/name/state/value/focus/ activation behavior for free.
<!-- Wrong: not a reliable button -->
<div class="button" onclick="saveDocument()">Save</div>
<!-- Correct -->
<button type="button" onclick="saveDocument()">Save</button>
Do not publish tables claiming a particular spoken phrase always works for
every role — command grammar differs by speech product, platform, locale,
and version. Correct semantics still improve compatibility and remain
required for other AT. All functionality must be available through a
keyboard interface (unless the function requires path-dependent input) —
speech software can use that keyboard interface as one operating method. For
custom widgets: use the correct role only when no native element suffices;
implement the expected keyboard interaction; expose name/state/properties/
value; update programmatic state when visual state changes; follow an
established APG pattern. See skills/keyboard/SKILL.md.
Serious: Dictation and Text Entry Must Not Depend Only on keydown
Speech dictation may insert, replace, or delete text without producing the
same keydown sequence as a physical keyboard.
// Correct: responds to value changes regardless of input method
search.addEventListener('input', () => updateSuggestions(search.value));
// Wrong: dictation, paste, and autocomplete may bypass this logic
search.addEventListener('keydown', event => updateSuggestions(event.key));
Text-entry controls should: use native inputs/textareas; have persistent
visible labels; expose format instructions and errors programmatically;
accept paste, dictation, autocomplete, and programmatic value changes; allow
editing/correction before submission; avoid short time limits or speech-rate
assumptions; not require a specific punctuation phrase when another valid
value is acceptable; preserve entered content after validation errors. Do
not infer that a lack of keydown means no input occurred — validate the
current value at the appropriate event and again at submission.
Serious: Dragging, Gestures, and Spatial Interaction Need Direct Alternatives
Dragging with a speech-controlled pointer can require several commands and precise timing.
<!-- Reordering alternative -->
<li>
Budget report
<button type="button">Move up <span class="visually-hidden">Budget report</span></button>
<button type="button">Move down <span class="visually-hidden">Budget report</span></button>
</li>
If drag-and-drop is also available, both methods should update the same model and provide confirmation. For sliders, pair the draggable control with a direct entry or step control:
<label for="volume">Volume</label>
<input id="volume" type="range" min="0" max="100" value="50">
<label for="volume-value">Volume percentage</label>
<input id="volume-value" type="number" min="0" max="100" value="50">
WCAG 2.5.7 requires author-created dragging to have a single-pointer
alternative unless essential — a keyboard-only alternative does not
necessarily satisfy this single-pointer requirement. Path-based or
multipoint gestures need a single-pointer alternative under 2.5.1 too, unless
the gesture is essential. See skills/touch-pointer/SKILL.md.
Moderate: Focus, Scrolling, and Overlays
Some speech users emulate keyboard commands or combine speech with another input method — focus order, visibility, and unobscured controls remain important. Sticky headers/cookie notices/chat launchers must not hide a focused component (WCAG 2.4.11).
html { scroll-padding-block-start: var(--sticky-header-height, 4rem); }
:focus-visible { scroll-margin-block: 0.5rem; }
Ensure: focus doesn't move behind an open modal; closing a dialog returns focus to a logical control; direct activation scrolls the target into view when needed; overlays can be dismissed without dragging or precise pointer movement; no essential control is permanently covered at supported viewport sizes. Speech input can be combined with a screen reader, so interaction must not depend on seeing focus or an overlay.
Serious: Expose State, Results, and Error Feedback Programmatically
<button type="button" aria-expanded="false" aria-controls="filters-panel">Filters</button>
<div id="filters-panel" hidden><!-- Filter controls --></div>
Update both aria-expanded and hidden together — similarly for
aria-pressed, aria-checked, selected tabs, and slider values.
<p id="cart-status" role="status" aria-atomic="true"></p>
Use visible text and an appropriate status-message pattern (see
skills/aria-live-regions/SKILL.md) — don't move focus for every
confirmation. For errors needing immediate correction, associate the error
with the field and use the form's established error-summary strategy. Avoid
feedback that is: shown only by color/animation/icon change; available only
on hover; removed before it can be perceived; announced repeatedly for every
dictated character; hidden from accessibility APIs; disconnected from the
control that caused it.
Moderate: Labels, Symbols, and Localization
Use the same human language for visible labels and accessible names —
translate aria-label, hidden context, descriptions, and error messages
with the rest of the interface. Do not normalize every symbol into an
unrelated word — the correct name depends on function: a magnifying-glass
icon used as a control is named "Search," not "Magnifying glass"; a visible
X used only as a close icon should have the function "Close." Test speech
matching in each supported interface and dictation language — a command
working in English may not work after translation.
Product and Platform Variation
Speech tools differ in how they activate a control by name, display name/ number/grid overlays, disambiguate repeated names, emulate keyboard/pointer input, handle embedded frames and shadow DOM, and switch between command/dictation modes. Do not put fixed command phrases, product versions, or compatibility claims into a project standard unless the project actively maintains and retests them.
Record the actual test environment:
Speech input product and version:
Operating system and version:
Browser and version:
Input and interface language:
Command, dictation, or mixed mode:
Viewport, zoom, and text size:
Testing
- Static inspection: identify the visible label; inspect the computed accessible name/role; confirm visible words occur intact and in order in the name; check for hidden text before or inside the visible phrase; verify state/value/description relationships; confirm repeated controls can be distinguished; verify icon-only controls have concise names; verify names and hidden text are translated. Inspect the computed accessibility tree — don't infer the final name by reading attributes independently.
- Keyboard foundation (without a pointer): reach and operate all functionality; follow a logical focus order; open/use/close custom widgets; escape dialogs/menus/popovers; confirm focus stays visible and unobscured; complete drag alternatives.
- Speech input test (per supported environment): activate visibly labeled controls by speaking the visible label using the tool's documented grammar; inspect the tool's name/number overlay for discoverability; distinguish repeated controls; operate icon-only controls and custom widgets; dictate/edit/replace/submit text; trigger and correct validation errors; navigate modals/menus/tabs/disclosures/ autocomplete; complete sorting/reordering/slider/map/canvas tasks through alternatives; confirm state changes and status feedback; repeat critical tasks after zoom/reflow/localization changes. Do not require every control to respond to one literal phrase like "Click [label]" — use the tested tool's documented grammar.
- Test with users who regularly use speech input — a technical pass cannot reveal the full effort of correcting recognition errors, disambiguating controls, or navigating a dense overlay.
Automated checks can detect controls without accessible names, some visible-label-in-name mismatches, invalid/missing roles/states, click handlers on non-interactive elements, and duplicate accessible names — cannot determine whether a speech product actually recognizes a label, whether repeated controls are practically distinguishable, or whether the total number of commands makes a task unreasonably difficult. Treat uncertain automated results as review items, not confirmed failures.
Common Failures
| Failure | Correction |
|---|---|
| Visible "Save" button has hidden name "Confirm changes" | Use "Save," or a name beginning with "Save" |
| Hidden words inserted inside the visible phrase | Keep visible words contiguous and in order |
| Every repeated link is visibly "Read more" | Use specific visible text or append contextual hidden text after it |
| Placeholder is the only field label | Add a persistent, associated <label> |
| Icon-only button has no accessible name | Add a concise name; prefer persistent visible text |
| Tooltip treated as a visible label | Keep the label persistently visible when discoverability matters |
Styled <div> acts as a button | Use <button> or implement the complete custom-widget contract |
Validation runs only on keydown | Respond to input, current values, and submission |
| Reordering requires drag and drop only | Add Move up/Move down or position controls |
| Gesture is the only way to operate a feature | Add simple controls for the same result |
| Sticky content hides the focused control | Adjust layout/scrolling; test at supported sizes |
| State changes only visually | Update programmatic state and provide visible feedback |
| Project documentation promises universal spoken commands | Test and document the actual supported products and locales |
| Testing assumes every speech user can see | Include non-visual and combined AT use |
Definition of Done Checklist
- Every control exposes an accurate name, role, state, and value
- Every visible text label is contained intact and in order in the accessible name
- Visible text appears at the start of the accessible name where practical
- Native HTML used where it can provide the required control
- Custom controls follow supported keyboard and ARIA patterns
- Repeated controls can be distinguished efficiently without renaming them
- Icon-only controls have concise, localized accessible names
- Form fields have visible, programmatically associated labels (not placeholder-only)
- Dictation, paste, autocomplete, editing, and correction update fields correctly
- Validation does not depend only on physical key events
- All functionality available through a keyboard interface
- Dragging and path-based gestures have single-pointer alternatives
- Focus remains logical, visible, and unobscured
- States, validation errors, confirmations, and results exposed programmatically
- Critical tasks tested in supported speech products, browsers, and languages
- Automated results supplemented by manual speech-input testing
Key WCAG Criteria
- 1.3.1 Info and Relationships (A)
- 2.1.1 Keyboard (A)
- 2.4.3 Focus Order (A)
- 2.4.7 Focus Visible (AA)
- 2.4.11 Focus Not Obscured Minimum (AA)
- 2.5.1 Pointer Gestures (A)
- 2.5.3 Label in Name (A) — Serious if visible label not contained in accessible name
- 2.5.7 Dragging Movements (AA)
- 3.3.2 Labels or Instructions (A)
- 4.1.2 Name, Role, Value (A)
- 4.1.3 Status Messages (AA)
WCAG criteria describe outcomes, not product-specific spoken commands — a command failing in one environment may indicate a content defect, a product limitation, a localization issue, or an unsupported combination. Record the environment and investigate before assigning a standards failure.
References
- Full best practices guide
- W3C WAI Speech Recognition Perspective
- WCAG 2.2 Understanding 2.5.3 Label in Name
- WCAG 2.2 Understanding 2.5.7 Dragging Movements
- ARIA Authoring Practices Guide
- Providing Accessible Names and Descriptions
Standards horizon: These rules target WCAG 2.2 AA. Monitor: https://www.w3.org/TR/wcag-3.0/
Signals
- GitHub stars
- 44
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
speech-recognition-mgifford- Source
- github.com/mgifford/accessibility-skills