Skip to main content

Accessibility — requirements

Epic — release gate (floor) + differentiator (panel). MVP-1 requirements for the a11y floor baked into every DS atom (see the feature architecture). These are pass/fail launch conditions, audited each release. The per-member preferences panel (reduced-motion toggle, text scale, high-contrast, dyslexia font, focus mode) is MVP-2 — out of scope here beyond the floor. Accessibility is a ⭐ differentiator: Rewhaven's audience is neurodivergent and predictable, low-overwhelm UI is therapeutic, not cosmetic.

TypeNon-functional ⭐
Layer (build approach)Frontend — DS prefs + a11y floor
RICER 7 × I 2 × C 80% / E 3 = 3.7 · Tier MVP-2 (floor = MVP-1)
KPI (summary)WCAG AA pass %; a11y-pref adoption — see Success criteria
Traces tofeature accessibility · C4 design-system · prioritization · visual-timers
Depends ondesign_system (one token source DsTheme; a11y floor in every atom)

Success criteria (definitive KPI)

Success = 100% of DS atoms pass the automated a11y floor (contrast AA, minimum hit-target, semantics) AND zero P1 a11y defects — every release; manual screen-reader pass on core flows green.

These gates hold at 100%, verified at every release:

  • Contrast: every color pair in DsTheme — dark (default) and light — meets WCAG 2.2 AA: 4.5 : 1 normal text; 3 : 1 large text and UI components.
  • Hit-targets: every tappable DS atom provides a 48 × 48 dp minimum area; no exceptions for icon-only controls.
  • Semantics: every DS atom carries Semantics label, role, and state; no unlabelled interactive widget reaches a release build.
  • Reduced-motion: MediaQuery.disableAnimations is honored app-wide; no motion token bypasses the flag.
  • Focus order: switch-access and keyboard traversal is logical on every screen; no focus trap without an explicit escape.
  • Screen reader (SR): a manual VoiceOver + TalkBack pass on four core flows (onboarding, today-chores, token wallet, settings) is green before every release.
  • Scalable text: no content truncates or overflows at 200% system font scale.

Window: every release is a gate — no ramp. A single P1 defect (contrast failure, missing semantic label, broken SR flow) fails the release.

Measurement & audit

CheckTriggerMechanism
Contrast AA (both themes)CI — every PRToken-contract tests assert color-pair ratios
Hit-target sizeCI — every PRAtom widget tests assert size >= 48×48 dp
Semantics / labelsCI — every PRSemanticsController assertions on every atom story
Reduced-motion gateCI — every PRWidget test with disableAnimations: true; assert no frame delay
Screen-reader passPre-release manualVoiceOver (iOS) + TalkBack (Android) on 4 core flows
Scalable-text overflowPre-release visual regressionWidgetbook screenshots at textScaleFactor 1.0 / 1.5 / 2.0
A11y CI gateCI dashboardFail = block merge; zero tolerance on contrast / semantics / hit-target

Telemetry privacy model: any future a11y aggregate telemetry = event-type + coarse count only; no child identity, no session content, never marketing; internal-BI only. No a11y preference data is collected until MVP-2 preferences exist.

Scope

The a11y floor applies to every atom, molecule, and template in packages/design_system/ and every app screen that composes them, across both dark (default) and light themes. This epic concerns infrastructure-level accessibility: correct tokens, semantics, sizing, motion behavior, and a green SR pass on core flows.

Per-member preferences — the levers that let each household member tune the a11y experience to their neurodivergent profile — are MVP-2. The floor established here makes those preferences non-breaking additions rather than reworks.

Non-functional requirements

NFR-A11Y-1 — Semantic labels and roles on every DS atom

Priority: P1 · Status: 🔨 to build Statement. Every interactive and informational DS atom carries Semantics annotations — label, role, and state — so screen readers announce correctly without app-layer patches. AcceptanceGiven any DS atom in a widget test When SemanticsController is queried Then every tappable has a non-empty label, a correct role (button, checkbox, image, etc.), and state flags match the visual state.

NFR-A11Y-2 — Minimum hit-target size (48 × 48 dp)

Priority: P1 · Status: 🔨 to build Statement. Every tappable DS atom — including icon-only controls, chip closers, and navigation items — presents at least a 48 × 48 dp tappable area via padding or touch-slop, supporting motor-impaired and neurodivergent users. AcceptanceGiven any tappable DS atom When measured in a widget test Then tappableSize.width >= 48 && tappableSize.height >= 48; CI fails the PR on any regression.

NFR-A11Y-3 — WCAG AA contrast in both themes

Priority: P1 · Status: 🔨 to build Statement. All text and interactive-state color pairs in DsTheme meet WCAG 2.2 AA in dark (default) and light themes; no DS atom hard-codes a color outside DsTheme. AcceptanceGiven the token-contract test suite runs in CI When all ColorToken pairs are evaluated Then every pair passes the AA ratio; any hardcoded color causes a test failure.

NFR-A11Y-4 — Reduced-motion honored app-wide

Priority: P1 · Status: 🔨 to build Statement. The app forwards MediaQuery.disableAnimations to DsTheme's motion_tokens; every DS atom that animates respects the flag — no motion plays when it is set. Acceptance

  • Given disableAnimations = true When any motion-bearing DS atom renders Then the animation completes instantly (≤ 1 frame) or crossfades in ≤ 100 ms.
  • Given the CI widget test suite with disableAnimations: true When run Then no AnimationController produces a visible frame delay.

