import { ref } from 'vue' import { useNativeTheme } from './native-theme.ts' import { usePreferredThemes } from './preferred-theme.ts' import { useThemeSettings } from './theme-settings.ts' import { isDarkTheme } from './themes.ts' export * from './themes.ts' export default defineNuxtPlugin({ name: 'theme', dependsOn: ['cosmetics'], setup(nuxtApp) { const $nativeTheme = useNativeTheme() const $preferredThemes = usePreferredThemes() function getPreferredNativeTheme() { const nativeTheme = $nativeTheme.value switch (nativeTheme) { case 'light': return $preferredThemes.light case 'dark': case 'unknown': if (import.meta.dev && import.meta.server && nativeTheme === 'unknown') { console.warn( '[theme] no client hint is available for request, using dark theme as default', ) } return $preferredThemes.dark } } const $settings = useThemeSettings(() => getPreferredNativeTheme()) useHead({ htmlAttrs: { class: () => [`${$settings.active}-mode`] } }) function syncTheme() { $settings.active = $settings.preferred === 'system' ? getPreferredNativeTheme() : $settings.preferred } if ( import.meta.server && $settings.preferred === 'system' && $nativeTheme.value !== 'unknown' ) { // take advantage of the client hint syncTheme() } if (import.meta.client) { const $clientReady = ref(false) nuxtApp.hook('app:suspense:resolve', () => { $clientReady.value = true }) watchEffect(() => $clientReady.value && syncTheme()) } function cycle() { const nextTheme = isDarkTheme($settings.active) ? $preferredThemes.light : $preferredThemes.dark $settings.preferred = nextTheme return nextTheme } return { provide: { theme: reactive({ ...toRefs($settings), /** * Preferred themes for each mode. */ preferences: $preferredThemes, /** * Current native (system) theme provided through client hint header or * `prefers-color-scheme` media query. */ native: $nativeTheme, cycle, }), }, } }, })