-
Notifications
You must be signed in to change notification settings - Fork 41
Dynamically modify CSS variable at a context level #546
-
Idea
It would be great to allow overrides of CSS variables at a React Context level, rather than at global scope. This would allow themes, or overrides, to apply to part of the app, rather than the whole global scope.
Why current solution does not work?
Looking at update-css-variables and its details, it seems to work
at the global level.
Uniwind.updateCSSVariables('light', {
'color-primary': '#ff0000', // Missing -- prefix
})
- On web,
updateCSSVariablesapplies changes directly to the DOM usingdocument.documentElement.style.setProperty(). - On React Native,
updateCSSVariablesupdates the internal variable store with normalized values.
This changes css var values at global level, not at element level. Therefor changes are applied to whole app, not part of it. From css perspective, we can set var value at any level as needed.
Prior Art
- In NativeWind v5, you can use
VariableContextProviderto manage design tokens like colors or spacing at runtime without rebuilding your
stylesheet. VariableContextProvider - In NativeWind v4,
varsis a function that takes a dictionary of CSS variables and returns a style object that can be used in React
Native components. This allows you to set CSS variable values from JavaScript and have them flow down the component tree.
link
How would the API look?
Potentially could follow the VariableContextProvider pattern.
<VariableContextProvider variables={{ "--theme-color": "#3b82f6" }}>
<Text className="text-[--theme-color]">Blue text</Text>
</VariableContextProvider>
On web, this would probably mean setting the CSS var value on an injected div or on its children.
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 2 comments
-
Thanks for the detailed write-up. The API you suggested makes sense and feels like a reasonable direction. We'll discuss this internally and think through what the right API and implementation could look like across platforms. Thanks again for bringing this up.
Beta Was this translation helpful? Give feedback.
All reactions
-
Sure. Couple of thoughts on this potential api and changes. Did some exploration of correct code (JS side).
Api
import { ScopedVariables } from 'uniwind' <ScopedVariables variables={{ '--theme-color': '#3b82f6', '--gap': 8 }}> <Text className="text-[--theme-color]">Blue text</Text> <View className="gap-(--gap)" /> </ScopedVariables>
Was thinking about hook or var, but NW seems to move toward Component too.
Changes
// "packages/uniwind/src/core/context.ts" export const UniwindContext = createContext({ scopedTheme: null as ThemeName | null, variables: null as CSSVariables | null, })
on web we would need to have div container with contents like in theme
// `packages/uniwind/src/components/ScopedTheme/ScopedTheme.tsx` export const ScopedTheme: React.FC<React.PropsWithChildren<ScopedThemeProps>> = ({ theme, children }) => { const parent = useUniwindContext() const value = useMemo<UniwindContextType>( () => ({ scopedTheme: theme, variables: parent.variables }), [theme, parent.variables], ) return ( <UniwindContext.Provider value={value}> <div className={theme} style={{ display: 'contents' }}> {children} </div> </UniwindContext.Provider> ) }
// `packages/uniwind/src/components/ScopedVariables/ScopedVariables.tsx` const contextValue = useMemo<UniwindContextType>( () => ({ scopedTheme: parent.scopedTheme, variables: merged }), [parent.scopedTheme, merged], ) // this is from https://github.com/uni-stack/uniwind/blob/c8ed75fccb569dc7c37535a0a4762a112473c5f0/packages/uniwind/src/core/config/config.ts#L41 const inlineStyle = useMemo(() => { const style: Record<string, string | number> = { display: 'contents' } for (const [key, value] of Object.entries(variables)) { style[key] = typeof value === 'number' ? `${value}px` : value } return style }, [variables]) return ( <UniwindContext.Provider value={contextValue}> <div style={inlineStyle as React.CSSProperties}>{children}</div> </UniwindContext.Provider> )
I'm not sure about ``${value}pxfor css vars here, but seems like this is something already done inconfig` in same way.
Last part is about store & cache - probably just skip all together if var are set
// `packages/uniwind/src/core/native/store.ts` const hasContextVariables = uniwindContext.variables !== null if (hasContextVariables) { return this.resolveStyles(className, componentProps, state, uniwindContext) } const cacheKey = `${className}${state?.isDisabled ?? false}${state?.isFocused ?? false}${state?.isPressed ?? false}${isScopedTheme} // rest is same.
I've not looked too deep into native, it seems simpler. If you guys ok with above, I' probably prototype in a branch.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1