Apply full WCAG 2.2 AA semantics to Notification Centre
epic-in-app-notification-centre-ui-task-011 — Audit and apply complete WCAG 2.2 AA accessibility semantics across all NotificationCentreScreen sub-widgets: add Semantics labels to all interactive elements, ensure focus order matches visual order, announce filter changes and unread count updates via NotificationAccessibilityAnnouncer (component 528-notification-accessibility-announcer) using live regions, ensure touch targets are minimum 44x44dp, verify colour contrast ratios meet 4.5:1 for text and 3:1 for UI components using the contrast-safe colour palette. Run flutter_accessibility_lint and resolve all violations.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 6 - 158 tasks
Can start after Tier 5 completes
Implementation Notes
Start with the flutter_accessibility_lint audit (`flutter pub run flutter_accessibility_lint`) to generate a baseline violation list before making changes. Tackle touch targets first (quickest wins), then Semantics labels, then live regions. For live regions, use `SemanticsService.announce(message, TextDirection.ltr)` inside NotificationAccessibilityAnnouncer rather than building custom Semantics trees — it maps correctly to both VoiceOver and TalkBack live region behaviour. For filter chip focus semantics, use `Semantics(label: 'Filter by ${filter.displayName}', selected: isActive, button: true, child: ...)`.
For the unread badge, use `Semantics(label: '${count} unread notifications', liveRegion: true, child: ...)`. Avoid `excludeSemantics: true` on items that are visually informative but just decorative in context — use ExcludeSemantics widget explicitly for clarity. Contrast checking: map every design token color used in the screen against the WCAG 2.2 AA calculator before sign-off.
Testing Requirements
Write flutter_test accessibility tests covering: (1) each NotificationListItem Semantics node contains label, hint, and correct isButton/isSelected properties, (2) MergeSemantics correctly merges icon and text for filter chips into a single announcement, (3) NotificationAccessibilityAnnouncer.announceFilterChange() is called when a filter chip is tapped, (4) touch target sizes are >= 44x44dp for all interactive elements (use tester.getSize()), (5) ExcludeSemantics wraps decorative dividers so they are invisible to screen readers. Run `flutter test --tags accessibility` as a dedicated suite. Additionally, perform manual VoiceOver testing on a physical iOS device as part of the QA checklist.
If a referenced entity (contact, certification, activity) has been deleted or its RLS policy now excludes the current user, the deep link handler may navigate to a screen that renders in an error state or throws an unhandled exception.
Mitigation & Contingency
Mitigation: The deep link handler must perform a lightweight existence check (HEAD request or minimal SELECT) before pushing the route. Define a contract with each destination screen for how to handle a not-found entity ID passed as a route parameter.
Contingency: If the existence check itself fails (network error), navigate to the destination screen anyway and let it handle the error gracefully with its own error state; do not block navigation for network timeouts.
If the tab badge widget triggers a full rebuild of the bottom navigation bar on every unread count change, it will cause visible jank on devices with many active Realtime events (e.g., org admins receiving org-wide alerts).
Mitigation & Contingency
Mitigation: Scope the badge widget to a dedicated BlocSelector that rebuilds only when the unread count value changes, not on any BLoC state emission. Use RepaintBoundary to isolate the badge from the rest of the nav bar.
Contingency: If performance issues persist, debounce badge updates to a maximum of one rebuild per 500ms and display the last known count during the debounce window.
Complex swipe-to-mark-read gestures and dynamic list updates may conflict with VoiceOver/TalkBack navigation patterns, particularly for Blindeforbundet users who rely exclusively on screen readers.
Mitigation & Contingency
Mitigation: Provide a dedicated accessibility action (Semantics.onTap / CustomSemanticsAction) for mark-as-read on each list item so screen reader users do not need the swipe gesture. Test with VoiceOver on iOS and TalkBack on Android before each release.
Contingency: If the swipe gesture proves incompatible with assistive technologies, disable it when a screen reader is detected (via ScreenReaderDetectionService) and rely solely on the tap-to-read and accessible action pathways.