Skip to content

fix(compiler): read a media query's em and rem against the font size rem inlines - #494

Open
YevheniiKotyrlo wants to merge 4 commits into
nativewind:mainfrom
YevheniiKotyrlo:fix/media-initial-font-size
Open

YevheniiKotyrlo wants to merge 4 commits into
nativewind:mainfrom
YevheniiKotyrlo:fix/media-initial-font-size

Conversation

@YevheniiKotyrlo

@YevheniiKotyrlo YevheniiKotyrlo commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A media query has no element, so its em and its rem are both the initial font size (Media Queries 4 §1.3). The compiler keeps a media bound in em as a runtime em descriptor, and with inlineRem: false a bound in rem as a runtime rem descriptor, and the media evaluator decides neither: (min-width: 10em) never matches, and with inlineRem: false no rem breakpoint does - Tailwind 4's sm: to 2xl: among them.

Solution

The compiler reads a media bound in em or rem against the rem it inlines, or React Native's default font size when inlineRem is false, so the bound is a number before the evaluator sees it. Container queries keep their em, which is the container's.

That default font size was a literal 14 in five places - the compiler's fallback, parseLength's default, the runtime's seed of __rn-css-rem, the runtime's default fontSize, and an unread rem field on the builder - and is DEFAULT_REM in src/utilities/rem.ts now; the unread field goes.

Tests

src/__tests__/native/media-query-font-relative.test.tsx renders a min-width and a max-width query at each bound - in em, against an inlined rem of 16, and in em and rem with inlineRem: false - and an em range. All six fail on #426.

Verification

On Ubuntu with the workflow's steps - build, lint, typecheck, yarn test, node --test node/__tests__/*.mjs, both library builds, the web export, the unstaged-changes checks and commitlint - everything passes.

Not run: the iOS and Android example builds.

Unchanged: an em inside a calc() in a media query is still a runtime descriptor the evaluator answers as unknown.

Device evidence, before and after: #494 (comment)

Merge order

It carries #426's commits and changes the media operand parsing #426 adds, so #426 lands first.

It also shares lines with #388 (compiler/media-query.ts, native/conditions/media-query.ts), #389 (compiler/declarations.ts, utilities/index.ts), #391 (compiler/declarations.ts, utilities/index.ts), #392 (native-internal/root.ts), #410 (native-internal/root.ts), #412 (native-internal/root.ts), #413 (compiler/declarations.ts), #415 (native/conditions/media-query.ts), #417 (compiler/declarations.ts), #420 (compiler/declarations.ts, utilities/index.ts), #425 (native/conditions/media-query.ts), #459 (compiler/compiler.types.ts, native/conditions/media-query.ts), #460 (compiler/compiler.ts), #491 (compiler/declarations.ts, utilities/index.ts), #492 (compiler/declarations.ts, utilities/index.ts), #495 (compiler/declarations.ts, utilities/index.ts); whichever lands second rebases.

Both evaluators answered a range pair such as (400px < width < 800px)
with false, though the compiler emits it faithfully. Media and container
queries now share compareMediaFeature, so each operator has one meaning,
and testMediaFeatureInterval answers a pair as its two comparisons.
…alse

MQ5 §3.1 evaluates a media condition in three-valued logic: a term the
runtime cannot decide is unknown, its negation is unknown, and a rule
applies only when the whole condition is true. Both runtimes answered such
a term false, so its negation matched: not (fictional-feature: 3) and
not (pointer: fine) applied everywhere.

A media feature value the compiler cannot resolve compiles to null, and
both runtimes evaluate in Kleene logic, answering unknown for a null
operand, a feature they do not measure, an operand they cannot order and a
container style() query. A container condition holding a size feature the
runtime does not know, or a value the compiler cannot resolve, selects no
container (CSS Conditional 5 §5.4), so the compiler emits no rule for it.

A feature in a boolean context is evaluated (MQ5 §2.4.2). The runtime
answers the features whose value is the same on every React Native screen,
(hover: none) no longer matches, a device with no color scheme preference
matches light, and an <mq-boolean> value compiles to its integer.
…rem inlines

A media query has no element, so its em and rem are both the initial
font size (Media Queries 4 §1.3). The compiler kept a media bound in em
as a runtime em descriptor, and with inlineRem: false one in rem as a
runtime rem descriptor; the media evaluator decides neither, so
(min-width: 10em) never matched, and with inlineRem: false no rem
breakpoint did. Both now compile against the rem the compiler inlines,
or React Native's default font size when it inlines none.

The default font size was a literal 14 in four places; it is
DEFAULT_REM in utilities, and the builder's unread rem field goes.
@YevheniiKotyrlo
YevheniiKotyrlo marked this pull request as ready for review October 16, 2026 13:34
@YevheniiKotyrlo

Copy link
Copy Markdown
Contributor Author

Device evidence — before / after

UNFIXED — Only the control paints. A bound in em is left for the runtime, which cannot decide it, so neither em query ever matches.

FIXED — All three bars paint green. A media query's em is the initial font size, so 10em is 160 pixels and 1000em is wider than any screen.

before — 3.1.0-rc.0 after — with this PR

Android 16 emulator (API 36, GLES on the host GPU), 1140×2400 @ 480dpi, both frames from one run. My test app inlines rem at 16px, so the em bounds read 160px and 16000px. before is the published 3.1.0-rc.0 plus what my test app needs from my other patches to run: the native and native-internal subpath exports, PR 459, cascade-layer ranking, and a shim that compiles a class-qualified :root inert. after adds the rest of my open PRs, this one included. One of them shows on every screen: PR 417 narrows a var()-valued font-family to one family, so the after sets its text in the theme's face where the before falls back to the platform's.

@YevheniiKotyrlo
YevheniiKotyrlo marked this pull request as ready for review October 16, 2026 15:00

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant