fix: propagate inlineRem and Metro config to compiler (Tailwind v4) (#316)#336
Open
maxencehenneron wants to merge 2 commits intonativewind:mainfrom
Open
fix: propagate inlineRem and Metro config to compiler (Tailwind v4) (#316)#336maxencehenneron wants to merge 2 commits intonativewind:mainfrom
maxencehenneron wants to merge 2 commits intonativewind:mainfrom
Conversation
Two related bugs caused Tailwind v4's text utilities to render at the
wrong size on native. Both manifest in v4 specifically and are benign
on v3, because v3 emits direct `rem` values while v4 routes them
through CSS custom properties.
Bug 1 — Metro transformer drops `withReactNativeCSS` options
-----------------------------------------------------------
`withReactNativeCSS()` writes its options to
`config.transformer.reactNativeCSS`, which Metro surfaces as
`config.reactNativeCSS` in the transformer. The transformer was
instead spreading `options.reactNativeCSS`, a key that nothing ever
writes to — `JsTransformOptions` is per-request context built by
Metro, and `config.transformer.*` never flows into it.
Result: every option passed to `withReactNativeCSS` (including
`inlineRem`) was silently dropped before reaching `compile()`.
Bug 2 — regex-discovered rem isn't propagated downstream
--------------------------------------------------------
`compile()` contains a fallback that scans the CSS for
`:root { font-size: Npx }` when `inlineRem` isn't explicitly set,
so users can configure rem scaling directly from CSS. The discovered
value was only wired into the first-pass lightningcss `Length`
visitor via a local `effectiveRem`. It was never written back into
`options`, so the second code path — `parseLength()` in
`declarations.ts`, which reads `inlineRem` from
`builder.getOptions()` — kept using the default 14.
This is invisible on Tailwind v3, which emits
`.text-base { font-size: 1rem }` directly: the `1rem` is a proper
Length node, so the first-pass visitor handles it and scales
correctly. It breaks on Tailwind v4, which emits
`:root { --text-base: 1rem }` + `.text-base { font-size: var(--text-base) }`.
The `1rem` inside the custom property is a token-list, inlined
verbatim by `inlineVariables`, then resolved via `parseLength` in
the second pass — where it reads the stale default.
Net effect on v4: every `text-*` utility was sized against rem=14
regardless of what `:root { font-size }` declared or what the user
configured, while direct `.my-class { font-size: 1.5rem }` rules
scaled correctly. The divergence is why the bug evaded isolated tests.
The fix
-------
- metro-transformer.ts: read `reactNativeCSS` from `config`, not
`options`. Drop the unreachable `options.reactNativeCSS` spread
and its type intersection.
- compiler.ts: after resolving `effectiveRem`, write it back to
`options.inlineRem` so the builder's downstream consumers see
the same value as the first-pass visitor.
Contributor
Author
|
Fixes (#316) for tailwind v4. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes Tailwind v4 rem-scaling issues on React Native by ensuring compiler configuration (notably inlineRem) actually reaches all compilation paths, including the second-pass length parsing used after CSS variable inlining.
Changes:
- Fix Metro transformer to read
withReactNativeCSS()options fromconfig.reactNativeCSSrather than per-request transform options. - Persist the regex-discovered
:root { font-size: Npx }rem value back ontooptions.inlineRemso the second-passparseLength()path uses the same effective rem multiplier.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/metro/metro-transformer.ts | Propagates compiler options from Metro config into compile() so withReactNativeCSS() options aren’t dropped. |
| src/compiler/compiler.ts | Writes discovered effective rem back into compiler options to keep first-pass and second-pass rem handling consistent. |
7444258 to
46ef4c4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes two related bugs that cause Tailwind v4's text utilities to render at the wrong size on native. Both manifest specifically on v4 and are benign on v3 — v3 emits direct
remvalues while v4 routes them through CSS custom properties, which traverse a different code path in the compiler.Bug 1 — Metro transformer drops
withReactNativeCSSoptionswithReactNativeCSS()writes its options toconfig.transformer.reactNativeCSS, which Metro surfaces asconfig.reactNativeCSSinside the transformer. The transformer was instead spreadingoptions.reactNativeCSS— a key that nothing ever writes to.JsTransformOptionsis per-request context built by Metro's server (platform, dev, hot, etc.);config.transformer.*never flows into it.Result: every option passed to
withReactNativeCSS(includinginlineRem) was silently dropped before reachingcompile().Bug 2 — regex-discovered
remisn't propagated downstreamcompile()has a fallback that scans CSS for:root { font-size: Npx }wheninlineRemisn't explicitly set, so users can configure rem scaling from CSS. The discovered value was only wired into the first-pass lightningcssLengthvisitor via a localeffectiveRem— never written back intooptions. The second code path —parseLength()indeclarations.ts, which readsinlineRemfrombuilder.getOptions()— kept using the default14.Why v3 is unaffected
Tailwind v3 emits rem values directly:
css .text-base { font-size: 1rem; line-height: 1.5rem; } The
1remis a proper lightningcssLengthnode, so the first-pass visitor handles it and scales correctly. The brokenparseLengthpath is never exercised.Why v4 breaks
Tailwind v4 routes utility values through CSS custom properties:
css :root { --text-base: 1rem; } .text-base { font-size: var(--text-base); } The
1reminside the custom property is a token-list, inlined verbatim byinlineVariables, then resolved viaparseLengthin the second pass — where it reads the stale default.see
react-native-css/src/compiler/declarations.ts
Lines 1359 to 1364 in b5f2a4d
Net effect on v4: every
text-*utility was sized againstrem=14regardless of what:root { font-size }declared or what the user configured. Meanwhile direct rules like.my-class { font-size: 1.5rem }scaled correctly — which is why the bug evaded isolated tests.