From 1d15884f5817c04a8599556600501d642e404102 Mon Sep 17 00:00:00 2001 From: Hubert Bieszczad Date: Tue, 25 Nov 2025 11:11:04 +0100 Subject: [PATCH 1/2] feat: allow for multiple props in useCSSVariable --- .../src/hooks/useCSSVariable.native.ts | 36 -------- packages/uniwind/src/hooks/useCSSVariable.ts | 56 ------------- .../useCSSVariable/getVariableValue.native.ts | 3 + .../hooks/useCSSVariable/getVariableValue.ts | 19 +++++ .../uniwind/src/hooks/useCSSVariable/index.ts | 1 + .../hooks/useCSSVariable/useCSSVariable.ts | 84 +++++++++++++++++++ 6 files changed, 107 insertions(+), 92 deletions(-) delete mode 100644 packages/uniwind/src/hooks/useCSSVariable.native.ts delete mode 100644 packages/uniwind/src/hooks/useCSSVariable.ts create mode 100644 packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts create mode 100644 packages/uniwind/src/hooks/useCSSVariable/getVariableValue.ts create mode 100644 packages/uniwind/src/hooks/useCSSVariable/index.ts create mode 100644 packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts diff --git a/packages/uniwind/src/hooks/useCSSVariable.native.ts b/packages/uniwind/src/hooks/useCSSVariable.native.ts deleted file mode 100644 index d5643ec3..00000000 --- a/packages/uniwind/src/hooks/useCSSVariable.native.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useEffect, useState } from 'react' -import { UniwindListener } from '../core/listener' -import { UniwindStore } from '../core/native' -import { StyleDependency } from '../types' - -const getVariableValue = (name: string) => { - return UniwindStore.vars[name] -} - -let warned = false - -export const useCSSVariable = (name: string) => { - const [value, setValue] = useState(getVariableValue(name)) - - useEffect(() => { - const updateValue = () => setValue(getVariableValue(name)) - const dispose = UniwindListener.subscribe( - updateValue, - [StyleDependency.Theme], - ) - - updateValue() - - return dispose - }, [name]) - - if (value === undefined && __DEV__ && !warned) { - warned = true - // eslint-disable-next-line no-console - console.warn( - `We couldn't find your variable ${name}. Make sure it's used at least once in your className, or define it in a static theme as described in the docs: https://docs.uniwind.dev/api/use-css-variable`, - ) - } - - return value -} diff --git a/packages/uniwind/src/hooks/useCSSVariable.ts b/packages/uniwind/src/hooks/useCSSVariable.ts deleted file mode 100644 index 04599063..00000000 --- a/packages/uniwind/src/hooks/useCSSVariable.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { useEffect, useState } from 'react' -import { UniwindListener } from '../core/listener' -import { CSSListener, parseCSSValue } from '../core/web' -import { StyleDependency } from '../types' - -const documentStyles = typeof document !== 'undefined' - ? window.getComputedStyle(document.documentElement) - : null - -const getVariableValue = (name: string) => { - if (!documentStyles) { - return undefined - } - - const value = documentStyles.getPropertyValue(name).trim() - - if (value === '') { - return undefined - } - - return parseCSSValue(value) -} - -let warned = false - -/** - * A hook that returns the value of a CSS variable. - * @param name Name of the CSS variable. - * @returns Value of the CSS variable. On web it is always a string (1rem, #ff0000, etc.), but on native it can be a string or a number (16px, #ff0000) - */ -export const useCSSVariable = (name: string): string | number | undefined => { - const [value, setValue] = useState(getVariableValue(name)) - - useEffect(() => { - const updateValue = () => setValue(getVariableValue(name)) - const themeListenerDispose = UniwindListener.subscribe(updateValue, [StyleDependency.Theme]) - const classListenerDispose = CSSListener.subscribeToClassName(':root', updateValue) - - updateValue() - - return () => { - themeListenerDispose() - classListenerDispose() - } - }, [name]) - - if (value === undefined && __DEV__ && !warned) { - warned = true - // eslint-disable-next-line no-console - console.warn( - `We couldn't find your variable ${name}. Make sure it's used at least once in your className, or define it in a static theme as described in the docs: https://docs.uniwind.dev/api/use-css-variable`, - ) - } - - return value -} diff --git a/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts b/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts new file mode 100644 index 00000000..0165880d --- /dev/null +++ b/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts @@ -0,0 +1,3 @@ +import { UniwindStore } from '../../core/native' + +export const getVariableValue = (name: string) => UniwindStore.vars[name] diff --git a/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.ts b/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.ts new file mode 100644 index 00000000..a68e24bb --- /dev/null +++ b/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.ts @@ -0,0 +1,19 @@ +import { parseCSSValue } from '../../core/web' + +const documentStyles = typeof document !== 'undefined' + ? window.getComputedStyle(document.documentElement) + : null + +export const getVariableValue = (name: string) => { + if (!documentStyles) { + return undefined + } + + const value = documentStyles.getPropertyValue(name).trim() + + if (value === '') { + return undefined + } + + return parseCSSValue(value) +} diff --git a/packages/uniwind/src/hooks/useCSSVariable/index.ts b/packages/uniwind/src/hooks/useCSSVariable/index.ts new file mode 100644 index 00000000..984b7a3d --- /dev/null +++ b/packages/uniwind/src/hooks/useCSSVariable/index.ts @@ -0,0 +1 @@ +export * from './useCSSVariable' diff --git a/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts b/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts new file mode 100644 index 00000000..7d5241cb --- /dev/null +++ b/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts @@ -0,0 +1,84 @@ +import { useEffect, useRef, useState } from 'react' +import { UniwindListener } from '../../core/listener' +import { StyleDependency } from '../../types' +import { getVariableValue } from './getVariableValue' + +const getValue = (name: string | Array) => + Array.isArray(name) + ? name.map(getVariableValue) + : getVariableValue(name) + +const arrayEquals = (a: Array, b: Array) => { + if (a.length !== b.length) { + return false + } + + return a.every((value, index) => value === b[index]) +} + +let warned = false + +const logDevError = (name: string) => { + warned = true + + // eslint-disable-next-line no-console + console.warn( + `We couldn't find your variable ${name}. Make sure it's used at least once in your className, or define it in a static theme as described in the docs: https://docs.uniwind.dev/api/use-css-variable`, + ) +} + +type CreateArray = []> = TAcc['length'] extends N ? TAcc : CreateArray + +type UseCSSVariable = { + (name: string): string | number | undefined + >(names: T): CreateArray +} + +/** + * A hook that returns the value of a CSS variable. + * @param name Name / Array of names of the CSS variable. + * @returns Value / Values of the CSS variable. On web it is always a string (1rem, #ff0000, etc.), but on native it can be a string or a number (16px, #ff0000) + */ +export const useCSSVariable: UseCSSVariable = (name: string | Array) => { + const [value, setValue] = useState(getValue(name)) + const nameRef = useRef(name) + + useEffect(() => { + if (Array.isArray(name) && Array.isArray(nameRef.current)) { + if (arrayEquals(name, nameRef.current)) { + return + } + + setValue(getValue(name)) + nameRef.current = name + + return + } + + if (name !== nameRef.current) { + setValue(getValue(name)) + nameRef.current = name + } + }, [name]) + + useEffect(() => { + const updateValue = () => setValue(getValue(nameRef.current)) + const dispose = UniwindListener.subscribe(updateValue, [StyleDependency.Theme]) + + return dispose + }, []) + + if (Array.isArray(value)) { + value.forEach((val, index) => { + if (val === undefined && __DEV__ && !warned) { + logDevError(name[index] ?? '') + } + }) + } + + if (value === undefined && __DEV__ && !warned) { + logDevError(name as string) + } + + return value as never +} From daf106807c014aadef7734696bf8407358eca97b Mon Sep 17 00:00:00 2001 From: Hubert Bieszczad Date: Tue, 25 Nov 2025 11:37:24 +0100 Subject: [PATCH 2/2] chore: improve useCSSVariable type --- packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts b/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts index 7d5241cb..8e9a0bba 100644 --- a/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts +++ b/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts @@ -27,11 +27,14 @@ const logDevError = (name: string) => { ) } +type IsGenericNumber = T & 0 extends -1 ? false : true type CreateArray = []> = TAcc['length'] extends N ? TAcc : CreateArray type UseCSSVariable = { (name: string): string | number | undefined - >(names: T): CreateArray + >( + names: T, + ): IsGenericNumber extends true ? Array : CreateArray } /**