Settings modal lacks focus trap — has role="dialog" and aria-modal="true" but no focus management, so Tab can move focus to the underlying page #168
Labels
No labels
blocked:commit-ready-failed
blocked:gate-fetch
blocked:gate-lint
blocked:gate-plan
blocked:gate-research
blocked:needs-attention
blocked:needs-decision
blocked:needs-human
blocked:no-changes
blocked:verify-failed
bug
deferred
feature-request
in-progress
needs-decompose
needs-triage
quickfix
security/medium
triage-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/Kimi_Agent_Brother_10_Digitizing_App#168
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Finding
src/components/workspace/SettingsModal.tsx:64-65has the right ARIA attributes:But there is no focus trap. A user opens the Settings modal, presses Tab repeatedly, and focus moves to elements behind the modal — the Navbar, the Workspace's tool buttons, etc. The visual overlay is shown; the keyboard focus is not contained.
A screen reader user opens the modal and the screen reader announces the modal; they Tab to a button that they can't see (it's behind the backdrop). A keyboard user with a modal open can't Tab to the close button because Tab is moving focus through hidden elements.
Where
src/components/workspace/SettingsModal.tsx:64-65(the role and aria-modal). No focus management code anywhere.Other modals in the codebase:
ExportDialog(src/components/workspace/ExportDialog.tsx): hasrole="dialog" aria-modal="true"(line 234) — same gapNewFolderDialog: per #134, has duplicate-name detection; verify if it has focus trapThreadLibraryView(src/components/workspace/ThreadLibraryView.tsx): hasrole="dialog" aria-modal="true"(line 39) — same gapSuggested fix shape
Use the standard focus-trap pattern (or a library like
focus-trap-react):A minimal implementation is ~30 lines per modal. A
useFocusTrapcustom hook (one file, ~50 lines) that the three modals all import keeps the implementation DRY.Severity
High. Accessibility is broken for keyboard and screen reader users. The ARIA attributes claim a modal but the implementation doesn't back it up.
Discovered by
Gap analysis (2026-10-02) of modal accessibility.
Related
Fixed in
7a95d7d(fix(#168): trap focus and Escape inside Settings, Export, Thread Library modals).Added
src/hooks/useFocusTrap.ts(~50 lines, no new dep) and wired it into all three modals: SettingsModal, ExportDialog, ThreadLibraryView. NewFolderDialog was already on Radix Dialog so it gets focus management free.Tests: 11 hook unit tests + 2 integration tests per modal (Tab-wrap, Escape closes). 706/706 vitest passing, tsc clean, eslint clean.