critical priority medium complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

An AccessibilityTokens Dart class (or extension on the existing design token theme) defines the following validated constants: minTouchTarget (48.0dp × 48.0dp), normalTextContrastRatio (4.5), largeTextContrastRatio (3.0), largeTextThresholdSp (18.0 regular / 14.0 bold), minFontSizeSp (12.0), maxFontScaleFactor (2.0), minLineHeightMultiplier (1.4)
An AccessibilityDesignTokenEnforcer utility class exposes static assertion methods: assertTouchTarget(Size actual), assertContrastRatio(Color foreground, Color background, {bool isLargeText = false}), assertFontSize(double fontSizeSp), assertLineHeight(double lineHeightMultiplier)
All assertion methods throw a FlutterError (not a generic AssertionError) in debug mode with a descriptive message including the token name, actual value, required value, and the WCAG criterion violated
All assertion methods are no-ops in release mode — zero performance impact in production
assertContrastRatio() implements the WCAG relative luminance formula correctly (verified by unit tests with known color pairs)
A ContrastSafeColorPalette class or extension provides the app's approved color pairs as named constants — e.g., textPrimaryOnBackground, textSecondaryOnCard — so developers reference named pairs rather than raw Color values
An AccessibilityTokenEnforcerWidget Flutter widget wraps a child and asserts touch target size and layout constraints in debug mode via LayoutBuilder
Integration with the existing design token theme: AccessibilityTokens constants are derived from or override ThemeData values where applicable — no two sources of truth for the same token
A README-style dartdoc comment on AccessibilityDesignTokenEnforcer explains how to add new tokens and how to bypass assertions for intentional exceptions (using a named `// ignore: a11y-touch-target` comment pattern)
All six failing contrast pairs identified in task-011 audit are resolved by replacing raw Color values with ContrastSafeColorPalette named pairs

Technical Requirements

frameworks
Flutter
performance requirements
All assertion methods must be compiled away in release mode (kDebugMode guard or assert() blocks) — zero runtime overhead in production
WCAG relative luminance calculation must complete in < 0.1ms per call — pure math, no I/O
security requirements
No external dependencies introduced — the enforcer is pure Dart/Flutter with no third-party packages
Token constants are compile-time immutable (static const) to prevent runtime mutation
ui components
AccessibilityTokenEnforcerWidget (debug-mode layout validator)
ContrastSafeColorPalette (named color pair constants)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Implement the WCAG relative luminance formula from scratch in pure Dart — it is a well-defined algorithm (linearize sRGB values, then L = 0.2126R + 0.7152G + 0.0722B) and has no dependency. Contrast ratio = (L1 + 0.05) / (L2 + 0.05) where L1 is the lighter luminance. Guard ALL assertion method bodies with `if (!kDebugMode) return;` as the first line — this is more reliable than using assert() which can be compiled away depending on build configuration. For the AccessibilityTokenEnforcerWidget, use LayoutBuilder to get the rendered size of the child and compare against minTouchTarget — trigger the assertion in the layout phase, not paint phase, so the error is caught early.

To avoid false positives on decorative non-interactive widgets, require consumers to explicitly opt in by wrapping interactive widgets, rather than globally wrapping everything. The ContrastSafeColorPalette should be co-located with the existing design token theme file — add it as a static extension or a nested class, not a separate file, to make the relationship clear. Keep the token constants aligned with the AccessibilityTokens used in task-011 tests — these two tasks should reference the same source constants. Once this component exists, the CertificationStatusScreen audit (task-011) should import and use assertContrastRatio() in its tests.

Testing Requirements

Unit tests for contrast math: write golden-value tests for assertContrastRatio() using known WCAG examples — e.g., pure black on pure white = 21:1 (passes), mid-gray #767676 on white = 4.48:1 (barely passes AA normal text), light gray #AAAAAA on white = 2.32:1 (fails). Assert these exact values to lock in the luminance formula. Unit tests for assertion behavior: in debug mode, assert that assertTouchTarget(Size(40, 40)) throws a FlutterError; assert that assertTouchTarget(Size(48, 48)) does not throw. In release mode simulation (override kDebugMode with a test flag or use conditional compilation), assert that the same assertTouchTarget(Size(40, 40)) call does not throw.

Integration tests: place AccessibilityTokenEnforcerWidget around a button with a known-small touch target in a widget test and verify the FlutterError is thrown. ContrastSafeColorPalette tests: assert every named color pair in the palette passes its corresponding contrast ratio assertion — these tests serve as a regression guard against theme changes breaking contrast compliance. Run these tests in CI.

Component
Accessibility Design Token Enforcer
infrastructure medium
Epic Risks (4)
high impact medium prob technical

The error message registry and help content registry both depend on bundled JSON assets loaded at startup. If asset loading fails silently (e.g. malformed JSON, missing pubspec asset declaration), the entire plain-language layer falls back to empty strings or raw error codes, breaking the accessibility guarantee app-wide.

Mitigation & Contingency

Mitigation: Implement eager validation of both assets during app initialisation with an assertion failure in debug mode and a structured error log in release mode. Add integration tests that verify asset loading in the Flutter test harness on every CI run.

Contingency: Ship a hardcoded minimum-viable fallback message set directly in Dart code so the app always has at least a safe generic message, preventing a blank or code-only error surface.

medium impact medium prob dependency

The AccessibilityDesignTokenEnforcer relies on dart_code_metrics custom lint rules. If the lint toolchain is not already configured in the project's CI pipeline, integrating a new linting plugin may cause unexpected build failures or require significant CI configuration work beyond the estimated scope.

Mitigation & Contingency

Mitigation: Audit the existing dart_code_metrics configuration in the project before starting implementation. Scope the lint rules to a separate Dart package that can be integrated incrementally, starting with the most critical rule (hard-coded colors) and adding others in subsequent iterations.

Contingency: Fall back to Flutter test-level assertions (using the cognitive-accessibility-audit utility) to catch violations in CI if the lint plugin integration is delayed, preserving enforcement coverage without blocking the epic.

medium impact low prob technical

WizardDraftRepository must choose between shared_preferences and Hive for local persistence. Choosing the wrong store for the data volume (e.g. shared_preferences for complex nested wizard state) can lead to serialisation bugs or performance degradation, particularly on lower-end Android devices used by some NHF members.

Mitigation & Contingency

Mitigation: Define a clean repository interface first and implement shared_preferences as the initial backend. Profile serialisation round-trip time with a realistic wizard state payload (≈10 fields) before committing to either store.

Contingency: Swap the persistence backend behind the repository interface without touching wizard UI code, which is possible precisely because the repository abstraction isolates the storage detail.

medium impact high prob scope

The AccessibilityDesignTokenEnforcer scope could expand significantly if a large portion of existing widgets use hard-coded values. Discovering widespread violations during this epic would force either a major refactor or a decision to exclude legacy components, potentially reducing the enforcer's coverage and value.

Mitigation & Contingency

Mitigation: Run a preliminary audit of existing widgets using a simple grep for hard-coded hex colors and raw pixel values before implementation begins. Use the results to set a realistic remediation boundary for this epic and log all out-of-scope violations as tracked tech-debt items.

Contingency: Scope the enforcer to new and modified components only (via file-path filters in dart_code_metrics config), shipping a partial but immediately valuable coverage rather than blocking the epic on full-codebase remediation.