diff --git a/apps/frontend/nuxt.config.ts b/apps/frontend/nuxt.config.ts
index 513fd5e9..e8de813c 100644
--- a/apps/frontend/nuxt.config.ts
+++ b/apps/frontend/nuxt.config.ts
@@ -392,14 +392,6 @@ export default defineNuxtConfig({
autoprefixer: {},
},
},
- routeRules: {
- "/**": {
- headers: {
- "Accept-CH": "Sec-CH-Prefers-Color-Scheme",
- "Critical-CH": "Sec-CH-Prefers-Color-Scheme",
- },
- },
- },
compatibilityDate: "2024-07-03",
});
diff --git a/apps/frontend/src/components/ui/charts/ChartDisplay.vue b/apps/frontend/src/components/ui/charts/ChartDisplay.vue
index 36a603f5..852c687d 100644
--- a/apps/frontend/src/components/ui/charts/ChartDisplay.vue
+++ b/apps/frontend/src/components/ui/charts/ChartDisplay.vue
@@ -162,7 +162,7 @@
+ useState("cosmetics", () => {
+ const cosmetics = useCookie("cosmetics", {
+ maxAge: 60 * 60 * 24 * 365 * 10,
+ sameSite: "lax",
+ secure: true,
+ httpOnly: false,
+ path: "/",
+ });
+
+ if (!cosmetics.value) {
+ cosmetics.value = {
+ searchLayout: false,
+ projectLayout: false,
+ advancedRendering: true,
+ externalLinksNewTab: true,
+ notUsingBlockers: false,
+ hideModrinthAppPromos: false,
+ preferredDarkTheme: "dark",
+ searchDisplayMode: {
+ mod: "list",
+ plugin: "list",
+ resourcepack: "gallery",
+ modpack: "list",
+ shader: "gallery",
+ datapack: "list",
+ user: "list",
+ collection: "list",
+ },
+ hideStagingBanner: false,
+ };
+ }
+
+ return cosmetics.value;
+ });
+
+export const saveCosmetics = () => {
+ const cosmetics = useCosmetics();
+
+ console.log("SAVING COSMETICS:");
+ console.log(cosmetics);
+
+ const cosmeticsCookie = useCookie("cosmetics", {
+ maxAge: 60 * 60 * 24 * 365 * 10,
+ sameSite: "lax",
+ secure: true,
+ httpOnly: false,
+ path: "/",
+ });
+
+ cosmeticsCookie.value = cosmetics.value;
+};
diff --git a/apps/frontend/src/composables/nuxt-accessors.ts b/apps/frontend/src/composables/nuxt-accessors.ts
deleted file mode 100644
index 5130f626..00000000
--- a/apps/frontend/src/composables/nuxt-accessors.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export function useTheme() {
- return useNuxtApp().$theme;
-}
-
-export function useCosmetics() {
- return useNuxtApp().$cosmetics;
-}
diff --git a/apps/frontend/src/composables/theme.js b/apps/frontend/src/composables/theme.js
new file mode 100644
index 00000000..460ebdf1
--- /dev/null
+++ b/apps/frontend/src/composables/theme.js
@@ -0,0 +1,58 @@
+export const useTheme = () =>
+ useState("theme", () => {
+ const colorMode = useCookie("color-mode", {
+ maxAge: 60 * 60 * 24 * 365 * 10,
+ sameSite: "lax",
+ secure: true,
+ httpOnly: false,
+ path: "/",
+ });
+
+ if (!colorMode.value) {
+ colorMode.value = {
+ value: "dark",
+ preference: "system",
+ };
+ }
+
+ if (colorMode.value.preference !== "system") {
+ colorMode.value.value = colorMode.value.preference;
+ }
+
+ return colorMode.value;
+ });
+
+export const updateTheme = (value, updatePreference = false) => {
+ const theme = useTheme();
+ const cosmetics = useCosmetics();
+
+ const themeCookie = useCookie("color-mode", {
+ maxAge: 60 * 60 * 24 * 365 * 10,
+ sameSite: "lax",
+ secure: true,
+ httpOnly: false,
+ path: "/",
+ });
+
+ if (value === "system") {
+ theme.value.preference = "system";
+
+ const colorSchemeQueryList = window.matchMedia("(prefers-color-scheme: light)");
+ if (colorSchemeQueryList.matches) {
+ theme.value.value = "light";
+ } else {
+ theme.value.value = cosmetics.value.preferredDarkTheme;
+ }
+ } else {
+ theme.value.value = value;
+ if (updatePreference) theme.value.preference = value;
+ }
+
+ if (import.meta.client) {
+ document.documentElement.className = `${theme.value.value}-mode`;
+ }
+
+ themeCookie.value = theme.value;
+};
+
+export const DARK_THEMES = ["dark", "oled", "retro"];
diff --git a/apps/frontend/src/composables/vue.ts b/apps/frontend/src/composables/vue.ts
deleted file mode 100644
index cf49bc46..00000000
--- a/apps/frontend/src/composables/vue.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Creates a computed reference that uses a provide getter function called with an argument representing the current mount state of the component.
- * @param getter A getter function that will run with `mounted` argument representing whether or not the component is mounted.
- * @returns A computed reference that changes when component becomes mounted or unmounted.
- */
-export function useMountedValue
(getter: (isMounted: boolean) => T) {
- const mounted = ref(getCurrentInstance()?.isMounted ?? false);
-
- onMounted(() => (mounted.value = true));
-
- onUnmounted(() => (mounted.value = false));
-
- return computed(() => getter(mounted.value));
-}
diff --git a/apps/frontend/src/layouts/default.vue b/apps/frontend/src/layouts/default.vue
index 863ad8b3..4be98be2 100644
--- a/apps/frontend/src/layouts/default.vue
+++ b/apps/frontend/src/layouts/default.vue
@@ -62,7 +62,7 @@
:title="formatMessage(messages.changeTheme)"
@click="changeTheme"
>
-
+
diff --git a/apps/frontend/src/pages/auth/sign-in.vue b/apps/frontend/src/pages/auth/sign-in.vue
index 0110affe..7dc22f7b 100644
--- a/apps/frontend/src/pages/auth/sign-in.vue
+++ b/apps/frontend/src/pages/auth/sign-in.vue
@@ -85,7 +85,7 @@
ref="turnstile"
v-model="token"
class="turnstile"
- :options="{ theme: $theme.active === 'light' ? 'light' : 'dark' }"
+ :options="{ theme: $colorMode.value === 'light' ? 'light' : 'dark' }"
/>
-
+
{{ colorTheme[option] ? formatMessage(colorTheme[option]) : option }}
@@ -169,6 +170,7 @@
v-model="cosmetics.externalLinksNewTab"
class="switch stylized-toggle"
type="checkbox"
+ @change="saveCosmetics"
/>
@@ -185,6 +187,7 @@
v-model="cosmetics.hideModrinthAppPromos"
class="switch stylized-toggle"
type="checkbox"
+ @change="saveCosmetics"
/>
@@ -201,6 +204,7 @@
v-model="cosmetics.searchLayout"
class="switch stylized-toggle"
type="checkbox"
+ @change="saveCosmetics"
/>
@@ -217,19 +221,19 @@
v-model="cosmetics.projectLayout"
class="switch stylized-toggle"
type="checkbox"
+ @change="saveCosmetics"
/>
-
diff --git a/apps/frontend/src/pages/user/[id].vue b/apps/frontend/src/pages/user/[id].vue
index f7dbd010..dbd82fcb 100644
--- a/apps/frontend/src/pages/user/[id].vue
+++ b/apps/frontend/src/pages/user/[id].vue
@@ -489,6 +489,7 @@ function cycleSearchDisplayMode() {
cosmetics.value.searchDisplayMode.user,
tags.value.projectViewModes,
);
+ saveCosmetics();
}