critical priority medium complexity infrastructure pending infrastructure specialist Tier 0

Acceptance Criteria

BufdirAccessibilityColorTokens exposes named foreground/background Color pairs: at minimum `fieldRowNormal`, `fieldRowEmpty`, `fieldRowHighlighted`, `sectionHeader`, `deltaPositive`, `deltaNegative`
Every color pair meets WCAG 2.2 AA: 4.5:1 contrast ratio for normal text sizes (<18pt / <14pt bold) and 3:1 for large text
Color tokens are declared as `const` and integrate with the app's existing design token system — they do not override global theme colors
`buildFieldSemanticLabel(String section, String field, String value)` returns a human-readable English string in the format: `'<section>: <field>, value <value>'` — suitable for VoiceOver/TalkBack
`buildEmptyFieldSemanticLabel(String section, String field)` returns `'<section>: <field>, no data reported'`
`buildDeltaSemanticLabel(String field, num delta)` returns `'<field> changed by <+/->delta compared to prior period'`
`announceValueChange(String message, BuildContext context)` calls `SemanticsService.announce(message, TextDirection.ltr)` and is safe to call from any widget callback
All functions handle null/empty inputs gracefully without throwing

Technical Requirements

frameworks
Flutter
Dart
apis
Flutter Semantics API (SemanticsService.announce, Semantics widget)
Flutter accessibility APIs (VoiceOver/TalkBack)
data models
BufdirAccessibilityColorTokens
performance requirements
Color token lookup is O(1) — const values, no runtime computation
announceValueChange must not block the UI thread
security requirements
Semantic labels must not expose raw internal IDs or database keys — use human-readable field names only
ui components
Semantics widget wrapper for field rows

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

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.

Component
Bufdir Preview Accessibility Utilities
infrastructure low
Epic Risks (2)
high impact medium prob integration

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.

high impact medium prob scope

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.