1- import { computed , type ComputedRef , type Ref , unref } from 'vue'
1+ import { computed , shallowRef , type ComputedRef , type Ref , type ShallowRef , unref } from 'vue'
22import { useMutationObserver , useResizeObserver , useSupported } from '@vueuse/core'
33
44type CssVariableSource = HTMLElement | null | undefined | Ref < HTMLElement | null | undefined >
55
6- type UseCssVariableOptions = {
7- element ?: CssVariableSource
8- watchResize ?: boolean
9- watchHtmlAttributes ?: boolean
10- }
6+ // Add existing css variables to expose in component scripts
7+ const colorVariables = [
8+ '--accent' ,
9+ '--bg' ,
10+ '--bg-elevated' ,
11+ '--bg-subtle' ,
12+ '--border' ,
13+ '--border-hover' ,
14+ '--border-subtle' ,
15+ '--fg' ,
16+ '--fg-muted' ,
17+ '--fg-subtle' ,
18+ ] as const
1119
1220function readCssVariable ( element : HTMLElement , variableName : string ) : string {
1321 return getComputedStyle ( element ) . getPropertyValue ( variableName ) . trim ( )
@@ -17,69 +25,42 @@ function toCamelCase(cssVariable: string): string {
1725 return cssVariable . replace ( / ^ - - / , '' ) . replace ( / - ( [ a - z 0 - 9 ] ) / gi, ( _ , c ) => c . toUpperCase ( ) )
1826}
1927
20- function resolveElement ( element ? : CssVariableSource ) : HTMLElement | null {
28+ function resolveElement ( element : CssVariableSource ) : HTMLElement | null {
2129 if ( typeof window === 'undefined' || typeof document === 'undefined' ) return null
22- if ( ! element ) return document . documentElement
2330 const resolved = unref ( element )
2431 return resolved ?? document . documentElement
2532}
2633
27- /**
28- * Read multiple CSS custom properties at once and expose them as a reactive object.
29- *
30- * Each CSS variable name is normalized into a camelCase key:
31- * - Leading `--` is removed
32- * - kebab-case is converted to camelCase
33- *
34- * Example:
35- * ```ts
36- * useCssVariables(['--bg', '--fg-subtle'])
37- * // => colors.value = { bg: '...', fgSubtle: '...' }
38- * ```
39- *
40- * The returned values are always resolved via `getComputedStyle`, meaning the
41- * effective value is returned (after cascade, theme classes, etc.).
42- *
43- * Reactivity behavior:
44- * - Updates automatically when the observed element changes
45- * - Can react to theme toggles via `watchHtmlAttributes`
46- * - Can react to responsive CSS variables via `watchResize`
47- *
48- * @param variables - List of CSS variable names (must include the leading `--`)
49- * @param options - Configuration options
50- * @param options.element - Element to read variables from (defaults to `:root`)
51- * @param options.watchResize - Re-evaluate values on resize (useful for media-query-driven variables)
52- * @param options.watchHtmlAttributes - Re-evaluate values when `<html>` attributes change
53- *
54- * @returns An object containing a reactive `colors` map, keyed by camelCase names
55- */
56- export function useCssVariables (
57- variables : readonly string [ ] ,
58- options : UseCssVariableOptions = { } ,
34+ export function useColors (
35+ element : ShallowRef < HTMLElement | null , HTMLElement | null > ,
36+ options : { watchHtmlAttributes ?: boolean ; watchResize ?: boolean } = { } ,
5937) : { colors : ComputedRef < Record < string , string > > } {
38+ const recomputeToken = shallowRef ( 0 )
39+ const invalidateColors = ( ) => {
40+ recomputeToken . value += 1
41+ }
42+
6043 const isClientSupported = useSupported (
6144 ( ) => typeof window !== 'undefined' && typeof document !== 'undefined' ,
6245 )
6346
64- const elementComputed = computed ( ( ) => resolveElement ( options . element ) )
65-
6647 const colors = computed < Record < string , string > > ( ( ) => {
67- const element = elementComputed . value
68- if ( ! element ) return { }
69-
48+ void recomputeToken . value
49+ const resolvedElement = resolveElement ( element )
50+ if ( ! resolvedElement ) return { }
7051 const result : Record < string , string > = { }
71- for ( const variable of variables ) {
72- result [ toCamelCase ( variable ) ] = readCssVariable ( element , variable )
52+ for ( const variable of colorVariables ) {
53+ result [ toCamelCase ( variable ) ] = readCssVariable ( resolvedElement , variable )
7354 }
7455 return result
7556 } )
7657
7758 if ( options . watchResize ) {
78- useResizeObserver ( elementComputed , ( ) => void colors . value )
59+ useResizeObserver ( element , invalidateColors )
7960 }
8061
8162 if ( options . watchHtmlAttributes && isClientSupported . value ) {
82- useMutationObserver ( document . documentElement , ( ) => void colors . value , {
63+ useMutationObserver ( document . documentElement , invalidateColors , {
8364 attributes : true ,
8465 attributeFilter : [ 'class' , 'style' , 'data-theme' , 'data-bg-theme' ] ,
8566 } )
0 commit comments