diff --git a/apps/examples/src/components/App.js b/apps/examples/src/components/App.js index 311de906..ea7af984 100644 --- a/apps/examples/src/components/App.js +++ b/apps/examples/src/components/App.js @@ -654,14 +654,14 @@ const animateSequence = css.keyframes({ }); const themedTokens = css.createTheme(tokens, { - squareColor: 'purple', + '--square-color': 'purple', textColor: 'purple', inputColor: 'purple', inputPlaceholderColor: 'mediumpurple' }); const themedTokensAlt = css.createTheme(tokens, { - squareColor: 'darkorange', + '--square-color': 'darkorange', textColor: 'darkorange', inputColor: 'orangered', inputPlaceholderColor: 'orange' @@ -684,7 +684,7 @@ const styles = css.create({ square: { height: 100, width: 100, - backgroundColor: tokens.squareColor + backgroundColor: 'var(--square-color)' // check global vars work too }, pseudoStates: { height: 50, diff --git a/apps/examples/src/components/tokens.stylex.js b/apps/examples/src/components/tokens.stylex.js index 3e5d7820..8856cae2 100644 --- a/apps/examples/src/components/tokens.stylex.js +++ b/apps/examples/src/components/tokens.stylex.js @@ -13,13 +13,13 @@ import { css } from 'react-strict-dom'; export const tokens: StyleVars< $ReadOnly<{ - squareColor: string, + '--square-color': string, textColor: string, inputColor: string, inputPlaceholderColor: string }> > = css.defineVars({ - squareColor: 'red', + '--square-color': 'red', // '--' prefix makes this is a global var textColor: { default: 'darkred', '@media (prefers-color-scheme: dark)': 'lightred' diff --git a/packages/react-strict-dom/src/native/stylex/customProperties.js b/packages/react-strict-dom/src/native/stylex/customProperties.js index c59b5a90..65a60d87 100644 --- a/packages/react-strict-dom/src/native/stylex/customProperties.js +++ b/packages/react-strict-dom/src/native/stylex/customProperties.js @@ -13,27 +13,16 @@ import { CSSUnparsedValue } from './typed-om/CSSUnparsedValue'; import { CSSVariableReferenceValue } from './typed-om/CSSVariableReferenceValue'; import { warnMsg } from '../../shared/logUtils'; -const memoizedValues = new Map(); - -function camelize(s: string): string { - const memoizedValue = memoizedValues.get(s); - if (memoizedValue != null) { - return memoizedValue; - } - const result = s.replace(/-./g, (x) => x.toUpperCase()[1]); - memoizedValues.set(s, result); - return result; -} - function normalizeVariableName(name: string): string { if (__DEV__) { if (!name.startsWith('--')) { throw new Error("Invalid variable name, must begin with '--'"); } } - // TODO: remove camelize - // https://github.com/facebook/react-strict-dom/pull/73 - return camelize(name.substring(2)); + // Scoped vars created by defineVars all start with __var__. + // But global vars manually created with '--' prefixed keys don't. + const varName = name.startsWith('--__var__') ? name.substring(2) : name; + return varName; } export function stringContainsVariables(input: string): boolean { diff --git a/packages/react-strict-dom/src/native/stylex/index.js b/packages/react-strict-dom/src/native/stylex/index.js index 4d2e5f38..7734871a 100644 --- a/packages/react-strict-dom/src/native/stylex/index.js +++ b/packages/react-strict-dom/src/native/stylex/index.js @@ -448,8 +448,12 @@ export const createTheme = ( const result: MutableCustomProperties = { $$theme: 'theme' }; for (const key in baseTokens) { const varName: string = baseTokens[key]; - const normalizedKey = varName.replace(RE_CAPTURE_VAR_NAME, '$1'); - result[normalizedKey] = overrides[key]; + const match = varName.match(RE_CAPTURE_VAR_NAME); + if (match) { + const x = match[1]; + const normalizedKey = x.startsWith('__var__') ? x : `--${x}`; + result[normalizedKey] = overrides[key]; + } } return result; }; @@ -466,8 +470,11 @@ export const defineVars = (tokens: CustomProperties): Tokens => { const result: Tokens = {}; for (const key in tokens) { const value = tokens[key]; - const customPropName = `${key}__id__${defineVarsCount++}`; - result[key] = `var(--${customPropName})`; + const isGlobalVar = key.startsWith('--'); + const customPropName = isGlobalVar + ? key + : `__var__${defineVarsCount++}_${key}`; + result[key] = isGlobalVar ? `var(${key})` : `var(--${customPropName})`; // NOTE: it's generally not a good idea to mutate the default context, // but defineVars is always called before any component body is evaluated, // and so it's safe to do so here. diff --git a/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native b/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native index 610def8d..46541ec8 100644 --- a/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native +++ b/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native @@ -2,19 +2,20 @@ exports[`properties: custom property css.createTheme: css.__customProperties 1`] = ` { - "rootColor__id__1": "red", - "rootColor__id__3": "red", - "themeAwareColor__id__2": { + "--pass-through": "purple", + "__var__1_rootColor": "red", + "__var__2_themeAwareColor": { "@media (prefers-color-scheme: dark)": "green", "default": "blue", }, + "__var__3_rootColor": "red", } `; exports[`properties: custom property css.createTheme: theme 1`] = ` { "$$theme": "theme", - "rootColor__id__3": "green", + "__var__3_rootColor": "green", } `; @@ -27,8 +28,9 @@ exports[`properties: custom property css.defineConsts: constants 1`] = ` exports[`properties: custom property css.defineVars: css.__customProperties 1`] = ` { - "rootColor__id__1": "red", - "themeAwareColor__id__2": { + "--pass-through": "purple", + "__var__1_rootColor": "red", + "__var__2_themeAwareColor": { "@media (prefers-color-scheme: dark)": "green", "default": "blue", }, @@ -37,8 +39,9 @@ exports[`properties: custom property css.defineVars: css.__customProperties 1`] exports[`properties: custom property css.defineVars: tokens 1`] = ` { - "rootColor": "var(--rootColor__id__1)", - "themeAwareColor": "var(--themeAwareColor__id__2)", + "--pass-through": "var(--pass-through)", + "rootColor": "var(--__var__1_rootColor)", + "themeAwareColor": "var(--__var__2_themeAwareColor)", } `; diff --git a/packages/react-strict-dom/tests/css-test.native.js b/packages/react-strict-dom/tests/css-test.native.js index 21e6e654..42721732 100644 --- a/packages/react-strict-dom/tests/css-test.native.js +++ b/packages/react-strict-dom/tests/css-test.native.js @@ -1157,7 +1157,7 @@ describe('properties: custom property', () => { test('legacy strings', () => { const options = { customProperties: { - rootColor: 'red' + '--rootColor': 'red' } }; expect( @@ -1183,7 +1183,8 @@ describe('properties: custom property', () => { themeAwareColor: { default: 'blue', '@media (prefers-color-scheme: dark)': 'green' - } + }, + '--pass-through': 'purple' }); expect(tokens).toMatchSnapshot('tokens'); expect(css.__customProperties).toMatchSnapshot('css.__customProperties'); @@ -1193,6 +1194,9 @@ describe('properties: custom property', () => { expect( resolveCustomPropertyValue(options, ['color', tokens.themeAwareColor]) ).toEqual('blue'); + expect( + resolveCustomPropertyValue(options, ['color', 'var(--pass-through)']) + ).toEqual('purple'); // dark theme expect( resolveCustomPropertyValue({ colorScheme: 'dark' }, [ @@ -1255,7 +1259,9 @@ describe('properties: custom property', () => { test('parses a basic var correctly', () => { const options = { - customProperties: { test: 'red' } + customProperties: { + '--test': 'red' + } }; expect( resolveCustomPropertyValue(options, ['color', 'var(--test)']) @@ -1264,7 +1270,9 @@ describe('properties: custom property', () => { test('parses a var with a default value', () => { const options = { - customProperties: { test: 'red' } + customProperties: { + '--test': 'red' + } }; expect( resolveCustomPropertyValue(options, ['color', 'var(--test, blue)']) @@ -1274,32 +1282,11 @@ describe('properties: custom property', () => { ).toEqual('blue'); }); - // TODO: this transform should not be supported. Custom properties are case sensitive. - test('parses kebab case var to camel case', () => { - const options = { - customProperties: { testVar: 'red' } - }; - expect( - resolveCustomPropertyValue(options, ['color', 'var(--test-var)']) - ).toEqual('red'); - }); - - // TODO: this transform should not be supported. Custom properties are case sensitive. - test('parses kebab case var with a default value', () => { - const options = { - customProperties: { testVar: 'red' } - }; - expect( - resolveCustomPropertyValue(options, ['color', 'var(--test-var, blue)']) - ).toEqual('red'); - expect( - resolveCustomPropertyValue(options, ['color', 'var(--not-found, blue)']) - ).toEqual('blue'); - }); - test('parses a var with a default value containing spaces', () => { const options = { - customProperties: { color: 'rgb(0,0,0)' } + customProperties: { + '--color': 'rgb(0,0,0)' + } }; expect( resolveCustomPropertyValue(options, [ @@ -1317,7 +1304,9 @@ describe('properties: custom property', () => { test('parses a var and falls back to default value containing a var', () => { const options = { - customProperties: { color: 'red' } + customProperties: { + '--color': 'red' + } }; expect( resolveCustomPropertyValue(options, [ @@ -1330,7 +1319,7 @@ describe('properties: custom property', () => { test('parses a var and falls back to a default value containing spaces and embedded var', () => { const options = { customProperties: { - test: '255' + '--test': '255' } }; expect( @@ -1343,7 +1332,9 @@ describe('properties: custom property', () => { test('basic var value lookup works', () => { const options = { - customProperties: { number: 10 } + customProperties: { + '--number': 10 + } }; expect( resolveCustomPropertyValue(options, ['borderWidth', 'var(--number)']) @@ -1352,7 +1343,9 @@ describe('properties: custom property', () => { test('var value lookup with pixel prop conversion', () => { const options = { - customProperties: { pxNumber: '10px' } + customProperties: { + '--pxNumber': '10px' + } }; expect( resolveCustomPropertyValue(options, ['borderWidth', 'var(--pxNumber)']) @@ -1361,7 +1354,9 @@ describe('properties: custom property', () => { test('var value lookup with em prop conversion', () => { const options = { - customProperties: { emNumber: '10em' } + customProperties: { + '--emNumber': '10em' + } }; expect( resolveCustomPropertyValue(options, ['borderWidth', 'var(--emNumber)']) @@ -1371,9 +1366,9 @@ describe('properties: custom property', () => { test('var value lookup with reference to another var', () => { const options = { customProperties: { - number: 10, - refToNumber: 'var(--number)', - refToRefToNumber: 'var(--refToNumber)' + '--number': 10, + '--refToNumber': 'var(--number)', + '--refToRefToNumber': 'var(--refToNumber)' } }; expect( @@ -1389,7 +1384,9 @@ describe('properties: custom property', () => { test('var with hover styles', () => { const options = { - customProperties: { test: '#333' }, + customProperties: { + '--test': '#333' + }, hover: true }; const { underTest } = css.create({ @@ -1413,7 +1410,9 @@ describe('properties: custom property', () => { test('rgb(a) function with args applied through a single var', () => { const options = { - customProperties: { example: '24, 48, 96' } + customProperties: { + '--example': '24, 48, 96' + } }; expect( resolveCustomPropertyValue(options, ['color', 'rgb(var(--example))']) @@ -1429,11 +1428,11 @@ describe('properties: custom property', () => { test('rgba function with args applied through multiple (& nested) vars', () => { const options = { customProperties: { - red: 255, - green: 96, - blue: 16, - rgb: 'var(--red), var(--green), var(--blue)', - alpha: 0.42 + '--red': 255, + '--green': 96, + '--blue': 16, + '--rgb': 'var(--red), var(--green), var(--blue)', + '--alpha': 0.42 } }; expect( @@ -1447,11 +1446,11 @@ describe('properties: custom property', () => { test('textShadow with nested/multiple vars', () => { const options = { customProperties: { - height: '20px', - width: '10px', - size: 'var(--width) var(--height)', - radius: '30px', - red: 'red' + '--height': '20px', + '--width': '10px', + '--size': 'var(--width) var(--height)', + '--radius': '30px', + '--red': 'red' } }; const styles = css.create({ @@ -1474,10 +1473,10 @@ describe('properties: custom property', () => { test('transform with nested/multiple vars', () => { const options = { customProperties: { - distance: 20, - angle: '45deg', - translateX: 'translateX(var(--distance))', - rotateX: 'rotateX(var(--angle))' + '--distance': 20, + '--angle': '45deg', + '--translateX': 'translateX(var(--distance))', + '--rotateX': 'rotateX(var(--angle))' } }; const styles = css.create({ @@ -1495,7 +1494,7 @@ describe('properties: custom property', () => { test('css variable declaration inside a media query', () => { const options = { customProperties: { - example: '42px' + '--example': '42px' }, viewportWidth: 450 }; diff --git a/packages/react-strict-dom/tests/html-test.native.js b/packages/react-strict-dom/tests/html-test.native.js index 27ff635e..ac9b77ef 100644 --- a/packages/react-strict-dom/tests/html-test.native.js +++ b/packages/react-strict-dom/tests/html-test.native.js @@ -98,7 +98,7 @@ describe('', () => { test('legacy: ThemeProvider', () => { const { ThemeProvider } = contexts; const customProperties = { - rootColor: 'red' + '--rootColor': 'red' }; const styles = css.create({ root: {