Skip to content

feat: Add focus trap and cancel auto-focus on modals#1187

Open
eduardomozart wants to merge 7 commits intognmyt:mainfrom
eduardomozart:patch-13
Open

feat: Add focus trap and cancel auto-focus on modals#1187
eduardomozart wants to merge 7 commits intognmyt:mainfrom
eduardomozart:patch-13

Conversation

@eduardomozart
Copy link
Copy Markdown
Contributor

@eduardomozart eduardomozart commented Feb 25, 2026

📋 Description

Improve keyboard accessibility in DialogProvider: add cancelBtnRef and autofocus logic so the cancel button is focused when the confirmation dialog is shown; otherwise, focus the first focusable element in the dialog. Implement handleKeyDownTrap to keep Tab/Shift+Tab cycling inside either the main dialog or the confirm node, attach it to the dialog area via onKeyDown, and add the cancel button ref. This prevents focus leakage and ensures proper keyboard navigation for the confirm flow.

SonarQubeCloud Feedback

  1. Non-interactive elements should not be assigned mouse or keyboard event listeners.
  • Sonar sometimes flags this if a keyboard/mouse handler is attached to an element that’s not interactive by default (e.g., a <div>).
  • But <dialog> is natively interactive, designed to be keyboard-accessible (MDN: HTML <dialog> element); they may be over-zealous or unaware of all HTML5 semantics (false positive).
  • Attaching keyboard handlers like onKeyDown is standard accessibility practice for modal dialogs, enabling features like focus trap and Escape to close.
  • Modern browser support for <dialog> is robust, and fallback handling/polyfill can be added if legacy support is needed.

📋 Tests

  • Open any modal in the app (Identity deletion, Provider deletion, Server deletion, etc.).
  • Tab cycles focus within the modal, never escaping to the rest of the page.
  • Cancel button is auto-focused on modal open, and the focus ring is visible.
  • Escape closes the modal as expected.

🚀 Changes made to ...

  • 🔧 Server
  • 🖥️ Client
  • 📚 Documentation
  • 🔄 Other: ___

✅ Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have looked for similar pull requests in the repository and found none
  • This pull request does not contain translations (translations are managed through Crowdin)

🔗 Related Issues

Closes #1179

Eduardo Oliveira added 3 commits February 24, 2026 21:28
Improve keyboard accessibility in DialogProvider: add cancelBtnRef and autofocus logic so the cancel button is focused when the confirmation dialog is shown, otherwise focus the first focusable element in the dialog. Implement handleKeyDownTrap to keep Tab/Shift+Tab cycling inside either the main dialog or the confirm node, attach it to the dialog area via onKeyDown, and add the cancel button ref. This prevents focus leakage and ensures proper keyboard navigation for the confirm flow.
Use Array.prototype.at(-1) to get the last focusable element, making the focus trap clearer and more robust. Make the dialog area focusable and announceable by adding tabIndex=-1, role="dialog", and aria-modal="true" so keyboard and screen reader users can interact correctly. Add :focus-visible styles in the dialog Sass to provide a clear keyboard focus indicator. Also tidy up trailing newline in the stylesheet.
Replace the wrapper <div class="dialog-area"> with a native <dialog> element and bind its open attribute to isVisible. Preserve existing animation classes, refs, onKeyDown trap and animation end handling; adjust closing markup accordingly. Removed explicit tabIndex, role and aria-modal attributes to rely on the native dialog semantics.
@eduardomozart eduardomozart changed the title feat: Add focus trap and cancel auto-focus feat: Add focus trap and cancel auto-focus on modals Feb 25, 2026
Eduardo Oliveira added 4 commits February 24, 2026 22:15
When the dialog confirm/cancel view is shown, add a .focus-outline class to the cancel button after calling focus() so the focus ring is visually forced for programmatic focus. Add SASS rules to apply an outline for button:focus, button:focus-visible, and button.focus-outline using colors.$primary (with outline-offset). Adjust dialog button hover/focus styles accordingly. This ensures a visible focus indicator for keyboard and programmatic focus scenarios.
Prefer focusing the first button inside .btn-area when the dialog opens (so primary/action buttons receive immediate focus). If no .btn-area buttons are found, fall back to the previous behavior of focusing the first general focusable element.
Reorder the querySelectorAll focus selector in Dialog.jsx to list input, select, and textarea before buttons and links. This ensures form fields are prioritized when determining the first focusable element (updated in both initial focus and focus-trap logic). No other behavior changes.
Prioritize focusing input/select/textarea and elements with valid tabindex when a dialog opens, and fall back to buttons/links if no input-like elements are present. Add a comment clarifying that querySelectorAll returns matches in document order, and reorder the focusable selector used elsewhere in the dialog to reflect the intended traversal and filtering behavior.
@sonarqubecloud
Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Implement Focus Trap and auto-focus "Cancel" on identity and provider deletion modals

1 participant