Implement WCAG 2.2 AA color tokens and semantic label builders
epic-bufdir-report-preview-foundation-task-008 — Build BufdirAccessibilityUtils with three capabilities: (1) a set of WCAG 2.2 AA compliant color tokens (foreground/background pairs that meet the 4.5:1 contrast ratio for normal text and 3:1 for large text) for use in the preview UI; (2) semantic label builder functions that generate descriptive Semantics labels for field rows — e.g., `buildFieldSemanticLabel(String section, String field, String value) -> String` that produces screen-reader-friendly strings; (3) live-region helpers wrapping Flutter's SemanticsService.announce for announcing dynamic value changes to VoiceOver/TalkBack users.
Acceptance Criteria
Technical Requirements
Implementation Notes
Compute WCAG contrast ratios at development time using a contrast checker tool (e.g., WebAIM or Figma plugin) — do not compute at runtime. Declare color pairs as a simple data class `BufdirColorPair { final Color foreground; final Color background; }` with a `const` constructor. Keep BufdirAccessibilityUtils as a pure utility class with only static methods and constants — no state. For announceValueChange, wrap SemanticsService.announce in a try/catch so that test environments without a semantic tree do not crash.
Label strings must be in Norwegian for VoiceOver/TalkBack users (the app's primary language), not English — adjust the format strings to Norwegian.
Testing Requirements
Unit tests for color contrast: for each token pair, compute relative luminance per WCAG 2.1 formula and assert contrast ratio >= 4.5 for normal tokens and >= 3.0 for large-text tokens. Unit tests for label builders: assert output string format for standard input, for empty value, for zero value, for positive delta, for negative delta, for null/empty section or field name (must not throw). Widget test for announceValueChange: mock SemanticsService and assert it is called with the correct message string.
The preview repository depends on aggregated data produced by the Bufdir Data Aggregation feature. If the aggregation RPC schema or Supabase view columns change during parallel development, the repository's typed Dart models will break, causing compile errors or runtime null-dereference failures.
Mitigation & Contingency
Mitigation: Define a shared Dart interface (abstract class) for the aggregated data contract early and have both features code against it. Use Supabase typed generated clients so schema mismatches surface at code generation time rather than runtime.
Contingency: If the aggregation schema changes after the repository is complete, run `supabase gen types dart` immediately, update the repository model, and run repository unit tests before unblocking UI development. Keep a mock data fixture so UI work can continue during the fix.
The BufdirReportStructureMapper must map internal activity category IDs to the exact label strings used on the official Bufdir reporting form. If the mapping is incomplete or uses outdated labels, coordinators will see mismatches when cross-referencing the preview with the paper form, potentially leading to incorrect submissions.
Mitigation & Contingency
Mitigation: Obtain the current Bufdir reporting form PDF directly from Bufdir (Norse Digital Products has an existing Bufdir dialogue). Extract all field labels and section names into a static constants file reviewed by at least one coordinator from NHF or HLF before implementation begins.
Contingency: If incorrect labels are discovered during UAT on TestFlight, update the static constants file and redeploy. Because the mapper is a pure Dart class with no database storage, corrections require no migration — only a new build.