export const useTheme = () => { const accent = useCookie('accent', { default: () => 'violet', maxAge: 60 * 60 * 24 * 365, }); const neutral = useCookie('neutral', { default: () => 'zinc', maxAge: 60 * 60 * 24 * 365, }); // disable hinting by default const hinting = useCookie('hinting', { default: () => '0', maxAge: 60 * 60 * 24 * 365, }); const colorScheme: { preference: Ref<'light' | 'dark' | 'system'>; value: Ref<'light' | 'dark'>; class: Ref<'light' | 'dark' | undefined> } = { preference: useCookie('colorScheme', { default: () => 'system', maxAge: 60 * 60 * 24 * 365, }), value: computed(() => { if (colorScheme.preference.value === 'system') { if (import.meta.client) { const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; return prefersDark ? 'dark' : 'light'; } return 'dark'; } return colorScheme.preference.value as 'light' | 'dark'; }), class: ref(undefined) } let listeningToColorScheme = false; const changeSystemColorScheme = (e: MediaQueryListEvent) => { if (colorScheme.preference.value !== 'system') return; colorScheme.class.value = e.matches ? 'dark' : 'light'; } watch(colorScheme.preference, () => { if (colorScheme.preference.value === 'system') { if (import.meta.client) { const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; if (!listeningToColorScheme) { listeningToColorScheme = true; window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', changeSystemColorScheme); } colorScheme.class.value = prefersDark ? 'dark' : 'light'; return; } if (listeningToColorScheme) { listeningToColorScheme = false; window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', changeSystemColorScheme); } // fallback to dark on the server colorScheme.class.value = 'dark'; return; } colorScheme.class.value = colorScheme.preference.value as 'light' | 'dark'; }, { immediate: true }); return { accent, neutral, hinting, colorScheme }; };