critical priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

All interactive elements (collapse/expand toggle, any buttons) have a Semantics label verified by tester.getSemantics(find.byType(...))
All informational elements (category rows, metadata chips, warning items) have non-empty Semantics labels
No Semantics node has an empty label or tooltip where one is required (enforced by flutter_test's expectAccessibilityGuidelines matcher)
Colour contrast ratio for all normal-text/background pairs in ExportPreviewPanel is >= 4.5:1 (verified programmatically)
Colour contrast ratio for large text (>= 18 pt or >= 14 pt bold) pairs is >= 3:1
The amber warning colour used in ExportPreviewWarningSection passes the 4.5:1 ratio against its text colour
All interactive touch targets measure >= 44Ă—44 logical pixels (verified with size assertions in widget tests)
A live-region announcement fires when ExportPreviewLoaded state is first emitted: 'Preview loaded. X categories. Y warnings.'
A live-region announcement fires when ExportPreviewWarningSection appears: 'X warnings found.'
Focus order is correct: metadata header → category table header → category rows → warning section header (verified with FocusTraversalGroup test)
No accessibility violations reported by Flutter's built-in accessibilityGuidelines checker (tester.pump + expectLater with meetsGuideline(androidTapTargetGuideline) and meetsGuideline(iOSTapTargetGuideline))
Screen reader manual test checklist completed on iOS (VoiceOver) covering all panel states

Technical Requirements

frameworks
Flutter
flutter_bloc
flutter_test
data models
accessibility_preferences
performance requirements
Accessibility audit tests must complete within the standard CI timeout (< 60 s per test file)
security requirements
Accessibility labels must not expose sensitive internal IDs (e.g. UUID values) to screen readers — use human-readable labels only
ui components
ExportPreviewPanel (full widget tree under audit)
ExportPreviewMetadataHeader
ExportPreviewWarningSection
BufdirCategoryTableRow

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Run the full accessibility test suite as a dedicated test file (export_preview_panel_a11y_test.dart) so it can be targeted separately in CI. For contrast ratio checks, implement a helper function contrastRatio(Color foreground, Color background) using the WCAG relative luminance formula — this avoids a third-party dependency. Fix any violations found during the audit in-place before marking this task complete; the audit is not a report task but a fix-and-verify task. Pay special attention to the skeleton loader: shimmer animations can produce animated Semantics nodes that confuse screen readers — suppress Semantics on skeleton rows with ExcludeSemantics.

For the live-region announcement on state transition, use a SemanticsService.announce() call within the BlocListener in ExportPreviewPanel rather than relying solely on Semantics(liveRegion: true), as the latter may not fire reliably on all platforms for dynamic content updates.

Testing Requirements

Automated tests using flutter_test: (1) Semantics label completeness — use tester.getSemantics on every named finder and assert label is non-empty; (2) Tap target size — use tester.getSize on all GestureDetector and InkWell widgets and assert >= Size(44, 44); (3) Live region — pump state transitions and verify SemanticsNode.liveRegion == true for the warning section and the panel root; (4) Accessibility guidelines — use expectLater(tester, meetsGuideline(androidTapTargetGuideline)) and meetsGuideline(iOSTapTargetGuideline) after each state render; (5) Focus traversal — use FocusTraversalPolicy traversal simulation to verify focus visits nodes in the documented order. Manual checklist: activate VoiceOver on iOS device, open ExportPreviewPanel, verify each section is announced correctly in all three states (loading, loaded with warnings, error). Document results in a test report artifact.

Component
Export Preview Panel
ui medium
Epic Risks (2)
medium impact medium prob technical

The preview panel requires a round-trip to the edge function to compute aggregated counts. If this call takes 5–15 seconds for large scopes, users may assume the app has frozen and navigate away, potentially triggering duplicate preview requests or leaving the export in an undefined state.

Mitigation & Contingency

Mitigation: Show an immediate skeleton loading state on the preview panel as soon as the period and scope are confirmed, with named stage labels (e.g. 'Querying activities…', 'Computing totals…') streamed from the edge function's progress events. Set a clear user-visible timeout with a retry option. Pre-warm the edge function with a lightweight ping when the scope selector is opened.

Contingency: If preview latency consistently exceeds 10 seconds for large scopes, cache the preview payload in local BLoC state and allow the coordinator to proceed to confirmation without re-fetching if scope and period have not changed since the last preview.

high impact medium prob scope

The export preview panel contains dynamic content (warning badges, loading skeletons, aggregated counts) that must be announced correctly by VoiceOver and TalkBack. Incorrect semantics annotations could make the preview unreadable for blind coordinators, violating the project's WCAG 2.2 AA accessibility mandate.

Mitigation & Contingency

Mitigation: Implement semantic annotations using Flutter's Semantics widget with explicit labels for all dynamic content. Use live region announcements for loading state transitions. Schedule a dedicated accessibility review session with a screen reader user (Blindeforbundet has relevant expertise) before marking the epic complete.

Contingency: If the preview panel cannot be made fully screen-reader-accessible in time for launch, ship a simplified text-only summary mode activated by a toggle at the top of the preview panel, which renders all data as a single readable paragraph with no interactive badges.