high priority medium complexity infrastructure pending testing specialist Tier 0

Acceptance Criteria

AccessibilityAuditRunner is a standalone Dart class in test/utils/accessibility_audit_runner.dart importable by any widget test
runSemanticAssertions(WidgetTester tester) method verifies that every interactive widget has a non-empty Semantics label
runTouchTargetChecks(WidgetTester tester) method finds all tappable widgets and asserts each has a minimum hit-test area of 44×44 logical pixels
runContrastChecks(List<ColorPair> pairs) method validates each foreground/background color pair meets WCAG 2.2 AA ratio (4.5:1 for normal text, 3:1 for large text)
A single auditAll(WidgetTester tester, {List<ColorPair>? colorPairs}) convenience method runs all three checks and collects all failures before throwing, so the developer sees every issue in one run
Failures produce human-readable messages identifying the widget type, key, and the specific criterion that failed
The runner does not depend on any production source files — only flutter_test and dart:ui
An example usage test in test/utils/accessibility_audit_runner_test.dart demonstrates the runner against a minimal scaffold with one passing and one intentionally failing widget
Runner integrates with flutter_test's expect() so failures appear as standard test failures in CI output
Documentation comment on the class explains which WCAG 2.2 AA criteria each method covers

Technical Requirements

frameworks
flutter_test
Flutter Semantics API
dart:ui (Color)
performance requirements
Full audit run on a single screen widget tree must complete in under 500ms to keep test suite fast

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Flutter's flutter_test package exposes SemanticsController via tester.getSemantics() and find.bySemanticsLabel(). Use tester.widget(finder) combined with tester.getRect() to measure actual hit-test areas — be aware that InkWell and IconButton both carry their own hit-test boxes, so search by type rather than gesture detector alone. For contrast ratio calculation, implement the WCAG relative luminance formula in pure Dart using Color.red/green/blue components — no external packages needed. Structure the class as a final class with only static methods to keep it stateless and simple.

For the color pair checks, define a simple ColorPair value object: class ColorPair { final Color foreground; final Color background; }. Keep all logic in the test/ directory tree — nothing in lib/ — to enforce the separation between production and test code. This runner will be called at the bottom of every widget test for navigation and UI components in this epic.

Testing Requirements

The AccessibilityAuditRunner is itself a test utility, so its own test file validates correct pass/fail behavior. Test 1: Pump a widget with all semantics labels present, all touch targets ≥44dp, and compliant contrast — auditAll must pass. Test 2: Pump a widget with a missing semantics label — runSemanticAssertions must fail with a message identifying the widget. Test 3: Pump a widget with a 40×40dp touch target — runTouchTargetChecks must fail.

Test 4: Call runContrastChecks with a non-compliant pair (e.g., light gray on white) — must fail with the computed ratio. Use flutter_test's expectLater and throwsA matchers to assert failure paths.

Component
Accessibility Audit Runner
infrastructure medium
Epic Risks (3)
high impact high prob technical

Flutter's ModalBottomSheet and showDialog do not automatically confine VoiceOver or TalkBack focus to the modal's subtree on all platform versions. Background content may remain reachable by screen readers, confusing users and violating WCAG 2.2 criterion 1.3.1.

Mitigation & Contingency

Mitigation: Wrap modal content in an ExcludeSemantics or BlockSemantics widget for background content. Use a Semantics node with liveRegion on the modal container and manually request focus via FocusScope after the modal animation completes. Test on both iOS (VoiceOver) and Android (TalkBack) during widget development.

Contingency: If platform-level focus trapping is unreliable, implement a custom modal wrapper widget that uses a FocusTrap widget (available in Flutter's internal tooling) and an Overlay entry with semantics blocking on the dimmed background layer.

medium impact medium prob technical

On iOS, the system-level swipe-back gesture (UINavigationController) can bypass PopScope and GoRouter's gesture suppression, meaning users can still accidentally dismiss screens via swipe even after the component is implemented. This breaks the gesture-free contract for motor-impaired users.

Mitigation & Contingency

Mitigation: Set popGestureEnabled: false in GoRouter route configurations where swipe-back is suppressed. Test specifically against Flutter's CupertinoPageRoute, which respects this flag, and verify that GoRouter generates Cupertino routes on iOS rather than Material routes with gesture enabled.

Contingency: If go_router's popGestureEnabled flag does not propagate correctly, wrap affected routes in a WillPopScope replacement (PopScope with canPop: false) and file a bug with the go_router maintainers. Document the workaround in the navigation-route-config component for future maintainers.

medium impact medium prob scope

The feature description implies migrating all existing ModalBottomSheet and dialog call sites across the app to use the new accessible helpers, which is a cross-cutting change. Scope underestimation could mean the epic finishes the new components but leaves many call sites un-migrated, leaving the accessibility promise partially broken.

Mitigation & Contingency

Mitigation: Audit all existing modal call sites at the start of the epic (grep for showModalBottomSheet, showDialog, showCupertinoDialog) and add the count to the task list. Treat migration as explicit tasks, not an implied post-step.

Contingency: If migration scope grows beyond the epic's estimate, create a follow-up tech-debt epic scoped only to call-site migration, and gate the release on at minimum all flows used by the accessibility user-story acceptance criteria being migrated.