medium priority low complexity infrastructure pending frontend specialist Tier 0

Acceptance Criteria

NotificationAccessibilityConfig is a pure const class (no runtime dependencies) exporting constants and utility methods
Semantic labels are defined for: notification list item, unread badge, mark-as-read button, mark-all-read button, notification center header, and notification settings toggle
Semantic labels are written in Norwegian Bokmål as the primary language (matching app locale) with English keys for developer reference
All foreground/background color pairs used in notification UI pass WCAG 2.2 AA contrast ratio (≥4.5:1 for normal text, ≥3:1 for large text) — verified by a static contrast check utility
Minimum touch target size constant is 48×48 logical pixels, applied via a minTouchTarget helper used in notification widget SizedBox wrappers
Live region announcement strategy: unread count changes use SemanticsService.announce() with AssertiveAnnouncementPolicy; non-critical updates use polite
A utility method buildNotificationSemantics(AppNotification n) returns a Semantics widget wrapper with correct label, hint, and liveRegion for a notification list item
Sensitive notification content (if any) is flagged with Semantics(obscured: true) to prevent screen reader leakage per NHF accessibility requirement

Technical Requirements

frameworks
Flutter
Flutter Semantics API
WCAG 2.2 AA
data models
accessibility_preferences
performance requirements
All constants are compile-time const — zero runtime allocation cost
Contrast ratio check utility runs synchronously with no async overhead
security requirements
Screen reader announcements must not read sensitive PII (contact names, personal details) aloud in public contexts — wrap sensitive fields with Semantics(obscured: true) when app is in background or screen is shared
VoiceOver/TalkBack must announce when recording or microphone is active (per speech_to_text security requirement for visually impaired users)
ui components
Semantics widget wrappers for notification list items
MergeSemantics for compound notification cards
ExcludeSemantics for decorative elements (notification type icons used purely visually)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Create lib/features/notifications/accessibility/notification_accessibility_config.dart as a final class with only static const members. Define a private _contrastRatio(Color fg, Color bg) function using the WCAG relative luminance formula (0.2126R + 0.7152G + 0.0722B after gamma correction) to validate color pairs at test time. Group semantic labels by widget: const notificationItemLabel, const markAsReadHint, const unreadBadgeLabel, etc. For live region strategy, create a NotificationLiveRegionPolicy enum with assertive and polite values and map each announcement type to the correct policy.

Reference accessibility_preferences data model when building the contrast mode variant — if contrastMode == 'high', use higher-contrast color tokens from the design token system. This config class should be the single source of truth imported by all notification widget builders.

Testing Requirements

Unit tests (flutter_test): (1) assert all semantic label constants are non-empty strings; (2) contrast ratio utility returns true for all defined color pairs against WCAG 4.5:1 threshold; (3) minTouchTarget constant equals 48.0; (4) buildNotificationSemantics returns a Semantics widget with non-null label and correct liveRegion value. Widget test: render a NotificationListItem wrapped with the config semantics and use tester.getSemantics() to assert correct label and hint are present. No external dependencies needed — pure Flutter semantics API.

Epic Risks (2)
medium impact medium prob technical

The notification badge widget depends on a persistent Supabase Realtime websocket subscription for live unread count updates. On mobile, network transitions (WiFi to cellular, background app state) can silently drop the websocket, resulting in a stale badge count that does not update until the next app foreground — reducing trust in the notification system.

Mitigation & Contingency

Mitigation: Implement connection lifecycle management in the badge widget's BLoC that re-subscribes on app foreground and on network reconnection events. Add a fallback polling query (every 60 seconds when app is foregrounded) to reconcile the badge count if the Realtime subscription is interrupted.

Contingency: If Realtime reliability proves insufficient in production, replace the live subscription with a polling approach using a configurable interval, accepting slightly delayed badge updates in exchange for reliability.

medium impact medium prob technical

The notification list item widget requires merged semantics combining title, body, timestamp, read state, and role-context icon into a single VoiceOver/TalkBack announcement. Getting the merged semantics structure right for both iOS (VoiceOver) and Android (TalkBack) simultaneously is non-trivial and common to break silently when widgets are refactored.

Mitigation & Contingency

Mitigation: Use the project's existing semantics-wrapper-widget pattern with explicit Semantics widgets and excludeSemantics on decorative children. Write accessibility widget tests using Flutter's SemanticsController to assert the exact announcement string. Test on physical devices with VoiceOver and TalkBack enabled before release.

Contingency: If merged semantics cannot be achieved cleanly on both platforms, implement platform-specific semantic trees using defaultTargetPlatform branching, ensuring each platform receives an optimal announcement even if the implementation differs.