Controls and Roles
Build the player from native HTML controls before adding ARIA. A real <button> already supports keyboard activation and exposes its accessible name to assistive technology. For a toggle control, use aria-pressed when that state helps users; do not make a clickable <div> imitate a button.
- Use semantic buttons for play, pause, stop, and replay, with labels that describe the current action.
- Use a native
<progress>element or a progress bar witharia-valuenowonly when the percentage is actually measurable. - Expose short state changes such as “Loading voice” and “Ready” in a polite status region; avoid announcing every download tick.
Loading and Playback States
A TTS interface has more states than playing and stopped: the model may be downloading, the browser may be preparing it, audio may be generating, and playback may have failed. Tell users which step is happening and keep a retry action available when it fails. If the browser cannot calculate a reliable percentage, show an indeterminate progress indicator and an honest message instead of a made-up number.
<p role="status" aria-live="polite">Preparing the voice model…</p>
<label for="model-progress">Voice model download</label>
<progress id="model-progress" value="42" max="100">42%</progress>
Update the progress value in code as bytes arrive, and announce only meaningful transitions. A screen reader should not have to listen to dozens of percentage changes before the user can continue reading.
Keyboard Navigation
- Use Tab and Shift+Tab to reach every control in a logical order; native buttons already respond to Space and Enter.
- Use Arrow keys for a seek bar or volume slider, and document shortcuts if you add any.
- Provide a visible focus ring and predictable focus order.
- Offer a skip link when the player repeats before the main page content, and do not move focus unexpectedly when a render finishes.
Captions and Transcripts
- Render the spoken text alongside audio; keep it selectable and copyable.
- For long audio, show section headers and timestamps for quick scanning.
- Keep the transcript available when audio is paused, muted, or unavailable; generated speech does not replace text alternatives.
Do not autoplay generated speech. Autoplay can interrupt screen-reader output and makes it difficult for users to choose when to listen. Place the transcript and player controls next to each other, and make errors visible in text rather than relying on a color change or sound alone.
Motion and Feedback
- Respect
@media (prefers-reduced-motion)and avoid flashy animations. - Use non‑color indicators (icons, text) in addition to color cues.
User Preferences
- Persist voice, speed, and volume preferences in local storage.
- Provide a reset option and accessible confirmation messages.
Testing Checklist
- Screen readers: NVDA/JAWS on Windows, VoiceOver on macOS/iOS.
- Keyboard-only navigation through loading, ready, playback, pause, and error states.
- Check focus visibility, text and control contrast, zoom at 200%, and a narrow mobile viewport.
- Test a slow connection and a failed model request so status announcements and retry controls are usable.
Run the checklist with the actual browser and player, not only a static mock-up. A control can look accessible while still losing its focus, changing its name incorrectly, or leaving screen-reader users unaware that a long model load has completed.
Frequently Asked Questions
How do I expose loading to screen readers?
Use a native progress element or a progressbar with a real value when progress is measurable, and announce phase changes through a polite status region without reading every percentage update.
What keys should control the widget?
Use native buttons so Tab, Space, and Enter work by default. Add arrow-key behavior only to controls such as a slider where users expect it.
How do I respect reduced motion?
Use the prefers-reduced-motion media query and avoid flashy transitions for critical flows.
Related Articles
Author: Kokoro Web Team • Last updated 2026-10-15