0.5.7 overlay toggle switch (and other translate/rotate utilities) render broken: Tailwind v4 @property rules unregistered inside shadow DOM

Author: hideuchCreated Jul 1, 2026Updated Jul 1, 2026

Symptom

In [email protected], overlay UI utilities that rely on Tailwind v4's translate / rotate / scale shorthand render incorrectly. The most visible symptom is the outline-render toggle switch: in the OFF state the white thumb pops out of the track and hangs below it. The same class of bug affects the resize handles, slider thumb, and expand arrow — anywhere the overlay CSS uses @apply -translate-* / translate-*.

Reproduction on Chrome 149 with a Tailwind v4 host (also reproduces with a non-Tailwind host, verified against a Panda CSS project — the host stylesheet is irrelevant since the overlay is inside a shadow DOM).

Downgrading to [email protected] restores correct rendering (v0.5.6 is on Tailwind v3, which uses preflight-seeded --tw-translate-* rather than @property).

Screenshots

Image

Root cause

React Scan mounts its overlay UI in a shadow DOM and injects the bundled Tailwind stylesheet as a <style> element inside that shadow root (packages/scan/src/core/index.ts). Tailwind v3 → v4 in 0.5.7 changed how -translate-y-1/2 etc. compile: instead of the preflight-seeded --tw-translate-* variables, v4 emits 62 @property rules to declare the initial-value (0).

Chromium does not register @property rules declared inside a shadow-DOM stylesheet. The CSSOM parses them into CSSPropertyRules (visible via stylesheet.cssRules), but the custom-property registry is empty for that shadow tree. Consequently --tw-translate-x has no computed value, and

css
translate: var(--tw-translate-x) var(--tw-translate-y);

collapses to translate: none.

Measurement on .react-scan-toggle > div::before in the OFF state (react-scan 0.5.7, Chrome 149):

property value
translate none
--tw-translate-x "" (empty)
--tw-translate-y calc(calc(1 / 2 * 100%) * -1)

For the ON state, the input:checked + div::before selector applies translate-x-full which explicitly sets --tw-translate-x: 100%, so translate resolves — that's why the ON state visually looks OK while OFF is broken.

Document-level @property registrations do cascade into a shadow tree, verified in a minimal repro on Chrome 149.

Spec: CSS Properties and Values API L1.

Proposed fix (branch available)

I have a one-file fix on my fork: hidenari-pixel/react-scan@fix/shadow-dom-property-registration (diff)

Approach: in initRootContainer(), extract every @property --…{…} block from the bundled overlay CSS with a regex before injecting the stylesheet into the shadow root, and mount just those rules under a <style data-react-scan="property-registrations"> in document.head. The remainder still goes into the shadow root as before.

After the fix, the same ::before in the OFF state resolves to:

property value
translate 0px -50%
--tw-translate-x 0
--tw-translate-y calc(calc(1 / 2 * 100%) * -1)

and 62/62 @property rules move from the shadow tree to document.head.

  • Change is ~14 lines in packages/scan/src/core/index.ts plus a changeset entry
  • pnpm --filter react-scan build succeeds
  • pnpm test:e2e — 47/47 pass, including the outline toggle instrumentation specs from #459
  • Manual verification in kitchen-sink on Chrome 149: toggle thumb renders centered in both OFF and ON states

Note

I tried to open this as a PR but hit "An owner of this repository has limited the ability to open a pull request to users that are collaborators on this repository." Happy to open the PR the moment the interaction limit is lifted, or invited as a collaborator, or feel free to cherry-pick the commit directly:

bash
git remote add hidenari-pixel https://github.com/hidenari-pixel/react-scan.git
git fetch hidenari-pixel fix/shadow-dom-property-registration
git cherry-pick 2407c8b

Related open work: PR #458 (React port of the toolbar UI) may incidentally sidestep this if it moves off Tailwind v4 utility classes, but it is still open and orthogonal.