From 101395a4f43093cee3eb0f169b06ce0cbf9ca10f Mon Sep 17 00:00:00 2001 From: Erica Gucciardo Date: Mon, 9 Mar 2026 10:16:39 -0400 Subject: [PATCH] fix(TrapFocus): don't refocus when another component intentionally moves focus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MutationObserver was forcibly pulling focus back into the flyout whenever a DOM mutation occurred while focus was outside the root. This broke external components (e.g. Radix Select) that render a portal and legitimately move focus out of the trap. The observer's original intent is to recover from accidental focus loss caused by a focused element being removed from the DOM — in which case the browser resets activeElement to document.body. Adding a guard for this condition preserves that behavior while leaving intentional focus moves (activeElement is a real element) untouched. Co-Authored-By: Claude Sonnet 4.6 --- packages/utilities/src/a11y/TrapFocus.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/utilities/src/a11y/TrapFocus.ts b/packages/utilities/src/a11y/TrapFocus.ts index b1e9023f..277685d6 100644 --- a/packages/utilities/src/a11y/TrapFocus.ts +++ b/packages/utilities/src/a11y/TrapFocus.ts @@ -144,6 +144,10 @@ class TrapFocus { // Focus stayed inside the wrapper, no need to refocus if (this.#root.contains(currentActiveElement)) return; + // Focus was intentionally moved to another component (e.g. a portal opened from inside the trap). + // Only refocus when focus was dropped (fell back to body), not when another component took it. + if (document.activeElement && document.activeElement !== document.body) return; + const focusable = getFocusableElements(this.#root, { additionalElement: includeTrigger ? trigger : undefined, });