high priority medium complexity testing pending testing specialist Tier 7

Acceptance Criteria

All four components (MileageEntryForm, DistanceInputField, RouteInputFields, RealtimeReimbursementDisplay) pass WCAG 2.2 AA contrast checks with a minimum ratio of 4.5:1 for normal text and 3:1 for large text
Every interactive element has a minimum touch target of 44x44 logical pixels as required by WCAG 2.5.5
Focus order traverses all four components in a logical, predictable sequence matching the visual reading order
All form fields and interactive controls have correct semantic labels that are announced correctly by TalkBack (Android) and VoiceOver (iOS)
RealtimeReimbursementDisplay uses a live region (aria-live equivalent via Flutter semantics) so that value changes are announced to screen-reader users without focus shift
At 200% system font size, no text is clipped, truncated, or overflows its container across any of the four components
At 200% system font size, all interactive elements remain tappable and visually distinct
No thin or italic font variants are used in any of the four components
A written compliance sign-off note is produced listing each WCAG criterion checked, its pass/fail status, and the fix applied where relevant
All identified issues are fixed before the sign-off note is finalized — the note must record zero outstanding failures

Technical Requirements

frameworks
Flutter
flutter_test
performance requirements
Accessibility tree traversal must complete within 100ms per widget in test harness to keep CI fast
security requirements
No sensitive reimbursement values may be read aloud without explicit user intent — confirm that RealtimeReimbursementDisplay live-region announcements do not expose personal financial data unexpectedly
ui components
MileageEntryForm
DistanceInputField
RouteInputFields
RealtimeReimbursementDisplay

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

Use Flutter's SemanticsTester and ensureSemantics() to programmatically verify the accessibility tree. Wrap any live-region widget in a Semantics widget with liveRegion: true and confirm announcements fire on value change. For contrast ratios, use a color-contrast tool against the project's design token palette — do not introduce ad-hoc color values. For 200% scaling, wrap test subjects in MediaQuery(data: MediaQuery.of(context).copyWith(textScaleFactor: 2.0)) inside the test pump.

Pay particular attention to the DistanceInputField suffix label (km/miles unit) which is a common truncation point at large font sizes. All fixes must use the design token system — do not hard-code font sizes or colors. Given the project's user base (NHF members including stroke survivors, Blindeforbundet members relying on screen readers), WCAG compliance is a hard requirement, not cosmetic polish.

Testing Requirements

Write flutter_test widget tests using the Semantics finder and tester.pumpWidget to exercise all four components in isolation. For each component: (1) assert that SemanticsNode labels are non-empty and descriptive; (2) assert touch target sizes using tester.getSize(); (3) assert that focus traversal order matches expected index sequence using FocusTraversalGroup. Simulate 200% textScaleFactor via MediaQuery override in each test and assert no RenderFlex overflow exceptions are thrown. Run the full accessibility test suite in CI on both Android and iOS simulator targets.

Manual screen-reader walk-through with VoiceOver on iOS and TalkBack on Android must also be performed and recorded in the sign-off note.

Component
Mileage Entry Form
ui medium
Epic Risks (3)
medium impact low prob technical

Triggering MileageCalculationService on every keystroke in the distance field could cause frame drops on lower-end Android devices if the widget rebuild chain is too broad. Jank during real-time calculation would degrade the peer-mentor experience, particularly for users with motor impairments who may type slowly and need immediate feedback.

Mitigation & Contingency

Mitigation: Keep the reimbursement calculation inside the RealtimeReimbursementDisplay widget subtree only, using a StatefulWidget with a local state variable rather than lifting state to the BLoC. This limits rebuilds to a single widget. Profile on a low-end device (Qualcomm Snapdragon 450 class) before code review.

Contingency: If profiling shows frame drops, debounce the onChanged callback by 50ms using a Timer before updating the local state, which remains imperceptible to the user while eliminating excessive rebuilds.

medium impact medium prob scope

The optional destination field requires balancing two competing requirements: WCAG 2.2 AA demands a clear label association for screen readers, while the privacy requirement means the label must communicate optionality and privacy sensitivity without exposing the user to undue pressure to fill it in. A poorly worded label could cause screen-reader users to misunderstand the field's optional nature or feel compelled to enter private location data.

Mitigation & Contingency

Mitigation: Draft label text and hint text with input from at least one screen-reader user during the accessibility review. Use Flutter's Semantics widget to provide a separate semantic label that is more descriptive than the visual placeholder. Test with TalkBack and VoiceOver before sign-off.

Contingency: If user testing reveals confusion, replace the inline hint with a tappable information icon that opens a brief explanation of why the field is optional and how the data is used, reducing cognitive load on the field label itself.

low impact medium prob integration

If OrgRateConfigRepository has not yet resolved the org rate when the form loads (e.g. slow network, first launch with empty cache), the reimbursement display cannot show a meaningful value. Showing '0' or an error state could confuse mentors and undermine trust in the feature.

Mitigation & Contingency

Mitigation: Display a loading skeleton in the reimbursement display widget until the rate is available. Once loaded, animate the value in. Show a subtle 'rate unavailable' message if the fetch times out, but allow form completion and submission (rate will be fetched server-side at submission time).

Contingency: If rate unavailability is frequent due to connectivity issues, store the last successfully fetched rate in SharedPreferences as an additional fallback so the widget can render a stale-but-indicative value with a caveat label.