Skip to content

Accessibility: expose the waveform as a keyboard-operable slider with ARIA #8

@scruffian

Description

@scruffian

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions