high priority low complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

Empty state widget displays the query string inline: 'No results found for "<query>"'
Empty state widget has Semantics(liveRegion: false, label: 'No results found for "<query>"') so screen readers read it when focus lands on it
Empty state does NOT use an image as the sole communicator of state (WCAG 2.2 AA 1.1.1)
Empty state text meets WCAG 2.2 AA contrast ratio of at least 4.5:1 against its background
Offline banner is a full-width container at the top of the list
Offline banner text reads 'You are offline. Showing cached results.' and uses a Semantics label with the same text
Offline banner background color meets WCAG 2.2 AA contrast ratio of at least 4.5:1 for the text within it
Offline banner disappears within 1 second of connectivity being restored
Both widgets use design token colors exclusively — no hardcoded hex values
Both widgets are independently testable (extracted as separate widget classes, not inlined)

Technical Requirements

frameworks
Flutter
flutter_test
apis
Flutter Semantics API
Flutter Connectivity stream
performance requirements
Offline banner visibility change must not trigger a full SearchResultsList rebuild
security requirements
Query string interpolated into empty state text must be HTML-escaped (not applicable for Flutter native, but sanitize for any web target builds)
ui components
EmptyStateWidget
OfflineBanner

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Extract EmptyStateWidget and OfflineBanner as their own StatelessWidget classes in the same file or a shared widgets sub-directory. Both widgets should accept their display strings as constructor parameters (not hardcoded) to support future localization. For the offline banner, use the design token for 'warning' or 'caution' surface color — typically an amber/yellow tone — and ensure the paired text token has sufficient contrast. Avoid using Icon alone in either widget; pair any icon with a visible text label.

The OfflineBanner should animate in/out using AnimatedSwitcher with a 200 ms fade for a polished but non-distracting transition.

Testing Requirements

Widget tests: render EmptyStateWidget with a sample query string and assert text content and Semantics label. Render OfflineBanner and assert Semantics label text. Use a contrast-check utility or manual review against the design token palette to confirm 4.5:1 ratio. Test that OfflineBanner is not rendered when connectivity flag is true.

Test that EmptyStateWidget is not rendered when results list is non-empty.

Component
Search Results List
ui low
Epic Risks (2)
medium impact medium prob technical

Flutter's Semantics live region support for announcing dynamic result count changes may behave inconsistently between VoiceOver (iOS) and TalkBack (Android), particularly regarding announcement throttling and focus management, causing the feature to pass testing on one platform and fail on the other.

Mitigation & Contingency

Mitigation: Test live region announcements on both iOS (VoiceOver) and Android (TalkBack) early in development using the existing accessibility test harness. Reference the existing LiveRegionAnnouncer component (608-live-region-announcer) patterns used elsewhere in the app.

Contingency: If cross-platform consistency cannot be achieved, implement a platform-specific announcement strategy using the SemanticsService.announce API with platform-conditional announcement timing to work around OS-specific throttling behaviour.

low impact low prob dependency

Voice-to-text progressive enhancement for Blindeforbundet may not be available or may behave unpredictably on all device/OS combinations, particularly older Android devices, potentially causing crashes or silent failures that degrade the search experience.

Mitigation & Contingency

Mitigation: Implement voice-to-text as a strictly optional enhancement: detect availability at runtime, show the microphone button only when the platform speech API reports availability, and wrap all voice invocations in try/catch with graceful degradation to standard text input.

Contingency: If voice-to-text causes instability on a subset of devices discovered during TestFlight/beta, disable the feature flag for that platform version while a fix is investigated, without impacting the core text-based search functionality.