Describe the bug
The rtl: and ltr: variants from Tailwind CSS v4 are silently dropped during compilation. No error is produced — the styles simply never apply. Related to nativewind/nativewind#1433 but traced to a different root cause in the react-native-css v5 compiler.
Root cause
- Tailwind v4 generates
:dir(rtl) in the rtl: variant (confirmed in tailwindcss source)
- Lightningcss 1.30.1 downcompiles
:dir(rtl) to :is(:lang(ae), :lang(ar), ...) at the parse level
parseIsWhereComponents in selector-builder.ts has a case "dir" handler but no case "lang" handler
:lang() hits default: { return null; } → returns null
- Inside
:where(), [dir="rtl"] attribute selectors also return null (line ~397)
- ALL alternatives produce nothing → the entire rule is silently dropped
Key finding: Features.DirSelector does not help
Adding Features.DirSelector to lightningcss include list does NOT prevent the downcompile. Confirmed with a console.log verifying the flag IS loaded — the CSS output still contains :is(:lang(...)) instead of :dir(rtl). The downcompile happens at the parse level, before feature flags are consulted.
Proposed fix
Add a case "lang" handler to parseIsWhereComponents. Lightningcss expands :dir(rtl) into a fixed set of 19 RTL languages. Map these back to [=, "dir", "rtl"], leveraging the existing runtime case "dir" check against I18nManager.isRTL:
case "lang": {
const RTL_LANGUAGES = new Set([
"ae","ar","arc","bcc","bqi","ckb","dv","fa","glk",
"he","ku","mzn","nqo","pnb","ps","sd","ug","ur","yi"
]);
const direction = component.languages?.[0] && RTL_LANGUAGES.has(component.languages[0]) ? "rtl" : "ltr";
queries ??= [{ specificity: [] }];
queries.forEach(query => {
getMediaQuery(query).push(["=", "dir", direction]);
});
return parseIsWhereComponents(type, selector, index + 1, queries);
}
Reproduction
Steps:
npm install && npx expo run:ios
- Press "Force RTL" button
- Fully restart the app
- The
rtl:bg-amber-400 class should make the background amber, but it stays blue
Expected behavior
rtl:bg-amber-400 should apply background-color: amber-400 when I18nManager.isRTL === true.
Workaround
Use patch-package with the patch from the fix branch.
Environment
- react-native-css: 3.0.7
- nativewind: 5.0.0-preview.4
- react-native: 0.86.0
- expo: 57
Describe the bug
The
rtl:andltr:variants from Tailwind CSS v4 are silently dropped during compilation. No error is produced — the styles simply never apply. Related to nativewind/nativewind#1433 but traced to a different root cause in the react-native-css v5 compiler.Root cause
:dir(rtl)in thertl:variant (confirmed intailwindcsssource):dir(rtl)to:is(:lang(ae), :lang(ar), ...)at the parse levelparseIsWhereComponentsinselector-builder.tshas acase "dir"handler but nocase "lang"handler:lang()hitsdefault: { return null; }→ returns null:where(),[dir="rtl"]attribute selectors also return null (line ~397)Key finding: Features.DirSelector does not help
Adding
Features.DirSelectorto lightningcssincludelist does NOT prevent the downcompile. Confirmed with a console.log verifying the flag IS loaded — the CSS output still contains:is(:lang(...))instead of:dir(rtl). The downcompile happens at the parse level, before feature flags are consulted.Proposed fix
Add a
case "lang"handler toparseIsWhereComponents. Lightningcss expands:dir(rtl)into a fixed set of 19 RTL languages. Map these back to[=, "dir", "rtl"], leveraging the existing runtimecase "dir"check againstI18nManager.isRTL:Reproduction
Steps:
npm install && npx expo run:iosrtl:bg-amber-400class should make the background amber, but it stays blueExpected behavior
rtl:bg-amber-400should applybackground-color: amber-400whenI18nManager.isRTL === true.Workaround
Use
patch-packagewith the patch from the fix branch.Environment