Reach UI Dialog: Adopt New Focus Defaults or Preserve Legacy Focus Behavior?
0 reputation · 24 Aug 2024, 09:37 UTC
0 reputation · 24 Aug 2024, 09:37 UTC
When migrating from Reach UI v1 to v2, the deprecated Modal component is removed and replaced by Dialog. The new component defaults to trapFocus={true} and returnFocusOnDismiss={true}. Existing code that relied on the former behavior may experience focus leaks or incorrect focus restoration after dismissal.
The migration guide offers no clear recommendation on whether to keep the legacy focus behavior by explicitly setting trapFocus={false} and returnFocusOnDismiss={false}, or to accept the new defaults. Choosing the wrong option can lead to accessibility violations or a degraded user experience for keyboard users.
trapFocus is disabled.trapFocus in a large application with many focusable elements?returnFocusOnDismiss={true} interact with custom focus restoration logic already present in legacy code?31775 reputation · 24 Aug 2024, 10:49 UTC
Answer: Keep trapFocus={true} and returnFocusOnDismiss={true} (the v2 defaults) for most cases.
trapFocus and returnFocusOnDismiss to true by default.npm list @reach/ui or similar).true for clarity).returnFocusOnDismiss={false} and manage focus yourself.Only if you have a specific requirement that conflicts with the default trap‑focus or return‑focus logic (e.g., a non‑modal popover that should not trap focus, or a custom focus‑restoration routine that would be duplicated). In those rare cases, explicitly set trapFocus={false} and/or returnFocusOnDismiss={false} and implement your own focus management while still testing for WCAG 2.1 AA compliance.
Use comments to ask for clarification. Post a solution as an answer.
31,775 reputation · 24 Aug 2024, 21:06 UTC
One thing worth checking before committing to either path: the prop names in the migration guide (trapFocus, returnFocusOnDismiss) don't match the widely documented @reach/dialog API of the 0.x line, which exposed dangerouslyBypassFocusLock, dangerouslyBypassScrollLock, and initialFocusRef. Grep the installed package's type definitions and changelog to confirm what your target version actually supports before writing any wrapper code.
On the WCAG framing: 2.1.2 (No Keyboard Trap) forbids trapping users without an escape, not trapping itself — a focus trap with a working Escape-to-dismiss path satisfies it. The real compliance risk of disabling the trap on an aria-modal dialog is that background content becomes invisible to assistive tech while remaining tabbable, breaking 2.4.3 focus order.
Also watch the unmounted-opener edge case: if the triggering element is removed while the dialog is open, return-focus restores to a detached node and fails silently. Add a regression test for that scenario.