Summary
The waveform seek area isn't exposed as an accessible, keyboard-reachable control. The player ships keyboard controls (initKeyboardControls), but they only activate after a mouse click and the waveform has no ARIA semantics, so screen-reader and keyboard-only users can't discover or operate seeking. It would be great if the library handled this natively rather than each consumer having to patch it from the outside.
Tested with @arraypress/waveform-player@1.2.1.
Current behavior
In src/js/core.js, initKeyboardControls():
- Sets the player container to
tabindex="-1" and only promotes it to tabindex="0" inside the click handler. Keyboard-only users who navigate with Tab can never focus it.
- The
keydown handler early-returns unless document.activeElement === this.container, so the arrow/space/number/mute shortcuts are unreachable without first clicking.
- The waveform element (
.waveform-container) has no role, no aria-label, and no aria-valuemin/aria-valuemax/aria-valuenow/aria-valuetext. Assistive technology has no way to announce it as a seekable control or report the current position.
- ↑/↓ are mapped to volume rather than seeking, which is unexpected for a control that visually reads as a progress/seek bar.
Why this matters
This fails WCAG 2.1 SC 2.1.1 (Keyboard) and 4.1.2 (Name, Role, Value). For consumers building accessible UIs, the only workaround today is to reach into the library's internals — add role="slider" + ARIA value attributes to .waveform-container, make it focusable, attach a separate keydown listener, and call stopImmediatePropagation() to suppress the library's own handler so seeking doesn't fire twice. That couples consumer code to private DOM structure and event ordering, and breaks easily across versions.
Proposed enhancement
Expose the waveform as a native, accessible slider, owned by the library:
- Make
.waveform-container (or a dedicated seek element) focusable via tabindex="0" and give it role="slider".
- Maintain
aria-valuemin="0", aria-valuemax (duration), aria-valuenow (current time), and a human-readable aria-valuetext (e.g. "0:45 of 3:00"), updated on timeupdate/durationchange/loadedmetadata.
- Support the standard slider keys when focused: ←/→ (and ideally ↑/↓) to seek, Page Up/Page Down for larger steps, Home/End for start/end, calling
preventDefault() to avoid page scroll.
- Allow the accessible name to be set via an option (e.g.
seekLabel / ariaLabel) so consumers can localize it.
An option to opt in/out (defaulting to on) would keep this backwards-compatible for anyone relying on the current behavior.
Contribution
Happy to open a PR implementing the above if that's welcome — we've already built an equivalent layer on top of the library and would prefer to upstream it.
Summary
The waveform seek area isn't exposed as an accessible, keyboard-reachable control. The player ships keyboard controls (
initKeyboardControls), but they only activate after a mouse click and the waveform has no ARIA semantics, so screen-reader and keyboard-only users can't discover or operate seeking. It would be great if the library handled this natively rather than each consumer having to patch it from the outside.Tested with
@arraypress/waveform-player@1.2.1.Current behavior
In
src/js/core.js,initKeyboardControls():tabindex="-1"and only promotes it totabindex="0"inside the click handler. Keyboard-only users who navigate with Tab can never focus it.keydownhandler early-returns unlessdocument.activeElement === this.container, so the arrow/space/number/mute shortcuts are unreachable without first clicking..waveform-container) has norole, noaria-label, and noaria-valuemin/aria-valuemax/aria-valuenow/aria-valuetext. Assistive technology has no way to announce it as a seekable control or report the current position.Why this matters
This fails WCAG 2.1 SC 2.1.1 (Keyboard) and 4.1.2 (Name, Role, Value). For consumers building accessible UIs, the only workaround today is to reach into the library's internals — add
role="slider"+ ARIA value attributes to.waveform-container, make it focusable, attach a separatekeydownlistener, and callstopImmediatePropagation()to suppress the library's own handler so seeking doesn't fire twice. That couples consumer code to private DOM structure and event ordering, and breaks easily across versions.Proposed enhancement
Expose the waveform as a native, accessible slider, owned by the library:
.waveform-container(or a dedicated seek element) focusable viatabindex="0"and give itrole="slider".aria-valuemin="0",aria-valuemax(duration),aria-valuenow(current time), and a human-readablearia-valuetext(e.g."0:45 of 3:00"), updated ontimeupdate/durationchange/loadedmetadata.preventDefault()to avoid page scroll.seekLabel/ariaLabel) so consumers can localize it.An option to opt in/out (defaulting to on) would keep this backwards-compatible for anyone relying on the current behavior.
Contribution
Happy to open a PR implementing the above if that's welcome — we've already built an equivalent layer on top of the library and would prefer to upstream it.