Skip to content

Conversation

@nikparo
Copy link
Contributor

@nikparo nikparo commented Sep 9, 2025

Closes #

📝 Description

Add a brief description

activeElement checks are sometimes done using document.activeElement !== element, which is an issue for apps containing shadow-dom elements such as web-components. This prevents Zag from being used in e.g. default Lit setups (#2681).

  • Add isActiveElement to @zag-js/dom-query for consistent activeElement checks.
  • getActiveElement is tweaked to allow for focusable (tabindex="0") web components that do not contain

⛳️ Current behavior (updates)

Please describe the current behavior that you are modifying

Focus trap and other utilities / machines do not find the focused elment within a shadow-dom root, resulting in unwanted behaviour.

🚀 New behavior

Please describe the behavior or changes this PR adds

Focused elements are consistently found.

💣 Is this a breaking change (Yes/No):

No

📝 Additional Information

@changeset-bot
Copy link

changeset-bot bot commented Sep 9, 2025

🦋 Changeset detected

Latest commit: a51a77c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 77 packages
Name Type
@zag-js/dom-query Minor
@zag-js/number-input Minor
@zag-js/date-picker Minor
@zag-js/focus-trap Minor
@zag-js/tags-input Minor
@zag-js/core Minor
@zag-js/accordion Minor
@zag-js/angle-slider Minor
@zag-js/avatar Minor
@zag-js/carousel Minor
@zag-js/checkbox Minor
@zag-js/clipboard Minor
@zag-js/collapsible Minor
@zag-js/color-picker Minor
@zag-js/combobox Minor
@zag-js/dialog Minor
@zag-js/editable Minor
@zag-js/file-upload Minor
@zag-js/floating-panel Minor
@zag-js/hover-card Minor
@zag-js/listbox Minor
@zag-js/menu Minor
@zag-js/navigation-menu Minor
@zag-js/pagination Minor
@zag-js/password-input Minor
@zag-js/pin-input Minor
@zag-js/popover Minor
@zag-js/presence Minor
@zag-js/progress Minor
@zag-js/qr-code Minor
@zag-js/radio-group Minor
@zag-js/rating-group Minor
@zag-js/scroll-area Minor
@zag-js/select Minor
@zag-js/signature-pad Minor
@zag-js/slider Minor
@zag-js/splitter Minor
@zag-js/steps Minor
@zag-js/switch Minor
@zag-js/tabs Minor
@zag-js/timer Minor
@zag-js/toast Minor
@zag-js/toggle-group Minor
@zag-js/toggle Minor
@zag-js/tooltip Minor
@zag-js/tour Minor
@zag-js/tree-view Minor
@zag-js/auto-resize Minor
@zag-js/dismissable Minor
@zag-js/focus-visible Minor
@zag-js/i18n-utils Minor
@zag-js/interact-outside Minor
@zag-js/popper Minor
@zag-js/remove-scroll Minor
@zag-js/scroll-snap Minor
@zag-js/preact Minor
@zag-js/react Minor
@zag-js/solid Minor
@zag-js/svelte Minor
@zag-js/vue Minor
@zag-js/async-list Minor
@zag-js/file-utils Minor
@zag-js/anatomy-icons Minor
@zag-js/anatomy Minor
@zag-js/docs Minor
@zag-js/store Minor
@zag-js/types Minor
@zag-js/aria-hidden Minor
@zag-js/collection Minor
@zag-js/color-utils Minor
@zag-js/utils Minor
@zag-js/date-utils Minor
@zag-js/highlight-word Minor
@zag-js/json-tree-utils Minor
@zag-js/live-region Minor
@zag-js/rect-utils Minor
@zag-js/stringify-state Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Sep 9, 2025

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

Project Deployment Preview Updated (UTC)
zag-nextjs Ready Ready Preview Sep 9, 2025 11:02am
zag-solid Ready Ready Preview Sep 9, 2025 11:02am
zag-svelte Ready Ready Preview Sep 9, 2025 11:02am
zag-vue Ready Ready Preview Sep 9, 2025 11:02am
zag-website Ready Ready Preview Sep 9, 2025 11:02am

Comment on lines +40 to +45
export function isActiveElement(element: Element | null | undefined): boolean {
if (!element) return false
const rootNode = element.getRootNode() as Document | ShadowRoot

return getActiveElement(rootNode) === element
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure if this function should be used in the createScope functions or not. Currently those check both whether the element is active and within the scope root. Is that intentional? It seems to me like that would be fragile e.g. if the component uses some kind of portals or renders something outside the scope root.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my thought as well. So yes, let’s add it to the scope and use the scope everywhere if possible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like this you mean? 8c8537d + a51a77c
With this isActiveElement is only used directly in dom-query and core.

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.

2 participants