Skip to content

Expose the waveform as a keyboard-operable, ARIA slider#9

Open
scruffian wants to merge 2 commits into
arraypress:mainfrom
scruffian:a11y/seek-slider
Open

Expose the waveform as a keyboard-operable, ARIA slider#9
scruffian wants to merge 2 commits into
arraypress:mainfrom
scruffian:a11y/seek-slider

Conversation

@scruffian
Copy link
Copy Markdown
Contributor

Summary

Makes the waveform seek surface accessible to keyboard and assistive-technology users. Closes #8.

Today the seek area is mouse-only: the container is tabindex="-1" until clicked, and the waveform has no role or ARIA value attributes, so keyboard-only and screen-reader users can't discover or operate seeking (WCAG 2.1.1 Keyboard, 4.1.2 Name/Role/Value).

What this does

  • Marks .waveform-container as role="slider", focusable in the tab order (tabindex="0").
  • Keeps aria-valuemin/aria-valuemax/aria-valuenow and a readable aria-valuetext (e.g. "0:30 of 2:00") in sync on metadata load, timeupdate, and external setProgress().
  • Handles the standard slider keys when focused — / and / (±5s), Page Up/Page Down (±10s), Home/End — calling preventDefault() (no page scroll) and stopPropagation() so the existing container-level handler doesn't also fire.
  • Works in both audio modes: in self mode it calls seekTo(); in external mode it dispatches waveformplayer:request-seek with a percent, exactly like click-to-seek.
  • Adds a :focus-visible outline for the slider, matching the existing button/marker focus styling.

Options

Option Type Default Description
accessibleSeek boolean true Expose the waveform as a keyboard-operable ARIA slider. Set false to opt out and keep the prior behavior.
seekLabel string null Accessible name for the slider. Falls back to the track title, then 'Seek'. (Lets consumers localize the label.)

Backwards compatibility

accessibleSeek defaults to true, so the waveform becomes a tab stop with slider semantics by default — this is the point of the fix. The existing click-to-focus container shortcuts are untouched. Anyone who needs the old markup can set accessibleSeek: false.

Testing

  • npm run build succeeds; changes are reflected in dist/.
  • Verified with a jsdom harness (external mode, so no real audio/canvas decoding needed): slider role/tabindex/aria-label/aria-valuemin are set; aria-valuemax/valuenow/valuetext track setProgress(30, 120)"0:30 of 2:00"; dispatches request-seek with percent = 35/120; Endpercent = 1; and accessibleSeek: false leaves the waveform with no role. All assertions pass.

The repo has no existing test suite (and instantiating the player under test requires a canvas stub), so I verified via the harness above rather than adding a framework. Happy to adjust the approach, key bindings, step sizes, or option names to match your preferences.

Context: we (WordPress/Gutenberg) currently layer this behavior on top of the library from the outside by reaching into .waveform-container and suppressing the internal keydown handler. We'd much rather rely on it natively — hence this PR.

scruffian added 2 commits June 5, 2026 14:53
The waveform seek surface was only operable by mouse: the container is
tabindex="-1" until clicked, and the waveform had no role or ARIA value
attributes, so keyboard-only and screen-reader users couldn't discover
or operate seeking (WCAG 2.1.1, 4.1.2).

Mark .waveform-container as role="slider", make it focusable, keep
aria-valuemin/max/now/valuetext in sync with playback, and handle the
standard slider keys (Arrow/Page/Home/End) to seek. Works in both self
and external audio modes. New options: accessibleSeek (default true,
opt-out) and seekLabel (accessible name, falls back to the track title).

Closes arraypress#8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant