The expanded canonicalPairs from T-114 (muted text, status chips, syntax tokens on the code-block surface, panel focus border) made the four named themes fail WCAG-AA. Retuning their palettes to pass would have changed the look users picked them for, so the gate is split instead. `canonicalPairs` shrinks back to the baseline every named theme passes; the new `extendedPairs` carries the stricter set and only runs against themes whose name ends `-hc` or `-cb`. Sibling files (`clide-hc`, `midnight-hc`, `paper-hc`, `terminal-hc`) ship today; the policy lives in D-69 with a back-ref from D-22. Co-Authored-By: Claude <noreply@anthropic.com>
4.5 KiB
4.5 KiB
Accessibility + i18n Decisions
A11y + i18n are Tier-0 contracts, not Tier-6 polish.
D-20: A11y is a Tier-0 contract
- Date: 2026-04-21
- Decision: Every widget primitive wraps its interaction surface in a
Semanticsnode at the point of creation. A11y coverage is a test-time gate (ci/test_a11y.sh), not a post-hoc polish pass.ensureSemantics()fires at app boot; Flutter's semantics tree is always populated. - Rationale: Retrofitting a11y onto a grown UI is what every project that skips this promises to do later and then doesn't. Making it a Tier-0 contract costs one
Semanticsline per primitive and a semantic-coverage test; postponing costs a rewrite. - Cost: Widget authors maintain correct labels; tests reject new primitives without semantics. Enforced by
test/a11y/coverage tests. - Raised by: 2026-04-21 planning.
D-21: i18n is a Tier-0 contract (fframe pattern + locale-fallback chain)
- Date: 2026-04-21
- Decision: All user-facing strings resolve through a namespaced i18n catalogue loader ported from fframe's text-driven pattern, extended with a locale-fallback chain fframe lacks. JSON per locale;
I18n.of(context).t('namespace.key', {vars}). Missing keys resolve down the chain (e.g.en_GB→en→ default), never fail silently; missing at the base locale logs a dev-mode error. - Rationale: Flutter's
intl+ ARB codegen is inflexible for plugin-contributed catalogs (see R-4) — we need per-extension catalogs that merge without a codegen step. fframe's shape fits; its silent-fallback behaviour does not, so we add the chain. - Cost: JSON has no comments and no trailing commas; translation tooling has to accept that. Separate
i18nfacade on every feature. - Raised by: 2026-04-21 planning.
D-22: WCAG-AA contrast gate on bundled themes
- Date: 2026-04-21
- Decision: Every bundled theme must pass a WCAG-AA contrast check on its canonical token pairs (text/background, link/background, focus-ring/background) at test time.
ci/test_a11y.shruns the gate; CI fails on regressions. - Rationale: Themes drift under "looks nicer" tweaks; contrast regressions land silently. Running the gate on every PR is the cheapest insurance. Ran the gate on initial themes — caught one summer-night muted token at 2.81:1 (below AA), fixed before landing.
- Cost: Third-party themes (Tier 6) won't be gated until an extension-time test hook lands. Bundled themes are gated today.
- Raised by: 2026-04-21 planning. Refined by D-69 — the gate's strict pair set only applies to high-contrast variants; named themes keep their published palettes.
D-69: published themes are user contracts; ship -hc variants for a11y
- Date: 2026-05-17
- Decision: The four bundled themes that ship under a recognisable name —
clide,midnight,paper,terminal— are user contracts. Their palette colours (including syntax tokens, status colours, and borderHi) MUST NOT be retuned to satisfy contrast gates. When a stricter contrast check would fail one of them, the fix is one of: (a) ship a sibling theme with-hc(high-contrast) or-cb(colour-blind) in the name and enforce the strict pair set only there, or (b) splitcanonicalPairsinto a baseline set every theme must pass and an extended set that only the-hc/-cbvariants must pass. - Rationale: Users pick
midnightbecause it looks like VS Code,paperbecause it reads as a drafting sheet,terminalbecause of the amber-on-near-black tmux feel. Quietly darkeningpaper's success/warning/info or boostingmidnight'sborderHito pass a WCAG-AA check changes what they got and what they signed up for. A11y is a Tier-0 contract (D-20), but it's served by offering an accessible variant, not by overwriting the aesthetic ones. VS Code itself shipsDefault Dark+and a separateDefault High Contrastfor exactly this reason. - Cost: Two extra theme files per "named" theme when we add a11y variants. The bundled-theme contrast gate (D-22) needs a baseline/extended split so the named themes don't fail the strict pairs.
- Raised by: 2026-05-17 — user intervened mid-T-114 when I had retuned
clide/midnight/paper/terminalpalette entries to satisfy the expandedcanonicalPairs; reverted, decision written, T-114 will follow this rule.