Skip to content

fix(dismissable): restore layer style mirror after framework style rewrites - #3177

Open
spokodev wants to merge 1 commit into
chakra-ui:mainfrom
spokodev:fix/drawer-backdrop-layer-vars
Open

fix(dismissable): restore layer style mirror after framework style rewrites#3177
spokodev wants to merge 1 commit into
chakra-ui:mainfrom
spokodev:fix/drawer-backdrop-layer-vars

Conversation

@spokodev

Copy link
Copy Markdown

Fixes #3148

Problem

The layer stack mirrors its metadata (--layer-index, --nested-layer-count, --z-index, data-nested, data-has-nested) onto style targets like Drawer.Backdrop imperatively via style.setProperty, but only when layers are added or removed. Frameworks own the same elements' style and replace the inline style wholesale on re-render — for the drawer, every drag pointermove updates --drawer-swipe-progress on the backdrop, so the first drag frame erases the mirrored variables and z-index: calc(100 + var(--layer-index)) breaks for the rest of the layer's lifetime.

Fix

Watch each mirrored element's style attribute with a MutationObserver and re-apply the metadata when it disappears while the layer is still in the stack. The observer is a no-op when the metadata is present (no loops: re-applying triggers one callback that sees --layer-index set and returns), keeps whatever the framework wrote (--drawer-swipe-progress survives), and is disconnected when the layer is removed so cleared styles stay cleared.

Tests

restores mirrored metadata after a framework rewrite of the style attribute fails on main (--layer-index stays empty after setAttribute("style", ...)) and passes with this change. A second test pins that removal still clears the mirror and the observer stops restoring. Dismissable suite: 15 passed.

@changeset-bot

changeset-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1d50f28

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

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

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

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
zag-nextjs Ready Ready Preview Jun 12, 2026 2:08pm
zag-solid Ready Ready Preview Jun 12, 2026 2:08pm
zag-svelte Ready Ready Preview Jun 12, 2026 2:08pm
zag-vue Ready Ready Preview Jun 12, 2026 2:08pm
zag-website Ready Ready Preview Jun 12, 2026 2:08pm

Request Review

@kalisaNkevin

Copy link
Copy Markdown
Member

Went through this one in detail — the fix and the new test coverage both hold up.

The approach (a MutationObserver per style target, keyed by a WeakMap) correctly solves #3148: it watches the style attribute and reapplies the layer-stack CSS vars when a framework re-render wipes them, which is exactly what was happening to Drawer.Backdrop during drag. The self-healing guard (if target.style.getPropertyValue("--layer-index") !== "") return) is what keeps this from looping on its own writes, so that edge case looks handled deliberately rather than by luck.

The test file is also a real addition on its own merits — layer-stack.ts had zero unit tests before this, and the new suite covers add/remove/dismiss, pointer-blocking, branches, the recentlyRemoved race handling, and the mirror-restoration behavior itself, including a test that it does not resurrect metadata after a layer is removed (the exact regression a sloppier version of this fix could introduce).

One thing worth a second look, not blocking: syncLayers() now attaches an observer to every layer's own node, not just styleTargets like the backdrop. Probably the right defensive call since any layer node could get its style clobbered the same way, but worth confirming that's intentional rather than broader than needed.

The bigger gap is that CI has never actually run on this commit — total_count: 0 workflow runs via the Actions API. Looks like a maintainer needs to approve running workflows for this PR before there's any automated signal on lint/types/tests, separate from how it reads on manual review.

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.

Drawer.Backdrop loses layer stack variables after dragging content

2 participants