From 48632f09dc1c373e4bd5ef64ed279f1e82648184 Mon Sep 17 00:00:00 2001 From: huymobile Date: Sat, 3 Oct 2026 08:46:40 +0700 Subject: [PATCH] fix(native): re-resolve a :root / fallback var() when an ancestor later provides it varResolver only recorded a render guard when the variable was found in the inherited VariableContext. When it was absent and resolved from :root or the var() fallback, no guard was recorded, so a mounted component kept that value after an ancestor (e.g. VariableContextProvider) started providing the variable. Record an absence guard before the :root / fallback lookups so testGuards re-resolves the styles when the variable appears in context. Fixes #377 Co-Authored-By: Claude Opus 5.5 --- src/__tests__/native/variables.test.tsx | 54 +++++++++++++++++++++++++ src/native/styles/variables.ts | 4 ++ 2 files changed, 58 insertions(+) diff --git a/src/__tests__/native/variables.test.tsx b/src/__tests__/native/variables.test.tsx index e61340b5..55fced31 100644 --- a/src/__tests__/native/variables.test.tsx +++ b/src/__tests__/native/variables.test.tsx @@ -242,6 +242,60 @@ test("VariableContextProvider", () => { expect(component.props.style).toStrictEqual({ color: "red" }); }); +test("VariableContextProvider later provides a :root variable", () => { + registerCSS( + ` + :root { --my-var: red; } + .test { color: var(--my-var); } + `, + { inlineVariables: false }, + ); + + const child = ; + + const { rerender } = render( + {child}, + ); + + expect(screen.getByTestId(testID).props.style).toStrictEqual({ + color: "red", + }); + + rerender( + + {child} + , + ); + + expect(screen.getByTestId(testID).props.style).toStrictEqual({ + color: "blue", + }); +}); + +test("VariableContextProvider later provides a variable with a fallback", () => { + registerCSS(`.test { color: var(--my-var, red); }`); + + const child = ; + + const { rerender } = render( + {child}, + ); + + expect(screen.getByTestId(testID).props.style).toStrictEqual({ + color: "red", + }); + + rerender( + + {child} + , + ); + + expect(screen.getByTestId(testID).props.style).toStrictEqual({ + color: "blue", + }); +}); + test("variable overriding with classes", () => { registerCSS(` :root { diff --git a/src/native/styles/variables.ts b/src/native/styles/variables.ts index 0b785645..07f3fd90 100644 --- a/src/native/styles/variables.ts +++ b/src/native/styles/variables.ts @@ -83,6 +83,10 @@ export function varResolver( return variableCycles.has(name) ? resolve(fallback) : value; } + // Guard the absence too, so an ancestor that later provides the variable + // replaces the :root / fallback value. + renderGuards?.push(["v", name, undefined]); + value = resolve(get(rootVariables(name))); if (variableCycles.has(name)) return resolve(fallback); if (value !== undefined) {