diff --git a/src/app/ColorSchemeInjector.tsx b/src/app/ColorSchemeInjector.tsx
new file mode 100644
index 00000000..cb8347fc
--- /dev/null
+++ b/src/app/ColorSchemeInjector.tsx
@@ -0,0 +1,42 @@
+"use client"
+
+import { useServerInsertedHTML } from "next/navigation"
+
+import {
+ COLOR_SCHEME_DARK,
+ COLOR_SCHEME_LIGHT,
+ DARK_MODE_STORAGE_KEY,
+} from "~/lib/constants"
+import { getStorage } from "~/lib/storage"
+
+export const ColorSchemeInjector = () => {
+ useServerInsertedHTML(() => {
+ return (
+
+ )
+ })
+ return null
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 13658a54..9426e227 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -11,6 +11,7 @@ import { getAcceptLang } from "~/lib/accept-lang"
import { APP_DESCRIPTION, APP_NAME, APP_SLOGAN, SITE_URL } from "~/lib/env"
import { getColorScheme } from "~/lib/get-color-scheme"
+import { ColorSchemeInjector } from "./ColorSchemeInjector"
import Providers from "./providers"
export const metadata: Metadata = {
@@ -95,7 +96,13 @@ export default function RootLayout({
}
return (
-
+
+
{modal}
diff --git a/src/hooks/useDarkMode.ts b/src/hooks/useDarkMode.ts
index 1ce908f7..4541da84 100644
--- a/src/hooks/useDarkMode.ts
+++ b/src/hooks/useDarkMode.ts
@@ -4,6 +4,7 @@ import { create } from "zustand"
import {
COLOR_SCHEME_DARK,
COLOR_SCHEME_LIGHT,
+ DARK_MODE_STORAGE_KEY,
DEFAULT_COLOR_SCHEME,
IS_DEV,
} from "~/lib/constants"
@@ -30,11 +31,9 @@ interface DarkModeConfig {
classNameDark?: string // A className to set "dark mode". Default = "dark".
classNameLight?: string // A className to set "light mode". Default = "light".
element?: HTMLElement | undefined | null // The element to apply the className. Default = `document.body`.
- storageKey?: string // Specify the `localStorage` key. Default = "darkMode". set to `undefined` to disable persistent storage.
transition?: ViewTransition | undefined // Specify the `animate` when switching the mode. Only Chromium >= 111 etc.
}
-const darkModeKey = "darkMode"
const useDarkModeInternal = (
initialState: boolean | undefined,
options: DarkModeConfig,
@@ -42,7 +41,6 @@ const useDarkModeInternal = (
const {
classNameDark = COLOR_SCHEME_DARK,
classNameLight = COLOR_SCHEME_LIGHT,
- storageKey = darkModeKey,
element,
transition,
} = options
@@ -51,29 +49,30 @@ const useDarkModeInternal = (
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 })
useEffect(() => {
- const presentedDarkMode = !isServerSide() && getStorage(storageKey)
+ const presentedDarkMode =
+ !isServerSide() && getStorage(DARK_MODE_STORAGE_KEY)
if (presentedDarkMode !== undefined) {
setDarkMode(presentedDarkMode === "true")
} else if (typeof initialState === "undefined") {
setDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches)
}
- }, [storageKey])
+ }, [])
useEffect(() => {
const handler = (e: MediaQueryListEvent) => {
- const storageValue = getStorage(storageKey)
+ const storageValue = getStorage(DARK_MODE_STORAGE_KEY)
const parseStorageValueAsBool = storageValue === "true"
setDarkMode(e.matches)
// reset dark mode, follow system
if (parseStorageValueAsBool === e.matches) {
- delStorage(storageKey)
+ delStorage(DARK_MODE_STORAGE_KEY)
}
}
const storageHandler = () => {
- const storageValue = getStorage(storageKey, true)
+ const storageValue = getStorage(DARK_MODE_STORAGE_KEY, true)
// if not storage color mode, switch to follow system
if (storageValue === undefined) {
setDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches)
@@ -94,7 +93,7 @@ const useDarkModeInternal = (
.matchMedia("(prefers-color-scheme: dark)")
.removeEventListener("change", handler)
}
- }, [storageKey])
+ }, [])
const getDarkMode = useGetState(darkMode)
useEffect(() => {
@@ -104,7 +103,7 @@ const useDarkModeInternal = (
window.matchMedia("(prefers-color-scheme: dark)").matches ===
getDarkMode()
) {
- delStorage(darkModeKey)
+ delStorage(DARK_MODE_STORAGE_KEY)
}
}
window.addEventListener("beforeunload", handler)
@@ -166,8 +165,8 @@ const useDarkModeInternal = (
value: darkMode,
toggle: (e: MouseEvent) => {
setDarkMode((d) => {
- if (storageKey && !isServerSide()) {
- setStorage(storageKey, String(!d))
+ if (DARK_MODE_STORAGE_KEY && !isServerSide()) {
+ setStorage(DARK_MODE_STORAGE_KEY, String(!d))
setMousePosition({ x: e.clientX, y: e.clientY })
}
return !d
@@ -184,18 +183,20 @@ const mockElement = {
}
export const useDarkMode = () => {
- const { toggle, value } = useDarkModeInternal(getStorage(darkModeKey), {
- classNameDark: COLOR_SCHEME_DARK,
- classNameLight: COLOR_SCHEME_LIGHT,
- storageKey: darkModeKey,
- element: (globalThis.document && document.documentElement) || mockElement,
- transition:
- !isServerSide() &&
- !!document.startViewTransition &&
- !window.matchMedia(`(prefers-reduced-motion: reduce)`).matches
- ? document.startViewTransition()
- : undefined,
- })
+ const { toggle, value } = useDarkModeInternal(
+ getStorage(DARK_MODE_STORAGE_KEY),
+ {
+ classNameDark: COLOR_SCHEME_DARK,
+ classNameLight: COLOR_SCHEME_LIGHT,
+ element: (globalThis.document && document.documentElement) || mockElement,
+ transition:
+ !isServerSide() &&
+ !!document.startViewTransition &&
+ !window.matchMedia(`(prefers-reduced-motion: reduce)`).matches
+ ? document.startViewTransition()
+ : undefined,
+ },
+ )
useEffect(() => {
useMediaStore.setState({
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 0db7c5f9..508e2a8f 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -16,3 +16,4 @@ export const MAXIMUM_FILE_SIZE = 100 // MB
export const COLOR_SCHEME_DARK = "dark"
export const COLOR_SCHEME_LIGHT = "light"
export const DEFAULT_COLOR_SCHEME: ColorScheme = "light"
+export const DARK_MODE_STORAGE_KEY = "darkMode"
diff --git a/src/lib/get-color-scheme.ts b/src/lib/get-color-scheme.ts
index 8056322a..06873445 100644
--- a/src/lib/get-color-scheme.ts
+++ b/src/lib/get-color-scheme.ts
@@ -1,7 +1,6 @@
import { cookies } from "next/headers"
-import { DEFAULT_COLOR_SCHEME } from "./constants"
import { ColorScheme } from "./types"
export const getColorScheme = () =>
- (cookies().get("color_scheme")?.value || DEFAULT_COLOR_SCHEME) as ColorScheme
+ cookies().get("color_scheme")?.value as ColorScheme