Skip to content

WCAG 2.2 AA audit fixes + dependent-settings gating - #10

Merged
jage9 merged 4 commits into
masterfrom
wcag-2.2-aa-fixes
May 12, 2026
Merged

WCAG 2.2 AA audit fixes + dependent-settings gating#10
jage9 merged 4 commits into
masterfrom
wcag-2.2-aa-fixes

Conversation

@jage9

@jage9 jage9 commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR bundles a WCAG 2.2 AA audit pass on the web UI together with a follow-up round of settings-panel UX improvements (dependent-control gating).

Accessibility (WCAG 2.2 AA)

  • 2.5.8 Target Size (Minimum) — The progress seek slider now exposes a 24 px tall interactive hit area while still rendering a 6 px visual bar via a ::before pseudo-element. Range slider thumbs grew from 18 px to 24 px (webkit + moz). Cue marks on the progress bar were re-centred so they stay aligned in the expanded track.
  • 1.4.11 Non-text Contrast — The badge border on the now-playing card was using --border (#0f3460), which only achieved 1.3:1 against the card background. A new --badge-border token (#4d7eb5) brings that to 3.76:1 on --bg-card, which passes the 3:1 minimum for UI components.
  • 4.1.2 Name, Role, Value — The discovery interval input previously relied on aria-disabled="true", which does not actually block keyboard or pointer interaction. Switched to the native disabled attribute on #disc-every so the input is genuinely inert when discovery is off.
  • 1.3.1 Info and Relationships — Added aria-describedby references to twenty-five settings controls that were missing them, so the existing inline help text is now programmatically associated with its field instead of being purely visual.
  • <summary> / <h2> double announcement — Every <details><summary> previously wrapped its label in <h2>, which screen readers announced once as a heading and once as a disclosure widget. Removed the inner <h2> and reapplied the heading look (1.1 rem, 600 weight, uppercase, letter-spaced, dim foreground) directly via details.card > summary CSS. Section aria-labelledby references now point at the corresponding tab IDs (tab-now etc.) so each tabpanel is still labelled. The EQ group retains its label via id="eq-heading" moved onto its summary.
  • Expand / collapse arrow — The / glyph in the summary ::after is now suppressed from assistive-tech speech using the CSS alt-text syntax content: "\25B8" / "". The disclosure widget already announces expanded / collapsed state, so the glyph is purely visual.
  • Smaller cleanups — Removed the redundant role="main" from <main>, consolidated the duplicate .sr-only class onto .visually-hidden, and dropped the now-dead .sr-only block.

Settings UX — dependent-control gating

  • The discovery Every N tracks row now collapses when Inject distant tracks is unchecked, matching the existing mood-arc-hours pattern.
  • The Use flats row and its description now collapse when Key notation is not set to Musical. This required extending show-when.js to recognise a selectId=value form on data-show-when, in addition to the existing checkbox form.
  • A global [hidden] { display: none !important } rule was added to app.css. Without it, .settings-row { display: flex } and .setting-desc { display: block } outranked the user-agent [hidden] { display: none } rule on specificity, so JS-set hidden attributes had no visual effect — the dependent rows stayed visible even though the JS logic was correct.
  • applyShowWhen() is now called at the end of applySettingsState. Previously the delegated change listener was the only path that re-evaluated visibility, but WebSocket state pushes set select values programmatically (.value = ...), which does not fire change, so the show-when state was effectively frozen to whatever the HTML defaults were at page load.
  • The stale help-text sentence referencing a Discovery button on the Now Playing tab was removed (the button was hidden a while back). The Use flats label was trimmed from "Use flats (musical mode)" to just "Use flats", and its description no longer carries the redundant "Only used when key notation is set to Musical" sentence.

Test plan

  • Open the Settings tab in Firefox. Toggle Key notation between Camelot and Musical: the Use flats row and its description should show in Musical and hide in Camelot.
  • Toggle Inject distant tracks off: the Every N tracks row should disappear.
  • Toggle Mood arc off: the Mood arc length (hours) row should disappear.
  • Toggle Enable voice liners off: the Trigger, Mix, and Library fieldsets should all collapse together.
  • While key notation is set to Camelot, click another setting to force a WebSocket state push. The Use flats row should stay hidden (regression check for the new applyShowWhen() call).
  • Move NVDA focus over the progress slider: it should announce role=progressbar with a meaningful aria-valuetext, and the hit target should be at least 24 px tall.
  • Inspect the now-playing badges in DevTools: they should have a visible #4d7eb5 border with at least 3:1 contrast against the card background.
  • Tab through the Settings panel with NVDA: every control should announce its associated description after its label.

Generated with Claude Code.

jage9 and others added 4 commits May 11, 2026 03:24
2.5.8 Target Size
- Progress seek slider: expand interactive hit area to 24 px via
  transparent height; 6 px visual bar rendered by ::before pseudo-element
- Range slider thumbs: 18 px → 24 px (webkit + moz)
- Cue marks re-centred with top:50%/translateY(-50%) in expanded track

1.4.11 Non-text Contrast
- Badge border: var(--border) (#0f3460, 1.3:1) → var(--badge-border)
  (#4d7eb5, 3.76:1 on --bg-card); new CSS variable added to :root

4.1.2 Name/Role/Value — aria-disabled misuse
- #disc-every initial HTML state: aria-disabled="true" → disabled attribute
- app.js + settings-panel.js: setAttribute("aria-disabled") →
  discEvery.disabled = !on; so native input correctly blocks interaction

1.3.1 Info and Relationships
- Added aria-describedby to every settings control that lacked it:
  preset-select, transition-select, harmonic-mode, dj-beatmatch,
  dj-phrase-align, dj-outro-intro, pb-eq-duck, pb-anchor-seed,
  pb-show-lyrics, pb-replaygain, pb-daypart, pb-mood-arc,
  pb-mood-arc-hours, pb-import-cues, pb-beat-sync-fx, pb-key-sync-fx,
  pb-beatmatch-on-skip, audio-device, pb-transition-mode, pb-crossfade,
  pb-fade-in, bpm-lo, bpm-hi, disc-enabled, disc-every

<summary><h2> double-announcement
- Removed <h2> from every <details> summary; heading appearance (1.1 rem,
  600 weight, uppercase, letter-spacing, text-dim colour) applied directly
  to details.card > summary via CSS
- details/section aria-labelledby attrs updated: panels now reference tab
  button IDs (tab-now, tab-settings, tab-queue, tab-library) rather than
  the removed heading IDs; EQ group retains its reference via id moved to
  <summary id="eq-heading">
- tabs.js focus management: falls back to first <summary> when no <h2>
  exists in the newly-activated panel

Expand/collapse arrow AT suppression
- details.card > summary::after content uses CSS alt-text syntax
  content: "\25B8" / "" so the ▸/▾ glyph is hidden from screen reader
  speech (expanded/collapsed state already announced by disclosure widget)

Misc
- Remove redundant role="main" from <main>
- Replace duplicate .sr-only with .visually-hidden on #hist-goto label
  and #sr-status; remove the now-dead .sr-only CSS block
- <section> tabpanel labels consistently use tab button IDs throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e help text

- Add data-show-when="disc-enabled" to the Every-N-tracks row so it
  collapses when the checkbox is unchecked (matches mood-arc-hours pattern)
- Remove outdated sentence referencing a Discovery button on the Now
  Playing tab; btn-discovery is display:none and no longer surfaced

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extend show-when.js to support `data-show-when="selectId=value"` so
elements can be gated on a specific select option, not just a checkbox.
Single delegated listener now covers both input[type=checkbox] and select.

Wire key-prefer-flats row + its desc to data-show-when="key-notation=musical"
so both collapse when Camelot notation is active.  Label trimmed from
"Use flats (musical mode)" to "Use flats" and description drops the now-
redundant "Only used when key notation is set to Musical" sentence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n WS push

- Add `[hidden] { display: none !important }` so the `data-show-when`
  mechanism actually hides .settings-row (flex) and .setting-desc
  (block) targets.  Author display rules outrank the UA
  `[hidden] { display: none }` on specificity, which let the Use-flats /
  mood-arc-hours / disc-every / liner-fieldset rows stay visible even
  with the `hidden` attribute correctly set.
- Call `applyShowWhen()` at end of `applySettingsState` so WS state
  pushes that set select values programmatically (key-notation,
  pb-pick-mode, etc.) re-evaluate visibility.  The delegated `change`
  listener never fires for programmatic .value writes, so without this
  the show-when state was frozen to whatever the HTML defaults were at
  page load.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jage9
jage9 force-pushed the wcag-2.2-aa-fixes branch from 7bec92f to 3ac342e Compare May 11, 2026 07:26
@jage9
jage9 merged commit 5fe5768 into master May 12, 2026
3 of 4 checks passed
@jage9
jage9 deleted the wcag-2.2-aa-fixes branch May 12, 2026 07:34
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