Files
meshcore-analyzer/tests/a11y-allowlist.yaml
T
735d9eb516 fix(#1715): dark-theme role swatches via per-theme CSS tokens (#1757)
## Summary

Dark-theme variants of the neighbor-graph role swatches (`#ngRoleChecks`
labels on `/analytics?tab=neighbor-graph`) still failed WCAG AA after
#1720's light-theme fix because the swatches used inline
`style="color:#..."` from `customize.js` `DEFAULTS.nodeColors`
(palette-700) — bypassing the theme tokens entirely.

Measured before:

| Role | Color | vs `#1a1a2e` (dark) |
|---|---|---|
| repeater | `#dc2626` | 3.53:1 ❌ |
| companion | `#2563eb` | 3.30:1 ❌ |
| observer | `#8b5cf6` | 4.02:1 ❌ |

## Fix

- Defines `--role-{repeater,companion,room,sensor,observer}` in `:root`
(palette-700, ≥4.5:1 on white) and overrides them in both dark blocks
(`[data-theme="dark"]` + the `@media (prefers-color-scheme: dark)`
mirror) with palette-400/500 shades that clear AA on `#1a1a2e`.
- Refactors the neighbor-graph swatch DOM in `public/analytics.js` from
inline `style="color:${hex}"` to class-based `<span class="role-swatch
role-swatch--{role}">`, with matching CSS rules that read the tokens.
- Removes all 5 `#1715` entries from `tests/a11y-allowlist.yaml` per the
issue's acceptance criteria.

After:

| Role | Light (vs `#fff`) | Dark (vs `#1a1a2e`) |
|---|---|---|
| repeater | `#dc2626` 4.83:1 | `#ef4444` 4.53:1 |
| companion | `#2563eb` 5.17:1 | `#3b82f6` 4.64:1 |
| room | `#15803d` 5.02:1 | `#16a34a` 5.18:1 |
| sensor | `#b45309` 5.02:1 | `#d97706` 5.35:1 |
| observer | `#7c3aed` 5.70:1 | `#a78bfa` 6.27:1 |

## Tests

- `test-a11y-1715-dark-role-swatches.js` — CSS-driven WCAG AA probes for
the 5 per-theme `--role-*` tokens plus markup invariants (no inline
color span; class names present in `#ngRoleChecks` block).
- Red commit: `a09ec21c` — fails on assertion with 12
below-threshold/markup probes.
- Green commit: `f87dcd64` — all probes PASS.
- `tests/a11y-allowlist.yaml` shed all 5 entries; the umbrella
`test-a11y-axe-1668.js` (CI) is now the live-browser net for those
cells.

## Preflight overrides

- `check-xss-sinks.sh` flags `public/analytics.js:2502` (label
"observer" appears in an `innerHTML=\`tpl\`` line). The flagged token is
a hardcoded literal string — no user-controlled data flows into that
template. No template content changed in this PR; the flag is
preexisting noise from the heuristic scan and the gate ultimately marks
✅ pass.

Fixes #1715

---------

Co-authored-by: clawbot <clawbot@kpa.local>
Co-authored-by: clawbot <bot@example.com>
2026-06-20 16:15:58 -07:00

38 lines
2.0 KiB
YAML

# a11y allowlist — operator-flagged false-positives / temporary suppressions.
#
# Governance (M5 0-violation baseline policy, re-stated here per PR #1711):
# - The CI gate's net floor is ZERO. Every entry in this file CONSUMES
# one notional "I owe an a11y fix" credit.
# - No wildcards. Each entry MUST pin a specific node via either:
# selector: '...' # exact axe target string (legacy)
# selector_pattern: '...' # JS RegExp; requires count_max: N
# - selector_pattern + count_max (PR #1711, finding #1): when a single
# class of nodes can repeat under live data (table rows, fleet lists),
# use a pattern with an EXPLICIT cap. If matches exceed count_max,
# the gate FAILS so unbounded growth re-trips CI for a human review.
# - Every entry MUST cite a GH issue # tracking the underlying fix.
# - expires_at: YYYY-MM-DD is LOAD-BEARING. expires_at <= today is a
# HARD FAIL (filterAllowlist throws). Do NOT soften to a warning —
# the harness relies on this to prevent "sed forward" rot.
# - strict_unused (PR #1711, finding #4): when set to true, any entry
# that matches NOTHING during a CI run FAILS the gate. Default WARN.
# Env override: A11Y_STRICT_UNUSED=1 promotes WARN → FAIL.
# - Rolling expiry: do NOT all share one date. New contributions should
# stagger (e.g. +30d / +60d from authoring date) so the gate's
# re-trip cadence is steady, not cliff-shaped.
#
# Match semantics: route + rule + (selector == target OR selector_pattern.test(target)).
# Substring matching is REFUSED (`.btn` never suppresses `.btn-primary`).
#
# Entry shape:
# - route: '/analytics?tab=X'
# selector: '.foo' # OR selector_pattern: '^\.foo:nth-child' + count_max: 50
# rule: color-contrast
# issue: 1234
# expires_at: 2026-09-11
strict_unused: false
# (issue #1715 entries removed by the dark-theme role-swatch fix — see
# test-a11y-1715-dark-role-swatches.js for the CSS-driven regression gate)