User Interface high complexity Shared Component mobile
2
Dependencies
0
Dependents
0
Entities
0
Integrations

Description

Wraps bottom sheets and modal dialogs with correct focus trapping, initial focus placement, and focus restoration on close. Announces the sheet title and available actions when it opens, and returns focus to the triggering element when dismissed, ensuring proper VoiceOver and TalkBack navigation.

Feature: Screen Reader Support

accessible-modal-sheet

Summaries

Modal dialogs and bottom sheets are high-stakes interaction points where users make decisions, confirm actions, or complete focused workflows. For screen reader users, a modal that does not properly trap focus or announce its purpose creates a disorienting experience where the user cannot determine what has appeared on screen or how to interact with it. The Accessible Modal Sheet Widget solves this by ensuring every modal in the application correctly announces itself, traps focus so the user cannot accidentally navigate outside it, and returns the user to their previous context when dismissed. This is essential for meeting WCAG 2.1 AA conformance — a common contractual requirement in enterprise and government procurement — and for delivering a dignified, frustration-free experience to users with visual impairments across all features that use modal interactions.

The Accessible Modal Sheet Widget is a high-complexity shared component that will be consumed by every feature in the application that uses bottom sheets or modal dialogs. Its two dependencies (`live-region-announcer` and `semantics-wrapper-widget`) must be finalized first. Because this component wraps a cross-cutting UI pattern, it requires coordination with feature teams to ensure consistent usage — documentation and a usage guide are advisable before distribution. Testing scope is broad: every modal entry point across all features must be verified with VoiceOver and TalkBack, including trigger-element focus restoration after dismissal.

Complex edge cases include modals opened from within other modals (stacked focus contexts), modals dismissed via back-gesture rather than a close button, and modals opened by push notifications. Budget additional QA time for these scenarios. A shared component failure here affects all features simultaneously, making it a high-priority item for early stabilization.

Accessible Modal Sheet Widget uses Flutter's `FocusTraversalGroup` and `FocusTrap` (via a custom `FocusTrapScope` wrapper) to constrain keyboard and assistive technology focus to the sheet's widget subtree while it is visible. `showAccessibleSheet(title, content, triggerElement)` stores a reference to the triggering `FocusNode`, then calls `showModalBottomSheet` or `showDialog` with the wrapped content. On open, `announceSheetOpen(title)` delegates to `live-region-announcer.announceAssertive()` to immediately notify the screen reader of the new context. `trapFocus(context)` activates the `FocusTrapScope`, and initial focus is set to the sheet title widget or the first `focusable` descendant using `FocusScope.of(context).autofocus`.

On dismissal — whether via user action or programmatic close — `releaseFocus(targetElement)` deactivates the trap and calls `requestFocus()` on the stored trigger `FocusNode`. `announceSheetClose()` dispatches a polite announcement via `live-region-announcer`. Because `triggerElement` is a `FocusNode` reference, callers must ensure the triggering widget remains mounted for focus restoration to succeed; documentation should warn against ephemeral trigger widgets.

Responsibilities

  • Trap focus within the modal sheet while it is open
  • Place initial focus on the sheet title or first actionable element on open
  • Restore focus to the trigger element when the sheet is closed
  • Announce the sheet's purpose and available actions on open

Interfaces

showAccessibleSheet(title, content, triggerElement)
trapFocus(context)
releaseFocus(targetElement)
announceSheetOpen(title)
announceSheetClose()

Relationships

Dependencies (2)

Components this component depends on