diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 59d5805..121e9d9 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -2,12 +2,21 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import App from './App.tsx'; import { Toaster } from 'react-hot-toast'; +import { initSystemThemeWatcher } from './theme.ts'; + +initSystemThemeWatcher(); createRoot(document.getElementById('root')!).render( <> - + , ); diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts new file mode 100644 index 0000000..524cfd2 --- /dev/null +++ b/frontend/src/theme.ts @@ -0,0 +1,34 @@ +const applyThemeToDocument = (isDark: boolean) => { + if (typeof document === "undefined") { + return; + } + const theme = isDark ? "dark" : "light"; + const root = document.documentElement; + const body = document.body; + + root.classList.toggle("dark", isDark); + root.setAttribute("data-theme", theme); + + if (body) { + body.classList.toggle("dark", isDark); + body.setAttribute("data-theme", theme); + body.style.colorScheme = isDark ? "dark" : "light"; + } +}; + +export const initSystemThemeWatcher = () => { + if (typeof window === "undefined" || typeof window.matchMedia === "undefined") { + return; + } + + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); + const applyMatch = (matches: boolean) => applyThemeToDocument(matches); + const handleChange = (event: MediaQueryListEvent) => applyMatch(event.matches); + + applyMatch(mediaQuery.matches); + if (typeof mediaQuery.addEventListener === "function") { + mediaQuery.addEventListener("change", handleChange); + } else if (typeof mediaQuery.addListener === "function") { + mediaQuery.addListener(handleChange); + } +};