medium priority low complexity frontend pending frontend specialist Tier 9

Acceptance Criteria

When the bottom sheet opens, focus is programmatically moved to the sheet's first focusable element (title or close button) within one frame
Focus cannot leave the sheet while it is open; Tab/switch-access cycles only within the sheet's focusable elements
The sheet container has Semantics role SemanticsRole.dialog (or equivalent Flutter semanticsHint indicating modal) so screen readers announce it as a dialog
Each wellbeing flag badge is wrapped in Semantics with a descriptive label (e.g., 'Wellbeing flag: Low energy') rather than relying on color alone
All body text meets 4.5:1 contrast ratio; section headings meet 3:1 contrast ratio against the sheet background
Traversal order: close button → title → scenario context section → wellbeing flags → CTA → dismiss — verified via semantic tree inspection
Screen reader announces 'Dialog opened: [scenario title]' on sheet appearance
On sheet dismissal, focus returns to the originating card element
No accessibility regression on existing widget tests for the bottom sheet

Technical Requirements

frameworks
Flutter
flutter_test
data models
ScenarioPrompt
WellbeingFlag
ScenarioContext
performance requirements
Focus management must complete within a single post-frame callback — no visible delay
security requirements
Semantics labels for sensitive scenario context fields must use generic descriptions, not raw user data, when the field is marked sensitive
ui components
ScenarioPromptDetailBottomSheet
WellbeingFlagChip
AppButton (CTA)
FocusScope (Flutter built-in — for focus trap)
Semantics (Flutter built-in)
FocusNode

Execution Context

Execution Tier
Tier 9

Tier 9 - 22 tasks

Can start after Tier 8 completes

Implementation Notes

Use FocusScope with a dedicated FocusScopeNode to trap focus. In the sheet's initState (or first-frame callback via WidgetsBinding.instance.addPostFrameCallback), call FocusScope.of(context).requestFocus(firstFocusNode). Wrap the DraggableScrollableSheet or modal container with Semantics(explicitChildNodes: true) and set scopesRoute: true and namesRoute: true so Flutter's routing semantics announces it as a dialog. For wellbeing flag chips, each chip widget must have its own Semantics node — do not rely on color alone (this also satisfies WCAG 1.4.1 Use of Color).

When the sheet is popped, call Navigator.pop and immediately restore focus to the originating card using a stored FocusNode reference passed into the sheet. Traversal order is controlled by FocusTraversalGroup with ReadingOrderTraversalPolicy. Reuse existing design tokens for all colors — do not add new color values.

Testing Requirements

Widget tests using flutter_test: (1) pump the bottom sheet, call tester.ensureSemantics(), and verify SemanticsNode tree includes a dialog-role node; (2) assert the first FocusNode inside the sheet receives focus after showModalBottomSheet resolves (use FocusManager.instance.primaryFocus); (3) verify that all wellbeing flag badges have non-empty Semantics labels; (4) simulate Tab key press and assert focus stays within the sheet (FocusTrap behavior). Manual test on device with TalkBack (Android) and VoiceOver (iOS) to confirm announcement order. Contrast ratios must be verified with an external tool (e.g., Colour Contrast Analyser) against design token hex values.

Epic Risks (2)
high impact medium prob technical

If the scheduler runs concurrently (e.g., two overlapping cron invocations due to edge function retry), duplicate prompts could be dispatched before the first run's history records are committed, breaking the deduplication guarantee.

Mitigation & Contingency

Mitigation: Use a Postgres advisory lock or unique constraint on (user_id, scenario_id, activity_ref) in the prompt history table to make concurrent writes idempotent; design the scheduler to check history inside a transaction.

Contingency: If concurrency issues persist in production, add a distributed lock via Supabase Edge Function concurrency limit (max_instances=1) for the evaluation function as a hard guard.

medium impact medium prob scope

Coordinators may find scenario configuration unclear if trigger conditions are expressed as raw JSON or technical terminology, leading to misconfiguration and irrelevant prompts being sent to peer mentors.

Mitigation & Contingency

Mitigation: Design the ScenarioConfigurationScreen to display human-readable descriptions of each template's trigger condition (e.g., 'Send 3 days after first contact if wellbeing concern was flagged') rather than raw rule properties; validate with an HLF coordinator in a design review before implementation.

Contingency: If coordinators still misconfigure rules after launch, add a preview mode that shows a simulated prompt based on a test activity before the rule is enabled.