Skip to content

fix(a11y): trap keyboard focus and restore focus on close in Modal (#42, #9) - #83

Open
ghzhost wants to merge 1 commit into
StellarSend:mainfrom
ghzhost:fix/modal-focustrap-restore-focus
Open

ghzhost wants to merge 1 commit into
StellarSend:mainfrom
ghzhost:fix/modal-focustrap-restore-focus

Conversation

@ghzhost

@ghzhost ghzhost commented Sep 2, 2026

Copy link
Copy Markdown

Overview

Fixes #42 (and resolves #9).

Modal is the shared dialog primitive behind every confirmation modal in the app (ConfirmModal, BatchConfirmModal, ConfirmEscrowModal, ConfirmSubscriptionModal, and Freighter connect modals). Prior to this fix:

  1. Focus was not contained within the modal dialog when tabbing, allowing keyboard navigation to escape into background page elements behind the dialog.
  2. Focus was not restored to the triggering element upon closing the modal.
  3. FocusTrap.tsx only focused the first element on mount and did not actively trap Tab/Shift+Tab key cycles.

Changes

  • src/components/common/FocusTrap.tsx:
    • Implemented full Tab and Shift+Tab cyclic focus trapping across all interactive/focusable elements (button, input, select, textarea, a[href], [tabindex]).
    • Added previous document.activeElement recording and automatic focus restoration upon deactivation / unmount when restoreFocus is enabled.
  • src/components/ui/Modal.tsx:
    • Wrapped modal dialog contents in <FocusTrap active={isOpen} restoreFocus={true}>.
  • src/components/ui/Modal.test.tsx:
    • Added unit tests verifying focus containment across Tab / Shift+Tab cycles and proper focus restoration to the trigger button upon closing via Escape.

Validation

  • npm test passing (all 28 test suites, 115 tests passed).
  • npm run build completed successfully without any TypeScript or bundling errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant