high priority medium complexity frontend pending frontend specialist Tier 6

Acceptance Criteria

All interactive elements (buttons, filter toggle, period selector, mentor rows, pull-to-refresh) have a minimum touch target of 44×44 logical pixels enforced via SizedBox or GestureDetector constraints
All OutlierBadge, DeltaIndicator, and stat card widgets are wrapped in Semantics with descriptive label attributes (e.g., 'Sessions completed: 12, up 3 from prior period')
Delta indicators include semantic labels that describe the change direction and magnitude textually, not just visually (e.g., Semantics(label: 'Hours logged: 8.5, decreased by 2 hours (19%) from prior period'))
Decorative icons and color-only indicators include Semantics(excludeSemantics: true) or ExcludeSemantics wrapper to prevent screen reader noise
VoiceOver (iOS) reads all mentor rows in list order from top to bottom without skipping or doubling elements
TalkBack (Android) reads all mentor rows in list order consistently
All text elements have contrast ratio ≥ 4.5:1 against their background (normal text) or ≥ 3:1 (large text ≥ 18pt / bold ≥ 14pt) verified against design token color values
All badge color pairs (foreground + background) pass WCAG 2.2 AA contrast verification documented in a PR comment or test file
Keyboard/switch-access navigation traverses elements in logical focus order (top-to-bottom, left-to-right)
No accessibility warnings are emitted by Flutter's semantics debug output during widget tests
Audit findings are documented as inline code comments on each remediated widget

Technical Requirements

frameworks
Flutter
Dart
flutter_test (semantics testing)
performance requirements
Semantics wrappers must not increase widget tree depth unnecessarily — use MergeSemantics where multiple child widgets form one logical semantic unit
security requirements
Semantic labels must not expose internal IDs, raw scores, or system identifiers — only human-readable descriptions
ui components
Semantics — Flutter built-in, applied to all interactive and informational elements
MergeSemantics — for stat cards combining label + value into one semantic node
ExcludeSemantics — for decorative icons and color-only indicators

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Start with an audit pass: run the app on a simulator with Accessibility Inspector (Xcode) enabled and note all elements lacking labels or with insufficient touch targets. Common Flutter accessibility pitfalls to fix: (1) Icon buttons without Tooltip or Semantics(label:...) — always add; (2) Row widgets mixing text and icons where VoiceOver reads them in wrong order — use MergeSemantics; (3) Color-only status indicators (green dot, red badge) — always add a text label in the Semantics tree even if visually hidden; (4) ListTile onTap without a semantic label — add Semantics(label: 'Mentor: ${name}, tap to view details'). For the 44×44 requirement: wrap icon buttons in SizedBox(width: 44, height: 44, child: IconButton(...)) and verify InkWell tap areas. Document all contrast ratio checks as a markdown table in a comment on the PR for reviewer verification.

The project is used by Blindeforbundet members who rely on VoiceOver — this task has real user impact.

Testing Requirements

Automated accessibility tests using flutter_test with SemanticsHandle: verify semantic labels exist on all interactive elements; verify delta indicators have non-empty label strings; verify touch targets are at least 44×44 by asserting widget constraints. Manual QA on a physical iOS device with VoiceOver enabled: record navigation order through the mentor list and compare against visual order. Manual QA on a physical Android device with TalkBack enabled. Use a color contrast analyzer tool (e.g., Colour Contrast Analyser desktop app) to verify all design token color pairs used for badges and text against WCAG 2.2 AA thresholds — document results.

Run flutter test --verbose and check for AccessibilityGuideline violations using AccessibilityGuideline.androidScrollable.

Component
Periodic Summary Digest Card
ui medium
Epic Risks (3)
medium impact medium prob technical

If the cached summary is from a prior period (e.g., previous quarter's card is still cached), the digest card could appear on the home screen with outdated numbers after a new period begins, confusing users who have already seen that data.

Mitigation & Contingency

Mitigation: In the SummaryCacheRepository, store the period_start and period_end alongside cached data. In the PeriodicSummaryCard BLoC, compare the cached record's period against the current date using PeriodCalculatorService. Only render the card if the cached summary belongs to the active period.

Contingency: If stale cards appear in production, push a hotfix that adds the period validity check to the BLoC and clears all cached summaries older than the current period boundary via a forced cache flush on next app launch.

medium impact medium prob technical

Using colour alone to distinguish underactive from overloaded mentors in the coordinator view would fail WCAG 1.4.1 (use of colour). This is especially critical for organisations serving users with colour vision deficiency.

Mitigation & Contingency

Mitigation: Always pair colour indicators with a text label or icon (e.g., a downward arrow + 'Underactive' text alongside the amber colour). Use the contrast-safe-color-palette design tokens throughout. Run the contrast-ratio-validator on all new colour combinations in CI.

Contingency: If an accessibility audit flags colour-only indicators post-launch, introduce icon-based indicators as a patch release and update the design token definitions to enforce the paired-indicator pattern going forward.

low impact medium prob integration

Inserting a new card into the role-based home screen requires changes to shared home screen widget composition, which may conflict with parallel feature branches also modifying the home screen layout, causing merge conflicts and integration delays.

Mitigation & Contingency

Mitigation: Implement the PeriodicSummaryCard as a fully self-contained widget that the home screen conditionally renders based on a BLoC state flag. Minimise changes to the home screen itself to a single conditional insertion point, reducing the surface area for merge conflicts.

Contingency: If integration conflicts block the PR, isolate the card behind the organisation-scoped feature flag so it can be toggled independently and merged without affecting other home screen changes.