11// Utilities
2+ import {
3+ createTheme as createV0Theme ,
4+ usePrefersDark ,
5+ } from '@vuetify/v0'
26import {
37 computed ,
4- getCurrentScope ,
58 inject ,
6- onScopeDispose ,
79 provide ,
810 ref ,
911 shallowRef ,
1012 toRef ,
1113 watch ,
1214 watchEffect ,
1315} from 'vue'
16+ import {
17+ genCssVariables ,
18+ genOnColors ,
19+ genVariations ,
20+ parseThemeOptions ,
21+ } from './colors'
1422import {
1523 consoleWarn ,
1624 deprecate ,
1725 getCurrentInstance ,
1826 IN_BROWSER ,
1927 mergeDeep ,
2028 propsFactory ,
21- SUPPORTS_MATCH_MEDIA ,
2229} from '@/util'
23- import {
24- genCssVariables ,
25- genOnColors ,
26- genVariations ,
27- parseThemeOptions ,
28- } from './colors'
2930
3031// Types
3132import type { VueHeadClient } from '@unhead/vue/client'
@@ -109,23 +110,78 @@ function getOrCreateStyleElement (id: string, cspNonce?: string) {
109110// Composables
110111export function createTheme ( options ?: ThemeOptions ) : ThemeInstance & { install : ( app : App ) => void } {
111112 const parsedOptions = parseThemeOptions ( options )
112- const _name = shallowRef ( parsedOptions . defaultTheme )
113113 const themes = ref ( parsedOptions . themes )
114- const systemName = shallowRef ( 'light' )
114+
115+ // Build processed themes with variations + on-colors for v0 registration
116+ function buildV0Themes ( ) {
117+ const processed : Record < string , { dark : boolean , colors : Record < string , string > } > = { }
118+ for ( const [ themeName , original ] of Object . entries ( themes . value ) ) {
119+ const defaultTheme = original . dark || themeName === 'dark'
120+ ? themes . value . dark
121+ : themes . value . light
122+
123+ const merged = mergeDeep ( defaultTheme , original ) as InternalThemeDefinition
124+ const colors = {
125+ ...merged . colors ,
126+ ...genVariations ( merged . colors , parsedOptions . variations ) ,
127+ }
128+ const fullColors = {
129+ ...colors ,
130+ ...genOnColors ( colors , merged . variables ) ,
131+ }
132+
133+ processed [ themeName ] = {
134+ dark : merged . dark ,
135+ colors : fullColors as Record < string , string > ,
136+ }
137+ }
138+ return processed
139+ }
140+
141+ // Resolve default theme — v0 doesn't understand 'system'
142+ const isSystemDefault = parsedOptions . defaultTheme === 'system'
143+ const { matches : prefersDark } = usePrefersDark ( )
144+ const resolvedDefault = isSystemDefault
145+ ? ( prefersDark . value ? 'dark' : 'light' )
146+ : parsedOptions . defaultTheme
147+
148+ // Create v0 theme instance
149+ const v0Theme = createV0Theme ( {
150+ reactive : true ,
151+ foreground : false , // We handle on-colors ourselves via genOnColors
152+ default : resolvedDefault ,
153+ themes : buildV0Themes ( ) ,
154+ } )
155+
156+ // Bridge v0's selectedId to Vuetify's name ref
157+ const _name = shallowRef ( parsedOptions . defaultTheme )
115158
116159 const name = computed ( {
117160 get ( ) {
118- return _name . value === 'system' ? systemName . value : _name . value
161+ if ( _name . value === 'system' ) {
162+ return prefersDark . value ? 'dark' : 'light'
163+ }
164+ return String ( v0Theme . selectedId . value ?? resolvedDefault )
119165 } ,
120166 set ( val : string ) {
121167 _name . value = val
168+ if ( val !== 'system' ) {
169+ v0Theme . select ( val )
170+ }
122171 } ,
123172 } )
124173
174+ // When system preference changes and we're in system mode, update v0 selection
175+ watch ( prefersDark , dark => {
176+ if ( _name . value === 'system' ) {
177+ v0Theme . select ( dark ? 'dark' : 'light' )
178+ }
179+ } )
180+
125181 const computedThemes = computed ( ( ) => {
126182 const acc : Record < string , InternalThemeDefinition > = { }
127- for ( const [ name , original ] of Object . entries ( themes . value ) ) {
128- const defaultTheme = original . dark || name === 'dark'
183+ for ( const [ themeName , original ] of Object . entries ( themes . value ) ) {
184+ const defaultTheme = original . dark || themeName === 'dark'
129185 ? themes . value . dark
130186 : themes . value . light
131187
@@ -136,7 +192,7 @@ export function createTheme (options?: ThemeOptions): ThemeInstance & { install:
136192 ...genVariations ( merged . colors , parsedOptions . variations ) ,
137193 }
138194
139- acc [ name ] = {
195+ acc [ themeName ] = {
140196 ...merged ,
141197 colors : {
142198 ...colors ,
@@ -207,24 +263,6 @@ export function createTheme (options?: ThemeOptions): ThemeInstance & { install:
207263 const themeClasses = toRef ( ( ) => parsedOptions . isDisabled ? undefined : `${ parsedOptions . prefix } theme--${ name . value } ` )
208264 const themeNames = toRef ( ( ) => Object . keys ( computedThemes . value ) )
209265
210- if ( SUPPORTS_MATCH_MEDIA ) {
211- const media = window . matchMedia ( '(prefers-color-scheme: dark)' )
212-
213- function updateSystemName ( ) {
214- systemName . value = media . matches ? 'dark' : 'light'
215- }
216-
217- updateSystemName ( )
218-
219- media . addEventListener ( 'change' , updateSystemName , { passive : true } )
220-
221- if ( getCurrentScope ( ) ) {
222- onScopeDispose ( ( ) => {
223- media . removeEventListener ( 'change' , updateSystemName )
224- } )
225- }
226- }
227-
228266 function install ( app : App ) {
229267 if ( parsedOptions . isDisabled ) return
230268
0 commit comments