Repository navigation
fix(compiler): list a stylesheet's variables in the same order on every read - #476
Open
YevheniiKotyrlo wants to merge 1 commit into
Open
YevheniiKotyrlo wants to merge 1 commit into
YevheniiKotyrlo wants to merge 1 commit into
Conversation
Contributor
Author
A stylesheet read twice —
|
main (2421ea77) |
this PR (54a0b306) merged into main |
|
|---|---|---|
first read's vr |
[["tone",[["blue",[["=","prefers-color-scheme","dark"]]],["red"]]]] |
the same: dark value first |
second read's vr |
[["tone",[["red"],["blue",[["=","prefers-color-scheme","dark"]]]]]] |
the same as the first read |
first read's vr, looked at again after the second read |
changed to the second read's order | the same as before the second read |
color rendered in dark mode, without inlineVariables and with inlineVariables: false |
red |
blue |
Chromium 156, Firefox 157 and WebKit 27.2 render this stylesheet blue in dark mode and red in light mode.
const compiled = compile(
`:root { --tone: red; }
@media (prefers-color-scheme: dark) { :root { --tone: blue; } }`,
{ inlineVariables: false },
);
const first = compiled.stylesheet();
JSON.stringify(first.vr);
JSON.stringify(compiled.stylesheet().vr);
JSON.stringify(first.vr);registerCSS(
`:root { --tone: red; } @media (prefers-color-scheme: dark) { :root { --tone: blue; } } .my-class { color: var(--tone); }`,
{ debug: true }, // and again with { debug: true, inlineVariables: false }
);
act(() => {
colorScheme.set("dark");
});
render(<View testID={testID} className="my-class" />);
screen.getByTestId(testID).props.style;I ran each snippet as a jest case in src/__tests__/native/ on Windows with Node 26. They are not part of the PR; its own cases are in compiler.test.tsx and selectors.test.tsx.
…ry read getNativeStyleSheet reversed each root and universal variable list in place to put the most specific value first. Reading the stylesheet again reversed the lists back, so a second read listed the unconditioned value first, and the first sheet's lists changed under it. The lists are now reversed into a copy.
YevheniiKotyrlo
force-pushed
the
fix/stylesheet-variable-order
branch
from
October 28, 2026 23:38
622a46e to
54a0b30
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
getNativeStyleSheetreverses each root and universal variable list in place to put the most specific value first. Reading the stylesheet a second time reverses the lists back. Measured onmain:The second read lists the unconditioned value first, so the dark value never wins, and the first sheet's lists change under it because both reads hand out the builder's own arrays. The same holds for
vu.registerCSSreads the stylesheet twice whendebugis on, logging one read and injecting the next, so onmaina debug run renders a dark-mode variable's light value.Solution
Both lists are reversed into a copy with
toReversed(), so every read of a stylesheet is the same and a sheet already handed out stays as it was.Tests
src/__tests__/compiler/compiler.test.tsx: reading the stylesheet twice gives the samevrandvu, most specific value first, and leaves the first read unchanged.src/__tests__/native/selectors.test.tsx: after a second read of the stylesheet is injected, a:rootand a*variable render their dark value in dark mode and their light value otherwise, withoutinlineVariablesand withinlineVariables: false. Chromium 156, Firefox 157 and WebKit 27.2 render the same stylesheet the same way: the dark rule comes later, so it wins whileprefers-color-scheme: darkholds (css-cascade-5 §6.1, mediaqueries-5 §12.5).vralso fails:root CSS variables with media queriesandroot variables track system dark mode.Verification
Every
ci.ymljob on this branch merged intomain(2421ea77) passes:lint,test,build-library,build-webandcommitlinton Ubuntu 26.04 with Node 26.12.1, where.nvmrcpins 22.18.0, andbuild-ios-devon macOS with Xcode 27.1, where the workflow pins 26.6.No existing issue tracks this. I searched for
getNativeStyleSheet,rootVariables reverseandvariables order media.Both reads and a debug render, on
mainand here: #476 (comment)