Summary
When the global Tailwind important flag is enabled (@import "tailwindcss" important;), state variants (active:, focus:, disabled:) no longer override a base declaration of the same property on native. The base value wins permanently while the state is active.
It works correctly on web (the active: pseudo-class wins on specificity), and it works on native without the important flag.
Originally filed as "active:bg-* not applied on native" — narrowed down: the cause is the global important flag interacting with the native resolver's precedence (details below).
Reproduction
Minimal repro: https://github.com/joernroeder/uniwind-bug-report-584
yarn && yarn ios # or yarn android
Press & hold each box:
| Box |
Classes |
Expected |
Actual |
| A |
bg-red-500 active:bg-blue-500 |
turns blue |
stays red ← bug |
| B |
active:bg-green-500 (no base) |
turns green |
turns green ✓ |
| C |
bg-red-500 active:opacity-40 |
dims |
dims ✓ |
The trigger is the global flag in src/global.css:
@import "tailwindcss" important;
B and C show the active: mechanism itself works — the failure is specific to a state variant overriding an !important base of the same property. With the global flag, every utility is !important, so no state variant can override a base color on native. active:opacity-* works only because nothing sets a competing base opacity. Remove important from the import and A turns blue as expected.
Environment
- uniwind 1.9.0 (also reproduces on 1.8.0)
- tailwindcss 4.3.0
- expo 56, react-native 0.85, Hermes — iOS and Android
Root cause
src/core/native/store.ts, the per-property precedence check:
if (
previousBest
&& (
previousBest.minWidth > style.minWidth
|| previousBest.complexity > style.complexity
|| previousBest.importantProperties.includes(property) // ← short-circuits before complexity
)
) {
continue
}
className entries resolve left-to-right. The base bg-red-500 (complexity: 0) registers backgroundColor first; with the flag its importantProperties includes backgroundColor. When pressed, active:bg-blue-500 (complexity: 1, since active !== null) is strictly more specific and should win — but importantProperties.includes(property) is OR'd before the complexity comparison, so the check short-circuits to continue and the active value is discarded. Importance is treated as an absolute veto, ignoring specificity.
Proposed fix
Only let an !important previousBest veto an override that is not more specific — gate the important term on complexity:
|| (
previousBest.importantProperties.includes(property)
&& previousBest.complexity >= style.complexity
)
This matches CSS semantics (a state pseudo-class beats a base rule of equal importance) and is order-independent (the complexity > term handles the reverse class order). The repro ships this as a Yarn patch (.yarn/patches/uniwind-npm-1.9.0-*.patch); with it applied, box A turns blue. Verified:
- base+active, both
!important → active wins on press ✔
- base+active, neither
!important → unchanged (active wins) ✔
!important base vs non-important same-complexity later → unchanged (base still vetoes) ✔
Happy to open a PR with this change.
Summary
When the global Tailwind
importantflag is enabled (@import "tailwindcss" important;), state variants (active:,focus:,disabled:) no longer override a base declaration of the same property on native. The base value wins permanently while the state is active.It works correctly on web (the
active:pseudo-class wins on specificity), and it works on native without theimportantflag.Reproduction
Minimal repro: https://github.com/joernroeder/uniwind-bug-report-584
Press & hold each box:
bg-red-500 active:bg-blue-500active:bg-green-500(no base)bg-red-500 active:opacity-40The trigger is the global flag in
src/global.css:B and C show the
active:mechanism itself works — the failure is specific to a state variant overriding an!importantbase of the same property. With the global flag, every utility is!important, so no state variant can override a base color on native.active:opacity-*works only because nothing sets a competing baseopacity. Removeimportantfrom the import and A turns blue as expected.Environment
Root cause
src/core/native/store.ts, the per-property precedence check:className entries resolve left-to-right. The base
bg-red-500(complexity: 0) registersbackgroundColorfirst; with the flag itsimportantPropertiesincludesbackgroundColor. When pressed,active:bg-blue-500(complexity: 1, sinceactive !== null) is strictly more specific and should win — butimportantProperties.includes(property)is OR'd before the complexity comparison, so the check short-circuits tocontinueand the active value is discarded. Importance is treated as an absolute veto, ignoring specificity.Proposed fix
Only let an
!importantpreviousBestveto an override that is not more specific — gate the important term on complexity:This matches CSS semantics (a state pseudo-class beats a base rule of equal importance) and is order-independent (the
complexity >term handles the reverse class order). The repro ships this as a Yarn patch (.yarn/patches/uniwind-npm-1.9.0-*.patch); with it applied, box A turns blue. Verified:!important→ active wins on press ✔!important→ unchanged (active wins) ✔!importantbase vs non-important same-complexity later → unchanged (base still vetoes) ✔Happy to open a PR with this change.