Files
clide/decisions/accessibility.md
T
jpmschweitzerandClaude 6e6546fe32 clean-house sweep: anchor drift, app/-prefix paths, D-59 sunset
Fixes 3 substring-truncated cross-reference anchors so they match
the full heading slug:
- D-3 link in architecture.md
- D-40 link in process.md (heading gained the [SUPERSEDED] tag)
- Q-15 link in questions-process.md

Strips the legacy `app/` prefix from path references in 5 files —
the dirs were flattened to repo root in the Flutter rebuild
(D-56). Three "was `app/...`" historical references in D-5 and
D-56 are deliberately preserved as record of the dissolution.

Adds an inline (tracked in T-88) note to D-59 so the
"must track dugite-native releases for security updates" intent
is wired to a backlog item — RULE-SUNSET-WITHOUT-TICKET would
otherwise keep flagging it on every sweep.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 16:40:38 +02:00

29 lines
2.5 KiB
Markdown

# 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 `Semantics` node 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 `Semantics` line 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](rejected.md#r-4-flutter-intl-and-arb-codegen)) — 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 `i18n` facade 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.sh` runs 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.
---