diff --git a/CHANGELOG.md b/CHANGELOG.md index e2dc1ec2b..b84c249a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md ### Fixed +- Scope, Bloom, Sharpen, location search and generated style sliders expose + explicit accessible names. The first-run checkbox retains its native label. - FIRMS records a source as successful only after appending its rows, avoiding contradictory success/failure status if aggregation throws. - Radio country filtering and voice country requests now resolve common English diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index b70fdf86a..0e3f20ad4 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -10,6 +10,13 @@ Launch payloads with missing records now say PAYLOAD DATA UNAVAILABLE. Missing n Updated: August 24, 2026 +## Control names for assistive technology + +Scope, Bloom, Sharpen and location search have explicit accessible names. +Generated style sliders use the same name as their visible parameter label. +The first-run suppression checkbox keeps its native wrapping label, so its +accessible name remains "Don't show this again". Control behavior is unchanged. + ## FIRMS source status The FIRMS proxy records source success after appending its rows. If aggregation diff --git a/index.html b/index.html index e8b0b8ddd..900620de0 100644 --- a/index.html +++ b/index.html @@ -440,7 +440,7 @@ mirrors SCOPE_FEATHER_RATIO_DEFAULT in src/scopeMask.js. Feather softens the black mask edge; label fading is the separate Fade slider. --> - + 11% @@ -460,7 +460,7 @@ Bloom
- + 0%
@@ -470,7 +470,7 @@ Sharpen
- + 49%
@@ -565,7 +565,7 @@ - + diff --git a/src/hudA11y.test.mjs b/src/hudA11y.test.mjs new file mode 100644 index 000000000..c2ef2e6dc --- /dev/null +++ b/src/hudA11y.test.mjs @@ -0,0 +1,30 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; + +const html = readFileSync(new URL('../index.html', import.meta.url), 'utf8'); +const ui = readFileSync(new URL('./ui.js', import.meta.url), 'utf8'); + +// Focused markup guards; actual computed names are checked in Chromium. +// Native labels and hidden inputs must not be treated as missing aria-labels. +test('HUD sliders and location search have descriptive explicit names', () => { + for (const [id, name] of [ + ['scope-feather-slider', 'Scope edge feather'], + ['bloom-intensity-slider', 'Bloom intensity'], + ['sharpen-intensity-slider', 'Sharpen intensity'], + ['location-search', 'Search location by name or coordinates'], + ]) { + const input = html.match(new RegExp(`]*\\bid="${id}"[^>]*>`))?.[0]; + assert.ok(input, `${id} exists`); + assert.ok(input.includes(`aria-label="${name}"`), `${id} has its descriptive name`); + } +}); + +test('the first-run checkbox keeps its native visible label', () => { + assert.match(html, /]*class="first-run-suppress"[^>]*>\s*\s*Don't show this again<\/span>\s*<\/label>/); +}); + +test('generated style sliders use the visible parameter label as their name', () => { + assert.match(ui, /label\.textContent\s*=\s*uMeta\.label;/); + assert.match(ui, /slider\.setAttribute\(['"]aria-label['"],\s*uMeta\.label\)/); +}); diff --git a/src/ui.js b/src/ui.js index 668e752d4..53c0fd5c8 100644 --- a/src/ui.js +++ b/src/ui.js @@ -8930,6 +8930,7 @@ export class StyleManager { const slider = document.createElement('input'); slider.type = 'range'; slider.className = 'param-slider'; + slider.setAttribute('aria-label', uMeta.label); slider.min = uMeta.min; slider.max = uMeta.max; slider.step = uMeta.max <= 1 ? '0.01' : '0.1';