From 8607a2e856aed2e68cbcc5fa98d5564a918bfe9d Mon Sep 17 00:00:00 2001 From: Eike Foken Date: Fri, 5 Jun 2026 21:41:40 +0200 Subject: [PATCH] Implement light-dark() support for css.defineVars --- .../src/native/css/customProperties.js | 32 ++++++++++++++++++- .../css-themes-test.native.js.snap | 3 +- .../tests/css/css-themes-test.native.js | 14 +++++++- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/react-strict-dom/src/native/css/customProperties.js b/packages/react-strict-dom/src/native/css/customProperties.js index 7389215f..e21787e0 100644 --- a/packages/react-strict-dom/src/native/css/customProperties.js +++ b/packages/react-strict-dom/src/native/css/customProperties.js @@ -40,6 +40,28 @@ export function stringContainsVariables(input: string): boolean { return input.includes('var('); } +function stringContainsLightDark(input: string): boolean { + return input.includes('light-dark('); +} + +const RE_LIGHT_DARK = /^light-dark\(\s*(.+?)\s*,\s*(.+?)\s*\)$/i; + +function resolveLightDarkValue( + variableValue: string, + colorScheme: 'light' | 'dark' +) { + const match = RE_LIGHT_DARK.exec(variableValue); + + if (match == null) { + warnMsg(`Invalid light-dark syntax: "${variableValue}"`); + return null; + } + + const [, lightStr, darkStr] = match; + + return colorScheme === 'dark' ? darkStr.trim() : lightStr.trim(); +} + function resolveVariableReferenceValue( propName: string, variable: CSSVariableReferenceValue, @@ -66,7 +88,15 @@ function resolveVariableReferenceValue( } if (variableValue != null) { - if (typeof variableValue === 'object' && variableValue.default != null) { + if ( + typeof variableValue === 'string' && + stringContainsLightDark(variableValue) + ) { + variableValue = resolveLightDarkValue(variableValue, colorScheme); + } else if ( + typeof variableValue === 'object' && + variableValue.default != null + ) { let defaultValue = variableValue.default; if (colorScheme === 'dark') { defaultValue = variableValue['@media (prefers-color-scheme: dark)']; diff --git a/packages/react-strict-dom/tests/css/__snapshots__/css-themes-test.native.js.snap b/packages/react-strict-dom/tests/css/__snapshots__/css-themes-test.native.js.snap index 926b3997..e01bebb6 100644 --- a/packages/react-strict-dom/tests/css/__snapshots__/css-themes-test.native.js.snap +++ b/packages/react-strict-dom/tests/css/__snapshots__/css-themes-test.native.js.snap @@ -3,7 +3,7 @@ exports[`css.* themes css.createTheme: theme 1`] = ` { "$$theme": "theme", - "rootColor__id__3": "green", + "rootColor__id__4": "green", } `; @@ -16,6 +16,7 @@ exports[`css.* themes css.defineConsts: constants 1`] = ` exports[`css.* themes css.defineVars: tokens 1`] = ` { + "lightDarkColor": "var(--lightDarkColor__id__3)", "rootColor": "var(--rootColor__id__1)", "themeAwareColor": "var(--themeAwareColor__id__2)", } diff --git a/packages/react-strict-dom/tests/css/css-themes-test.native.js b/packages/react-strict-dom/tests/css/css-themes-test.native.js index 9186d0d7..a30763ea 100644 --- a/packages/react-strict-dom/tests/css/css-themes-test.native.js +++ b/packages/react-strict-dom/tests/css/css-themes-test.native.js @@ -43,7 +43,8 @@ describe('css.* themes', () => { themeAwareColor: { default: 'blue', '@media (prefers-color-scheme: dark)': 'green' - } + }, + lightDarkColor: 'light-dark(blue, green)' }); expect(tokens).toMatchSnapshot('tokens'); @@ -54,6 +55,9 @@ describe('css.* themes', () => { }, themeAwareColor: { color: tokens.themeAwareColor + }, + lightDarkColor: { + color: tokens.lightDarkColor } }); @@ -66,6 +70,10 @@ describe('css.* themes', () => { root = create(); }); expect(root.toJSON().props.style.color).toBe('blue'); + act(() => { + root = create(); + }); + expect(root.toJSON().props.style.color).toBe('blue'); // dark theme ReactNative.useColorScheme.mockReturnValue('dark'); @@ -73,6 +81,10 @@ describe('css.* themes', () => { root = create(); }); expect(root.toJSON().props.style.color).toBe('green'); + act(() => { + root = create(); + }); + expect(root.toJSON().props.style.color).toBe('green'); }); test('css.createTheme', () => {