fix(a11y): add accessible names to hud sliders and inputs

Add explicit aria-label attributes to unlabelled interactive controls in index.html and dynamic parameter sliders in src/ui.js, fulfilling WCAG 2.1 Success Criterion 4.1.2 (Name, Role, Value).

- Add aria-label to #scope-feather-slider, #bloom-intensity-slider, #sharpen-intensity-slider, #location-search, #scene-import-file, and first-run suppression checkbox.
- Set aria-label to uniform label on dynamic style parameter sliders in ui.js.
- Add unit test suite (src/hudA11y.test.mjs) verifying accessible names across all inputs in index.html and dynamic slider construction.

Signed-off-by: Kushagra Kumar <[email protected]>
This commit is contained in:
Kushagra Kumar committed 2026-09-09 15:43:58 +05:30
1 parent 759652207f
commit 9e92ab6f9b
3 files changed
+59 -6

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import path from 'node:path';
test('index.html: all interactive input elements have accessible names (WCAG 4.1.2)', () => {
const htmlPath = path.resolve(process.cwd(), 'index.html');
const html = readFileSync(htmlPath, 'utf8');
// Match all <input ... /> tags
const inputMatches = [...html.matchAll(/<input\b([^>]*)\/?>/g)];
assert.ok(inputMatches.length > 5, 'expected to find interactive input elements');
for (const match of inputMatches) {
const attrs = match[1];
const hasAriaLabel = /aria-label=["'][^"']+["']/.test(attrs);
const hasAriaLabelledby = /aria-labelledby=["'][^"']+["']/.test(attrs);
const idMatch = attrs.match(/\bid=["']([^"']+)["']/);
const id = idMatch ? idMatch[1] : null;
let hasAssociatedLabel = false;
if (id) {
// Check if there is a <label for="id">
const labelForRegex = new RegExp(`<label\\b[^>]*\\bfor=["']${id}["']`, 'i');
hasAssociatedLabel = labelForRegex.test(html);
}
const hasAccessibleName = hasAriaLabel || hasAriaLabelledby || hasAssociatedLabel;
assert.ok(
hasAccessibleName,
`input tag "${match[0]}" must have an accessible name (aria-label, aria-labelledby, or label for)`
);
}
});
test('index.html: HUD sliders and controls have descriptive accessible labels', () => {
const htmlPath = path.resolve(process.cwd(), 'index.html');
const html = readFileSync(htmlPath, 'utf8');
assert.match(html, /id="scope-feather-slider"[^>]*aria-label="Scope edge feather"/);
assert.match(html, /id="bloom-intensity-slider"[^>]*aria-label="Bloom intensity"/);
assert.match(html, /id="sharpen-intensity-slider"[^>]*aria-label="Sharpen intensity"/);
assert.match(html, /id="location-search"[^>]*aria-label="Search location by name or coordinates"/);
assert.match(html, /data-first-run-suppress[^>]*aria-label="Do not show this message again on startup"/);
});
test('ui.js: dynamic style parameter sliders set aria-label', () => {
const uiPath = path.resolve(process.cwd(), 'src/ui.js');
const uiJs = readFileSync(uiPath, 'utf8');
assert.match(uiJs, /slider\.setAttribute\(['"]aria-label['"],\s*uMeta\.label\)/);
});