Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Dynamically modify CSS variable at a context level #546

dlebedynskyi started this conversation in Ideas
Discussion options

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, updateCSSVariables applies changes directly to the DOM using document.documentElement.style.setProperty().
  • On React Native, updateCSSVariables updates 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 VariableContextProvider to manage design tokens like colors or spacing at runtime without rebuilding your
    stylesheet. VariableContextProvider
  • In NativeWind v4, vars is 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.

You must be logged in to vote

Replies: 2 comments

Comment options

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.

You must be logged in to vote
0 replies
Comment options

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.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Ideas
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /