Add accessibility semantics to PauseStatusBanner
epic-peer-mentor-pause-ui-state-task-004 — Wrap PauseStatusBanner in appropriate Semantics nodes so screen readers announce paused status, timestamp, and reason as a coherent message. Mark the reactivate shortcut as a button with a descriptive label. Ensure dynamic type scaling does not truncate content.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Use Semantics(container: true, label: '...') with a computed label string that concatenates status + formatted timestamp + reason. Do NOT rely on child widget labels being merged automatically — build the composite label in the parent Semantics node. For the reactivate button use Semantics(button: true, label: 'Reactivate mentor — currently paused', child: ...). Wrap the entire banner in a LayoutBuilder + FittedBox pattern only if needed for overflow; prefer Flexible inside a Column.
Use MediaQuery.textScalerOf(context) (Flutter 3.12+) instead of deprecated textScaleFactor. Avoid ExcludeSemantics on the timestamp/reason — screen-reader users need this information. Design token colours must already satisfy contrast; if not, file a design system issue rather than overriding inline.
Testing Requirements
Unit tests: verify Semantics node properties (label, role, container=true) using flutter_test's SemanticsHandle and tester.getSemantics(). Widget tests: render banner with long reason strings at textScaleFactor 2.0 and assert no overflow errors. Widget tests: confirm reactivate button Semantics.button == true and label contains 'Reactivate'. Integration/manual: test with VoiceOver on iOS simulator — banner must be announced as single unit.
Accessibility audit with flutter_test's SemanticsController.checkSemantics() must pass.
PauseConfirmationDialog must meet WCAG 2.2 AA focus trap requirements: when the dialog opens, focus must move to the first interactive element; when it closes, focus must return to the triggering toggle. Flutter's default showDialog does not always guarantee correct focus restoration on Android, which could trap screen-reader users.
Mitigation & Contingency
Mitigation: Use a custom modal implementation wrapping Flutter's Dialog with explicit FocusScope management and test with TalkBack on Android and VoiceOver on iOS during development, not just in final QA. Reference the accessible-modal-sheet pattern already used elsewhere in the codebase.
Contingency: If full WCAG focus management cannot be achieved within the sprint, ship the feature with a documented accessibility defect and schedule a dedicated accessibility remediation task. Communicate the known gap to the accessibility stakeholder at HLF/NHF.
If the user taps the PauseReactivateToggle rapidly before the BLoC emits a loading state, multiple PauseMentorEvents could be added to the BLoC stream, resulting in duplicate service calls and inconsistent UI state (toggle flickering between states).
Mitigation & Contingency
Mitigation: Disable the toggle widget immediately on first tap by emitting MentorStatusLoading synchronously before the async service call begins. Use BLoC's event transformer with droppable() to discard subsequent events while a transition is in progress.
Contingency: If BLoC event deduplication is not sufficient, add a debounce at the widget level (300 ms) and a server-side idempotency check in MentorStatusService that no-ops if the requested transition is already in progress.
PauseStatusBanner must conditionally render the reactivate shortcut only for users with coordinator permission. If the permission check relies on a stale role state in the Riverpod provider, a peer mentor could briefly see a reactivate action they are not authorised to use, which could cause a confusing permission-denied error if tapped.
Mitigation & Contingency
Mitigation: Source the permission check directly from the role-resolution Riverpod provider (which is kept in sync with Supabase auth) rather than from local component state. Add a widget test asserting the shortcut is absent when the user role is peer_mentor.
Contingency: If the shortcut is accidentally shown to an unauthorised user, the underlying MentorStatusService enforces role validation server-side, so the worst outcome is a visible error message rather than an actual unauthorised state change.