NFR-A11Y-5 — Logical focus order; no unintended focus traps

Priority: P1 · Status: 🔨 to build Statement. Tab and switch-access traversal follows reading-logical order on every screen; no modal, sheet, or overlay traps focus without an announced escape route. AcceptanceGiven switch-access or keyboard navigation When traversing any screen Then focus visits every interactive element in top-to-bottom, left-to-right order (or explicit FocusOrder); no dead ends.

NFR-A11Y-6 — Screen-reader pass on core flows

Priority: P1 · Status: 🔨 to build Statement. A structured manual SR pass (VoiceOver iOS + TalkBack Android) on onboarding, today-chores, token wallet, and settings is green before every release: no unlabelled controls, broken reading order, or silent interactive elements. AcceptanceGiven the pre-release SR checklist is executed on a physical device When all four flows are traversed Then zero P1 findings (unlabelled tappable, wrong role, broken order, inaccessible modal).

NFR-A11Y-7 — Scalable text — no truncation or overflow at 200% scale

Priority: P1 · Status: 🔨 to build Statement. Every DS atom that displays text renders correctly at textScaleFactor 1.0, 1.5, and 2.0 — no truncation without a full-text Tooltip or semantic label, no pixel overflow, no layout collapse. AcceptanceGiven Widgetbook visual-regression screenshots at 1.0 / 1.5 / 2.0 When compared to baseline Then zero new overflow indicators and no unintended maxLines truncation without a full-text alternative.

Architecture considerations

  • One token source (DsTheme) is the enforcement point. Contrast ratios, sizing minimums, and motion-duration tokens are defined once; fixing a token fixes every atom. No DS atom may hard-code a color, size, or duration outside DsTheme.
  • Reduced-motion is a first-class motion_tokens concern, not an ad-hoc widget check. DsTheme exposes a reducedMotion flag derived from MediaQuery.disableAnimations; every animating atom branches on it.
  • Semantics live in the atom, not the screen. App screens may augment (add a dynamic value to a static label) but must never patch missing labels; the DS atom owns its Semantics wrapper.
  • Flutter a11y test utilities (SemanticsController, tester.getSemantics) enable CI-level semantic contract tests without a real device or SR daemon.
  • Visual regression at textScaleFactor — Widgetbook golden screenshots at three scale factors catch overflow regressions before human QA.
  • No a11y PII: any future adoption telemetry is a coarse aggregate (event-type + count); no child identity — consistent with the internal-BI model in Analytics.

Design work (ahead of build)

  • A11y token decisions — explicit AA contrast ratios per text size, minimum tap-target values in the spacing token scale, and focus-ring color tokens for both themes — must be set in DsTheme before atom build begins.
  • Reduced-motion variants — for each motion-heavy atom (token-award celebration, timer countdown, progress indicator), a reduced-motion design must be spec'd: instant cut or 100 ms crossfade, and what the final static state looks like.
  • Semantics conventions — a DS-level convention document: how labels are worded per atom type, which atoms use MergeSemantics, how state (checked, expanded, selected) is announced; written before atoms are built.
  • Pre-release SR checklist — the structured VoiceOver + TalkBack acceptance script for the four core flows, authored alongside the atom specs so QA has deterministic pass/fail criteria.

Decisions (resolved for MVP-1)

Resolved — see the MVP-1 decisions log for the canonical record, rationale, and status legend (✅ decided · ⚖️ counsel confirms · 🔜 MVP-1.x).

  • D-A11Y-1 — Contrast target.AAA body-text contrast (7:1) in the dark theme; AA (4.5:1) floor everywhere else and in the light theme (anchor). Dark-theme body tokens set to AAA.
  • D-A11Y-2 — Reduced-motion style.Crossfade ≤ 100 ms when disableAnimations is true (not instant) — a soft fade reads as intentional. Encode as motion_tokens.reducedMotionDuration = 100ms.
  • D-A11Y-3 — textScaleFactor ceiling. ✅ Formally support up to 200% (Android 14 cap) with no layout breakage; best-effort to 320% (iOS max) without an overflow guarantee. Layout constraints target 200%.
  • D-A11Y-4 — Focus-ring token. ✅ A dedicated --focus-ring token = a high-contrast accent (calm-dusk accent at full chroma), distinct per theme (lighter ring on dark, darker on light), ≥ 3:1 against adjacent colours.

Out of scope (MVP-1)

  • Per-member a11y preferences panel (reduced-motion toggle, text scale slider, high-contrast mode, dyslexia font, focus mode) → MVP-2; the floor here makes those preferences non-breaking additions.
  • AAA contrast as a release gate → aspirational target pending the open decision above; AA is the MVP-1 gate.
  • Dyslexia font switching (OpenDyslexic or licensed alternative) → MVP-2; Atkinson Hyperlegible body font provides a baseline floor.
  • Automated E2E screen-reader testing (Appium + Accessibility Service) → MVP-2; the manual SR pass is the MVP-1 gate.
  • Localized a11y labels → MVP-2; see Localization & i18n.
  • A11y adoption telemetry → MVP-2 when preferences exist; no preference data is collected in MVP-1 beyond the standard internal-BI gate.