You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Pro 1.7.0 / OSS 1.12.0] ios:/android: variants leak into the other platform's bundle with Tailwind 4.3.3 — only the first class per hoisted @media block keeps its platform #668
With Tailwind 4.3.3 (which Pro 1.7.0 / OSS 1.12.0 now pin via @tailwindcss/node), ios: / android: / native: classes leak into the other platform's bundle. Only the first class inside each @media ios { … } / @media android { … } block keeps its platform tag; every other class in the block compiles as an unconditional style and is emitted in both the iOS and Android stylesheets.
In our app (Pro 1.7.0, upgraded from 1.6.0 yesterday) this shows as e.g. ios:underline ios:w-screen-content android:border-b android:border-foreground on a TextInput rendering both the iOS text underline and the Android full-width bottom border on iOS, and the iOS full-width sizing plus text underline on Android. ios:font-… android:font-… pairs are affected the same way. Nothing changed in our class strings; the regression came purely from the Tailwind bump inside uniwind.
Theme blocks (@media (prefers-color-scheme: …)) are unaffected because they contain a single :root child.
Root cause: Tailwind 4.3.3 hoists variants. 4.3.2 emitted .ios\:underline { @media ios { … } } per class; 4.3.3 emits one @media ios { .ios\:w-4 {…} .ios\:underline {…} } block. In packages/uniwind/src/bundler/css-processor/processor.ts the rule.type === 'media' branch pushes the media queries once, then does this.declarationConfig = this.getDeclarationConfig() after each child rule, so children 2..N see an empty mediaQueries and never get platform set. addMetaToStylesTemplate then keeps them for every platform. This is the sibling of #661 / #662 — that fix handled the flattened selector half of the 4.3.3 change; this is the hoisted at-rule half.
Fix (verified against the repro; full native output correct on both platforms):
Happy to open a PR with this plus a ProcessorBuilder test feeding the hoisted shape if that helps.
Steps to Reproduce
git clone https://github.com/invivek26/uniwind-platform-variant-leak-repro && cd uniwind-platform-variant-leak-repro
bun install (resolves uniwind 1.12.0 + @tailwindcss/node 4.3.3; the same processor code ships in Pro 1.7.0)
node repro.mjs — compiles global.css through uniwind's own compileCSS for ios and android and lists which platform-variant classes land in each stylesheet
Actual:
== ios bundle stylesheet contains:
ios:w-4 (platform-tagged=true)
ios:underline (platform-tagged=false) <- lost its platform
android:py-2 (platform-tagged=false) <- android class shipped to iOS
native:mt-1 (platform-tagged=true)
native:mb-2 (platform-tagged=false)
== android bundle stylesheet contains:
ios:underline (platform-tagged=false) <- ios class shipped to Android
android:border-b (platform-tagged=true)
android:py-2 (platform-tagged=false)
native:mt-1 (platform-tagged=true)
native:mb-2 (platform-tagged=false)
Control: forcing Tailwind back to 4.3.2 with overrides on the same uniwind 1.12.0 gives the expected output (every class tagged, no cross-platform entries) — see the README.
Same root cause also breaks responsive breakpoints, not just platform variants.
With the hoisted @media blocks, sm:/lg:/xl: after the first in a block compile
with minWidth: 0 and apply at every width. In our app tailwindcss 4.3.2 emitted
74 separate @media (width >= 40rem) blocks and 4.3.3 emitted one, so phones
started rendering the desktop layout. The same one-line fix resolves it.
Quick way to check a project without building to a device: compile the CSS
through @tailwindcss/node, run it through uniwind's ProcessorBuilder with
{ platform: 'native' }, and read stylesheets['sm:hidden'][0].minWidth — 0 means
the breakpoint was stripped.
Same root cause also breaks responsive breakpoints, not just platform variants. With the hoisted @media blocks, sm:/lg:/xl: after the first in a block compile with minWidth: 0 and apply at every width. In our app tailwindcss 4.3.2 emitted 74 separate @media (width >= 40rem) blocks and 4.3.3 emitted one, so phones started rendering the desktop layout. The same one-line fix resolves it.
Quick way to check a project without building to a device: compile the CSS through @tailwindcss/node, run it through uniwind's ProcessorBuilder with { platform: 'native' }, and read stylesheets['sm:hidden'][0].minWidth — 0 means the breakpoint was stripped.
Activity
Brentlok commentedon Sep 7, 2026
I've changed label to oss since it affects both versions
Brentlok commentedon Sep 7, 2026
Feel free to open PR with fix 🙌
I'm assigning this to you then, thanks in advance
phoique commentedon Sep 7, 2026
Yes, I am bug a similar issue. I resolved it by downgrading the versions of uniwind (1.12.0 =>1.11.0) and tailwindcss (4.3.3 =>4.3.2).
invivek26 commentedon Sep 7, 2026
Just created a pr with the fix @Brentlok 🫡
romanlesnikov commentedon Sep 29, 2026
Same root cause also breaks responsive breakpoints, not just platform variants.
With the hoisted @media blocks, sm:/lg:/xl: after the first in a block compile
with minWidth: 0 and apply at every width. In our app tailwindcss 4.3.2 emitted
74 separate
@media (width >= 40rem)blocks and 4.3.3 emitted one, so phonesstarted rendering the desktop layout. The same one-line fix resolves it.
Quick way to check a project without building to a device: compile the CSS
through @tailwindcss/node, run it through uniwind's ProcessorBuilder with
{ platform: 'native' }, and read stylesheets['sm:hidden'][0].minWidth — 0 means
the breakpoint was stripped.
Brentlok commentedon Sep 29, 2026
#686 fixes that as well
github-actions commentedon Oct 1, 2026
New release v1.12.1 is published: https://github.com/uni-stack/uniwind/releases/tag/v1.12.1