You've already forked AstralRinth
forked from didirus/AstralRinth
- Cosmetics and theme preferences are now only stored in cookies instead of a combination of both cookies and state. - The theme plugin now supports client hints. This allows the server to render a page using the client-preferred theme provided it supplies this information (any browser other than Firefox), helping to avoid an annoying flash while the page is hydrating. - For the future, the theme plugin now supports additional light themes. Light theme preferences are currently not stored, but this can easily be fixed if the need arises. - The previous workaround using the Nitro plugin has been removed. Its functionality is now handled by the Nuxt theme plugin with cleaner code. - All pages and components now use the new plugins. - Compared to the previous attempt, this commit has been improved to be more robust in cases where the theme cookie contains invalid values. Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
91 lines
2.3 KiB
TypeScript
91 lines
2.3 KiB
TypeScript
import { ref } from "vue";
|
|
import { isDarkTheme } from "./themes.ts";
|
|
import { useNativeTheme } from "./native-theme.ts";
|
|
import { usePreferredThemes } from "./preferred-theme.ts";
|
|
import { useThemeSettings } from "./theme-settings.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,
|
|
}),
|
|
},
|
|
};
|
|
},
|
|
});
|