Skip to content

a11y(2.4.3): focus-trap — capture and restore focus to the trigger element on close#3536

Open
rosanusi wants to merge 2 commits into
mainfrom
wcag/2.4.3-focus-trap-restoration
Open

a11y(2.4.3): focus-trap — capture and restore focus to the trigger element on close#3536
rosanusi wants to merge 2 commits into
mainfrom
wcag/2.4.3-focus-trap-restoration

Conversation

@rosanusi

Copy link
Copy Markdown
Contributor

Description

Fixes WCAG 2.2 SC 2.4.3 (Focus Order) in src/lib/utilities/focus-trap.ts. The shared focus-trap Svelte action — consumed by Modal, Drawer, Maximizable, and the command-palette Modal — trapped Tab correctly but never captured the pre-activation focus position, so closing any of these surfaces dropped focus to document.body. Keyboard users lost their place in the page on every modal/drawer close.

Three gaps closed:

  1. No capturesetFocus() now records document.activeElement into previouslyFocused before calling firstFocusable.focus(), but only on first activation (previouslyFocused === null) and not on MutationObserver re-runs (fromObserver=true), so in-trap DOM changes do not clobber the original trigger.

  2. No restore on deactivatecleanUp() now calls previouslyFocused.focus() (guarded by document.body.contains() in case the trigger was removed from the DOM while the trap was open) and then resets previouslyFocused = null so re-enabling captures fresh.

  3. MutationObserver path fixedonChange previously called cleanUp() on every DOM mutation inside the trap, which would have triggered premature focus restoration after this change. Extracted removeListeners() (listener teardown only) and changed onChange to call that instead of cleanUp().

Consumers updated automatically (no per-file change needed):

  • src/lib/holocene/modal.svelte (use:focusTrap={true})
  • src/lib/holocene/drawer.svelte (use:focusTrap={true})
  • src/lib/holocene/maximizable/maximizable.svelte (use:focusTrap={maximized})
  • src/lib/components/command-palette/modal.svelte (use:focusTrap={true})

Primitive-level fix cascades to cloud-ui-main on tarball repack.

Screenshots

No visual change. The fix affects keyboard focus position after closing a modal or drawer.

Design

N/A.

Testing

  • Modal: Tab to a workflow action button → open Cancel Workflow modal → Tab through contents → press Escape. Confirm focus returns to the action button.
  • Drawer: Open Activity Options drawer → Tab through → close. Confirm focus returns to the trigger.
  • Command palette: Press Cmd+K from any page → Tab into results → press Escape. Confirm focus returns to the previously focused element.
  • Maximizable: Activate maximize on a CodeBlock → Tab inside → close. Confirm focus returns to the maximize button.
  • Trigger removed from DOM: Open a modal whose trigger row disappears (e.g., cancel a workflow that is then filtered out). Confirm focus falls silently to body rather than throwing.
  • Trap still works: Tab forward from last focusable wraps to first; Shift+Tab from first wraps to last.
  • MutationObserver regression: Open a modal with dynamic content (e.g., form validation that adds/removes fields). Confirm focus stays inside the trap during content changes and is only restored on close.

Checklist

Docs

No documentation changes required.

A11y-Audit-Ref: 2.4.3-focus-restoration-on-close

…t on close

Keyboard users who open a Modal/Drawer/Maximizable and close it via Escape
had focus fall to document.body, forcing full re-navigation. Fix captures
document.activeElement before first focus() call and restores it in cleanUp().

The MutationObserver path (DOM changes inside the trap) now calls
removeListeners() directly instead of cleanUp() to avoid incorrectly
restoring focus on in-trap content changes.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
holocene Ready Ready Preview, Comment Jun 11, 2026 4:43pm

Request Review

@github-actions github-actions Bot added a11y Accessibility audit PR a11y:bucket-3 Bucket 3: engineer required a11y:sc-2.4.3 labels Jun 11, 2026
@rosanusi rosanusi marked this pull request as ready for review June 11, 2026 16:50
@rosanusi rosanusi requested a review from a team as a code owner June 11, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y:bucket-3 Bucket 3: engineer required a11y:sc-2.4.3 a11y Accessibility audit PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant