Implement 44dp touch target assertion
epic-navigation-and-gesture-accessibility-service-and-audit-task-010 — Implement the touch target size assertion in AccessibilityAuditRunner that walks the semantics tree for all interactive SemanticsNode instances and asserts each has a minimum bounding rect of 44×44 logical pixels. Use SemanticsController.find and node.rect to measure. Collect all failing nodes into TouchTargetViolation records with widget identity, actual size, and the screen/route where found.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Start from the root SemanticsNode returned by WidgetTester.semantics.find(find.byType(MaterialApp)) and do a depth-first walk. Filter nodes by checking node.getSemanticsData().hasFlag(SemanticsFlag.isButton) etc. Use node.rect for the actual bounding rect in logical pixels — this already accounts for padding added by InkWell/GestureDetector. Do not use node.transform unless you need screen coordinates; logical pixel rect is sufficient for the 44dp check.
The design token system should define sizing.minTouchTarget = 44.0 — reference this constant in the assertion rather than hardcoding 44.0. This requirement is directly tied to WCAG 2.5.8 (Target Size Minimum, AA) and is critical for NHF users with motor impairments and elderly users across all three organizations.
Testing Requirements
Unit tests: verify TouchTargetViolation serialization; verify the 44dp comparison logic handles edge cases (exactly 44.0 passes, 43.9 fails, 0.0 fails). Integration tests: pump a widget tree containing both compliant (48×48) and non-compliant (32×32) interactive elements, run the audit, assert exactly the non-compliant elements appear in violations. Integration test using the real AppButton widget: confirm it passes (AppButton should be sized to design token sizing.minTouchTarget = 44). Coverage target: 95% of touch-target walking logic.
Flutter's SemanticsController used in integration tests is an internal or semi-internal API that can break between Flutter stable releases. If the audit runner relies heavily on undocumented semantics tree traversal, a Flutter upgrade could silently disable the audit checks without a build failure, creating false confidence.
Mitigation & Contingency
Mitigation: Use only the public flutter_test accessibility APIs (meetsGuideline, SemanticsController.ensureSemantics) and wrap all SemanticsController calls in a versioned helper class with explicit assertions that the expected semantics tree shape is still available. Pin the Flutter SDK range in pubspec.yaml.
Contingency: If SemanticsController APIs break on a Flutter upgrade, fall back to widget-level golden tests that include the semantics tree snapshot, combined with manual Switch Access and VoiceOver QA checklists executed before each release.
Flutter integration tests that simulate Switch Access traversal on multiple screens can be slow (30–120 seconds per test flow), which may make the audit runner impractical to run on every CI commit if the test suite already has long run times.
Mitigation & Contingency
Mitigation: Scope the audit runner to a dedicated integration test target that runs on pull requests targeting main and on nightly builds, not on every push. Parallelise test shards in CI to keep wall-clock time acceptable. Profile audit run times during development and trim any flows that duplicate coverage.
Contingency: If CI run times exceed acceptable thresholds, split the audit runner into a fast smoke suite (touch targets and semantic labels only, runs on every PR) and a thorough traversal suite (Switch Access simulation, runs nightly), with the nightly failure blocking the release branch rather than every PR.