Skip to content

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

Description

@hidenariTakeuchi

Symptom

In react-scan@0.5.7, 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 react-scan@0.5.6 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

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 test(e2e): deep Playwright coverage for the React Scan overlay UI #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:

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions