high priority medium complexity frontend pending frontend specialist Tier 6

Acceptance Criteria

All interactive elements (filter chips, notification list items, mark-all-read button, refresh control) have Semantics widgets with descriptive label, hint, and onTap/onLongPress properties
VoiceOver (iOS) and TalkBack (Android) read each notification item as: '[title], [timestamp], [read/unread status]' in a single focused announcement
Focus traversal order matches the visual top-to-bottom, left-to-right layout with no skipped or reversed focus jumps
When a filter chip is tapped, NotificationAccessibilityAnnouncer announces 'Showing [filter name] notifications' via a live region within 500ms
When the unread count changes, NotificationAccessibilityAnnouncer announces 'You have [N] unread notifications' via a polite live region
Every tappable element has a minimum touch target of 44x44 logical pixels (use SizedBox or Padding to expand hit area if needed)
All text elements pass WCAG 2.2 AA contrast ratio of 4.5:1 against their background, verified with the contrast-safe design token palette
All non-text UI components (borders, icons, badges) meet the 3:1 contrast ratio requirement
flutter_accessibility_lint reports zero violations after remediation
Semantics labels are localized — no hardcoded English strings in Semantics widgets; use AppLocalizations
The empty state and loading skeleton widgets also carry appropriate Semantics labels (e.g., 'Loading notifications', 'No notifications to display')

Technical Requirements

frameworks
Flutter
BLoC
apis
NotificationAccessibilityAnnouncer (component 528) — announceFilterChange(), announceUnreadCountUpdate()
SemanticsService.announce() for live region announcements
flutter_accessibility_lint CLI tool
data models
Notification (id, title, timestamp, isRead)
NotificationFilterType
performance requirements
Semantics tree overhead must not cause frame drops — avoid wrapping every pixel in a Semantics widget; use MergeSemantics where appropriate
Live region announcements must be debounced to avoid spamming the screen reader on rapid state changes (minimum 300ms debounce)
security requirements
Semantics labels must not expose sensitive notification content beyond what is visually shown
Notification sender identity in the semantics label must respect the same data-masking rules as the visual display
ui components
Semantics widget for each NotificationListItem
MergeSemantics to group icon + label in filter chips
ExcludeSemantics for purely decorative elements (dividers, background shapes)
SizedBox/Padding wrappers to enforce 44x44dp minimum touch targets

Execution Context

Execution Tier
Tier 6

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.

Component
Notification Centre Screen
ui medium
Epic Risks (3)
medium impact medium prob integration

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.

medium impact low prob technical

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.

high impact medium prob scope

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.