high priority medium complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

When DuplicateWarningBottomSheet opens, VoiceOver (iOS) and TalkBack (Android) immediately announce the sheet's title/description without requiring a swipe gesture
Focus (screen reader cursor) is trapped within the bottom sheet while it is open; tabbing or swiping does not move focus to content behind the sheet
When the bottom sheet is dismissed (via action or drag), focus returns to the element that triggered the sheet open
All interactive controls (buttons, close icon) have a minimum tap target size of 44×44 dp as measured by Flutter's widget size introspection
'Proceed' button (or equivalent primary action) has a semanticsLabel that includes the consequence: e.g., 'Proceed — save activity as reviewed duplicate'
'Cancel' button has a semanticsLabel that includes the consequence: e.g., 'Cancel — return to activity form without saving'
The close/drag handle, if interactive, has an accessible label such as 'Close duplicate warning'
Sheet container uses Semantics widget with liveRegion: true (or equivalent) so screen readers announce appearance without user-initiated navigation
All text within the sheet meets WCAG 1.4.3 contrast ratio ≥ 4.5:1 for normal text and ≥ 3:1 for large text, verified against design tokens
No text is conveyed exclusively through color — icons or text labels supplement any color-coded status indicators
All accessibility fixes are validated using flutter_test semantics API assertions (not just manual testing)
Manual test checklist is completed on a physical iOS device with VoiceOver and an Android device with TalkBack

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
data models
DuplicateWarning
performance requirements
Accessibility wrappers (Semantics, ExcludeSemantics) must not add measurable frame render overhead; verify with Flutter DevTools performance overlay
security requirements
Accessible labels must not inadvertently expose sensitive participant data in announcement strings; use generic role descriptions rather than names in semanticsLabels
ui components
DuplicateWarningBottomSheet (remediated)
Semantics wrapper with liveRegion
FocusTrap or Modal barrier with semantics exclusion for background content
AccessibleActionButton (minimum 44×44 dp with full semanticsLabel)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Wrap the bottom sheet content in a Semantics widget with liveRegion: true at the sheet root level so that assistive technologies announce its appearance automatically. Flutter's showModalBottomSheet already provides a modal barrier that blocks pointer events behind it, but it does NOT automatically trap focus for screen readers — you must wrap background content in ExcludeSemantics(child: ...) while the sheet is open, or use the semanticsLabel and FocusTrap approach. The recommended Flutter pattern is to use FocusTraversalGroup with FocusScopeNode to contain tab order. For the 44×44 dp minimum touch target, wrap buttons in a SizedBox with minimum 44×44 or use Flutter's MaterialTapTargetSize.padded which is set globally but may need per-widget override.

Write semanticsLabel strings as complete sentences describing the action AND its consequence — this matches the WCAG 2.4.6 Headings and Labels requirement and is particularly important given NHF's users include people with cognitive and visual disabilities (per workshop documentation). Run contrast checks programmatically by extracting design token hex values and computing luminance ratio in a unit test.

Testing Requirements

Flutter widget tests (flutter_test) must use tester.getSemantics() and SemanticsController to assert: the sheet's root node has liveRegion flag set; each action button has a non-empty semanticsLabel containing the consequence phrase; the drag handle has a semanticsLabel; background content nodes are excluded from semantics (ExcludeSemantics) while the sheet is open. Use flutter_test's SemanticsHandle and matchesSemantics matchers. Measure touch target sizes using tester.getSize() and assert width >= 44.0 and height >= 44.0 in logical pixels for all interactive elements. Manual accessibility tests must be documented in a checklist covering: VoiceOver iOS announcement on open; TalkBack Android announcement on open; focus trap verification; focus restoration on close; contrast check using a contrast analyzer tool against design token color values.

Component
Duplicate Warning Bottom Sheet
ui medium
Epic Risks (3)
high impact medium prob technical

The merge resolution path requires identifying which fields from the draft differ from the existing record, applying those differences to the existing record, and cancelling the draft — all as an atomic operation. Partial failures (e.g., update succeeds but draft cancellation fails) could leave the system in an inconsistent state.

Mitigation & Contingency

Mitigation: Implement the merge path as a Supabase RPC transaction that updates the existing record and soft-deletes the draft in a single atomic call. The DuplicateResolutionHandler should never attempt field-level merge at the application layer.

Contingency: If the atomic RPC approach proves too complex for the initial release, simplify the merge path to: mark existing record as the canonical record and cancel the new submission without field merging, displaying a message to the user to manually verify the existing record's fields. Log a follow-up ticket for full field-merge in a later sprint.

medium impact medium prob integration

The DuplicateWarningBottomSheet must intercept the activity wizard's save action without disrupting the wizard's existing navigation stack. If the bottom sheet is implemented as a separate route rather than an overlay, back navigation could break the wizard's step state.

Mitigation & Contingency

Mitigation: Use a `showModalBottomSheet` overlay pattern so the bottom sheet sits above the wizard route without pushing a new route onto the Navigator stack. The wizard's CuBit/BLoC retains all draft state while the sheet is visible. Test this integration with the existing activity-registration-cubit before merging.

Contingency: If the overlay approach causes Z-order or focus issues with the wizard's keyboard-aware layout, route the duplicate check result back to the wizard as a state event (DuplicateDetected), and let the wizard render a local inline warning banner instead of a bottom sheet.

medium impact medium prob technical

The bottom sheet and comparison panel involve complex layouts with multiple interactive elements. Screen reader users (particularly relevant for Blindeforbundet) may struggle with the side-by-side comparison layout if semantics are not carefully ordered.

Mitigation & Contingency

Mitigation: Design the comparison panel with a single-column semantic order (read record A fully, then record B fully) regardless of visual layout. Use Flutter's `Semantics` widget with `sortKey` to enforce correct traversal order. Test with TalkBack and VoiceOver against the WCAG 2.2 AA reading order criteria.

Contingency: If side-by-side layout cannot achieve acceptable screen reader ordering, switch to a stacked tab layout (Tab A / Tab B) for the comparison panel that is semantically simple even if less visually immediate for sighted users.