Expose the waveform as a keyboard-operable, ARIA slider - #9
Conversation
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
Expose the waveform surface as an ARIA slider: role="slider", focusable
in the tab order, with aria-valuemin/max/now and a readable
aria-valuetext ("0:30 of 2:00") kept in sync on metadata load,
timeupdate, and external setProgress(). Standard slider keys when
focused — arrows (+/-5s), Page Up/Down (+/-10s), Home/End — calling
preventDefault() to avoid page scroll. Works in self mode (seekTo) and
external mode (dispatches waveformplayer:request-seek, like
click-to-seek).
New options: accessibleSeek (default true; opt out to keep prior markup)
and seekLabel (accessible name, falls back to title then 'Seek').
Addresses WCAG 2.1.1 (Keyboard) and 4.1.2 (Name, Role, Value).
Upstreams the accessibility layer the WordPress/Gutenberg team had been
applying externally. Ported onto the current codebase from #9 (authored
against 1.2.1). Resolves #8.
Co-Authored-By: Ben Dwyer <275961+scruffian@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for this, @scruffian — really thorough writeup, and exactly the right call to upstream it rather than keep patching I've landed the functionality natively in v1.7.2 (just tagged). The codebase had moved on a fair bit since you opened this (you were against
Credited you as co-author on the commit (1a... Closing this in favour of the released version. Thanks again. 🙏 |
|
One practical follow-up, @scruffian — when you get a chance, it's worth bumping the pin on your side. It looks like Moving block-library to Heads up on what changed between 1.2.1 → 1.7.x in case it affects your integration:
Happy to help smooth over any rough edges if you hit something during the upgrade — and if it's useful I can put together a short 1.2.1 → 1.7.2 migration note. Thanks again for pushing this upstream. 🙏 |
|
Thanks! |
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 noroleor 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
.waveform-containerasrole="slider", focusable in the tab order (tabindex="0").aria-valuemin/aria-valuemax/aria-valuenowand a readablearia-valuetext(e.g."0:30 of 2:00") in sync on metadata load,timeupdate, and externalsetProgress().preventDefault()(no page scroll) andstopPropagation()so the existing container-level handler doesn't also fire.selfmode it callsseekTo(); inexternalmode it dispatcheswaveformplayer:request-seekwith apercent, exactly like click-to-seek.:focus-visibleoutline for the slider, matching the existing button/marker focus styling.Options
accessibleSeektruefalseto opt out and keep the prior behavior.seekLabelnull'Seek'. (Lets consumers localize the label.)Backwards compatibility
accessibleSeekdefaults totrue, 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 setaccessibleSeek: false.Testing
npm run buildsucceeds; changes are reflected indist/.tabindex/aria-label/aria-valueminare set;aria-valuemax/valuenow/valuetexttracksetProgress(30, 120)→"0:30 of 2:00"; → dispatchesrequest-seekwithpercent = 35/120; End →percent = 1; andaccessibleSeek: falseleaves the waveform with norole. 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.