Repository navigation
fix(compiler): read a media query's em and rem against the font size rem inlines - #494
Draft
YevheniiKotyrlo wants to merge 23 commits into
Draft
YevheniiKotyrlo wants to merge 23 commits into
YevheniiKotyrlo wants to merge 23 commits into
Conversation
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.
YevheniiKotyrlo
marked this pull request as ready for review
October 16, 2026 13:34
YevheniiKotyrlo
marked this pull request as draft
October 16, 2026 13:47
This was referenced Oct 16, 2026
Draft
Contributor
Author
YevheniiKotyrlo
marked this pull request as ready for review
October 16, 2026 15:00
…ot one of its own lightningcss passes any identifier through as a feature's value, so `(orientation: bogus)` compiled to a comparison that is false, and its negation matched everywhere. A keyword the feature does not take now compiles to an unknown term in a media query, and to no rule in a container query, as an unresolvable value already does, so neither the term nor its negation matches, as in Chromium, Firefox and WebKit.
…paring it A keyword is ASCII case-insensitive (css-values-4 §4.1). The keyword check folded it while the compiled comparison kept it as written, so `(orientation: PORTRAIT)` never matched and `not (orientation: PORTRAIT)` matched in portrait. The value is folded where the compiler reads it, which both the check and the comparison then use.
…ver emits lightningcss compiles every `min-` and `max-` media feature to a range on `width` or `height`, so no compiled condition names them.
Its new lines hid a duplicated union and an unused import behind the file-wide `eslint-disable`, and the compiler's operator default wrote a `never` into its error message.
…h, and answer hover by input A container term the compiler cannot encode, such as (width > 10in), is unknown rather than dropping the whole query with its `or` siblings, and `=` with an operand the runtime cannot order is unknown, as the other operators are. A condition no device can make true, such as a style() query or an unknown keyword alone or negated, compiles to no rule. An unknown media feature named like an Object.prototype member compiles again. hover and any-hover answer none on iOS and Android until a pointer hovers an element, and hover from then on and on other platforms (MQ5 §7.2, §7.3), so Tailwind's hover: keeps working wherever a pointer hovers. A held color scheme is compared as held, so a null one is unknown. The two files that kept a file-wide lint disable lint clean without it.
…y, as the components read it The jest helper resolves to the package's source in a consumer's suite while the components it renders resolve to the build, so a reset through a relative import missed the signal they read.
…d test an unresolved bound
…nput, and share one walk
…§7 and phones' browsers do
…ebkit-device-pixel-ratio A media query's reader is a module constant that takes the getter, a container's takes one context object, and the walk folds and and or itself, so evaluating a term allocates nothing. -webkit-device-pixel-ratio reads PixelRatio.get() as resolution does. The nested conditions are readonly, the container tests assert the colour absent before layout, and the condition test's afterEach, which the Jest preset's reset made dead, goes.
css-values-4 §7.4 excludes a negative <resolution>, and Chromium, Firefox and WebKit leave a media feature holding one unknown, as WPT's test_media_queries.html expects of `resolution: -3dpi`. It compiled to a negative dppx instead, so `(min-resolution: -1dpi)` matched every screen. A value outside a feature's syntax (env(), a negative resolution, or a keyword the feature does not take) is now decided in one place.
YevheniiKotyrlo
marked this pull request as draft
October 22, 2026 08:15
…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.
…lined or not A :root font size sets rem whether or not rem is inlined, but a media query read it only when rem was inlined, and React Native's default otherwise, so turning inlining off moved every em and rem breakpoint of a stylesheet that declares one. The compiler now resolves the root font size once, and the rem inliner, parseLength's rem and the media reader all read it from the builder.
YevheniiKotyrlo
force-pushed
the
fix/media-initial-font-size
branch
from
October 22, 2026 09:56
2dd7e52 to
567c206
Compare
This branch has not been deployed
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.


Problem
A media query has no element, so its
emand itsremare both the initial font size (Media Queries 4 §1.3). The compiler keeps a media bound inemas a runtimeemdescriptor, and withinlineRem: falsea bound inremas a runtimeremdescriptor, and the media evaluator decides neither:(min-width: 10em)never matches, and withinlineRem: falsenorembreakpoint does - Tailwind 4'ssm:to2xl:among them.Solution
The compiler reads a media bound in
emorremagainst the root font size the stylesheet configures,inlineRemor else a:rootfont size, whether or not rem is inlined, so the bound is a number before the evaluator sees it.mainalready inlines a media query'sremagainst that font size; the compiler now resolves it once, and the rem inliner,parseLength'sremand the media bound read it from the builder. Container queries keep theirem, which is the container's (CSS Conditional 5 §6.1).That default font size was a literal
14in five places - the compiler's fallback,parseLength's default, the runtime's seed of__rn-css-rem, the runtime's defaultfontSize, and an unreadremfield on the builder. It isDEFAULT_REMinsrc/utilities/rem.tsnow,parseLengthreads the builder's root font size, and the unread field goes.Tests
src/__tests__/native/media-query-font-relative.test.tsxrenders amin-widthand amax-widthquery at each bound: inemagainst the default rem and an inlined rem of 16, inemandremwithinlineRem: false, and under a 10px:rootfont size, inlined, not inlined and withinlineRem: 16; anemrange; and a negatedemterm. All 11 fail on #426. #426'scondition-semantics.test.tsxexpectsnot (width: 10em)to stay unknown; that case moves here, decided.Verification
Every
ci.ymljob passes on this head merged intomainwith #477's harness fix: lint, typecheck, the tests (1629 passed), the Node tooling tests, both builds, the web export, nothing unstaged, commitlint, and the iOS dev build on macOS 27.0.1 with Xcode 27.0, where CI pins 26.6.Device evidence, before and after: #494 (comment)
Known limits
:rootfont size moves its mediaemandremwith it, asmainalready moves its inlined mediarem; a browser keeps both at its initial font size (MQ4 §1.3).eminside acalc()in a media query is still a runtime descriptor the evaluator answers as unknown.Merge order
It carries #426's commits and changes the media operand parsing #426 adds, so #426 lands first.