diff --git a/src/__tests__/native/vars.test.tsx b/src/__tests__/native/vars.test.tsx index d87eb7c4..66586fbd 100644 --- a/src/__tests__/native/vars.test.tsx +++ b/src/__tests__/native/vars.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from "@testing-library/react-native"; import { View } from "react-native-css/components/View"; import { registerCSS, testID } from "react-native-css/jest"; -import { vars } from "react-native-css/runtime"; +import { VariableContextProvider, vars } from "react-native-css/runtime"; test("vars", () => { registerCSS( @@ -36,3 +36,49 @@ test("vars", () => { color: "blue", }); }); + +test("an element's own vars() outranks an inherited value", () => { + // `--my-var` is defined twice so `inline-variables.ts` cannot fold it into + // the consuming declaration; a single definition performs no runtime var() + // read and would pass without exercising precedence at all. + registerCSS( + `.decoy { --my-var: seed; } + .other-decoy { --my-var: seed2; } + .my-class { color: var(--my-var); }`, + ); + + render( + + + , + ); + + // css-cascade-4 ยง7.2: inheritance is a defaulting step, reached only when the + // cascade yields no declared value for the element. The element declares + // `--my-var`, so the ancestor's `red` never applies to it. + expect(screen.getByTestId(testID).props.style).toStrictEqual({ + color: "blue", + }); +}); + +test("an inherited value still applies when the element declares nothing", () => { + registerCSS( + `.decoy { --my-var: seed; } + .other-decoy { --my-var: seed2; } + .my-class { color: var(--my-var); }`, + ); + + render( + + + , + ); + + expect(screen.getByTestId(testID).props.style).toStrictEqual({ + color: "red", + }); +}); diff --git a/src/native/styles/variables.ts b/src/native/styles/variables.ts index af3d6ee2..d6361b97 100644 --- a/src/native/styles/variables.ts +++ b/src/native/styles/variables.ts @@ -46,11 +46,6 @@ export function varResolver( return; } - if (name in variables) { - renderGuards?.push(["v", name, variables[name]]); - return resolve(variables[name]); - } - variableHistory.add(name); let value = resolve(inlineVariables?.[name] as StyleDescriptor); @@ -61,13 +56,12 @@ export function varResolver( return value; } - value = resolve(variables[name]); - if (value !== undefined) { - renderGuards?.push(["v", name, value]); - options.inlineVariables ??= { [VAR_SYMBOL]: "inline" }; - options.inlineVariables[name] = value; - - return value; + if (name in variables) { + // The RAW inherited descriptor, not the resolved value: `testGuards` + // compares this against the next render's context, and a resolved value + // would never match. + renderGuards?.push(["v", name, variables[name]]); + return resolve(variables[name]); } value = resolve(get(universalVariables(name)));