Skip to content

Make the equalizer sliders keyboard and screen reader operable - #1370

Open
captbaritone wants to merge 2 commits into
masterfrom
a11y-sliders
Open

captbaritone wants to merge 2 commits into
masterfrom
a11y-sliders

Conversation

@captbaritone

Copy link
Copy Markdown
Owner

The preamp and the ten EQ band sliders are built on the custom VerticalSlider, a div with pointer handlers. It's a custom component because a rotated <input type="range"> can't have a pixel-perfect handle. As a result these sliders have no role, can't be focused, and ignore the keyboard.

Changes

VerticalSlider takes a new optional label prop. When it's provided, the slider:

  • renders as a focusable role="slider" with aria-label, aria-orientation="vertical", aria-valuemin/max 0–100, aria-valuenow (up is higher) and an optional aria-valuetext
  • handles the keyboard: ArrowUp/Right and ArrowDown/Left move by keyboardStep (default 1%), PageUp/PageDown by 10%, Home goes to the minimum and End to the maximum
  • wraps keyboard changes in the existing onBeforeChange/onAfterChange callbacks. onBeforeChange fires on keydown and onAfterChange on the matching keyup, so the marquee shows "EQ: 60HZ +6.0 DB" while a key is held, just as it does while dragging.

Without label nothing changes. The playlist scrollbar also uses VerticalSlider, and it stays out of the tab order.

Band passes:

  • name: "Preamp", "60 Hz", "1 kHz", …
  • value text: "+6.0 dB", "0.0 dB", "-12.0 dB"
  • arrow step: 1 dB (1/24 of the ±12 dB range)

Rendering is unchanged. The element is the same div, and Webamp's existing #webamp *:focus { outline: 0 } means focus draws nothing unless the opt-in focus indicator (#1368) is enabled.

Testing

  • New VerticalSlider.test.tsx covers:
    • not exposed as a slider without a label
    • correct ARIA attributes with one
    • the arrow, Page, Home and End keys, plus clamping and ignoring other keys
    • before/after callbacks (including no onAfterChange on a stray keyup, e.g. from tabbing in)
    • disabled behavior
  • New bandValueText cases in Band.test.js.
  • tsc, eslint, prettier --check and vitest run (217 passed) pass locally.

@netlify

netlify Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for tourmaline-kringle-c98715 ready!

Name Link
🔨 Latest commit b8b1b99
🔍 Latest deploy log https://app.netlify.com/projects/tourmaline-kringle-c98715/deploys/6ad10ae1ca48c23df511a697
😎 Deploy Preview https://deploy-preview-1370--tourmaline-kringle-c98715.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Size Change: +602 B (+0.12%)

Total Size: 491 kB

📦 View Changed
Filename Size Change
packages/webamp/built/webamp.bundle.min.js 298 kB +302 B (+0.1%)
packages/webamp/built/webamp.lazy-bundle.min.js 193 kB +300 B (+0.16%)

compressed-size-action

@captbaritone
captbaritone force-pushed the a11y-sliders branch 3 times, most recently from 07978ab to 5d6adc5 Compare October 15, 2026 16:59
The preamp and EQ band sliders use the custom VerticalSlider (a div), so they
had no role, could not be focused, and ignored the keyboard.

VerticalSlider now accepts an optional label. When provided, it renders as a
focusable role="slider" with aria-orientation, aria-valuenow (0-100, up is
higher) and aria-valuetext, and handles Arrow keys, PageUp/PageDown, Home and
End. The playlist scrollbar, which also uses VerticalSlider, passes no label
and is unchanged.

Band passes labels like "60 Hz", "1 kHz" and "Preamp", value text like
"+6.0 dB", and a keyboard step of 1 dB.

This branch has not been deployed

No deployments
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.

1 participant