diff --git a/client/src/components/app-sidebar.tsx b/client/src/components/app-sidebar.tsx index 5245ad8febd..e47a067b9d9 100644 --- a/client/src/components/app-sidebar.tsx +++ b/client/src/components/app-sidebar.tsx @@ -1,16 +1,17 @@ import { Calendar, Home, Inbox, Search, Settings } from "lucide-react"; import { useParams } from "react-router-dom"; +import { ThemeToggle } from "@/components/theme-toggle"; import { Sidebar, SidebarContent, + SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, - SidebarTrigger, } from "@/components/ui/sidebar"; // Menu items. @@ -51,6 +52,9 @@ export function AppSidebar() { + + + ); } diff --git a/client/src/components/theme-toggle.tsx b/client/src/components/theme-toggle.tsx new file mode 100644 index 00000000000..91677a4a78b --- /dev/null +++ b/client/src/components/theme-toggle.tsx @@ -0,0 +1,19 @@ +import { Moon, Sun } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { useTheme } from "@/hooks/use-theme"; + +export function ThemeToggle() { + const { theme, setTheme } = useTheme(); + + return ( + + ); +} diff --git a/client/src/hooks/use-theme.tsx b/client/src/hooks/use-theme.tsx new file mode 100644 index 00000000000..8dd3f1cd2b6 --- /dev/null +++ b/client/src/hooks/use-theme.tsx @@ -0,0 +1,32 @@ +import { useEffect, useState } from "react"; + +type Theme = "dark" | "light" | "system"; + +function useTheme() { + const [theme, setTheme] = useState( + () => (localStorage.getItem("theme") as Theme) || "system" + ); + + useEffect(() => { + const media = window.matchMedia("(prefers-color-scheme: dark)"); + + function applyTheme() { + const root = window.document.documentElement; + const systemTheme = media.matches ? "dark" : "light"; + const activeTheme = theme === "system" ? systemTheme : theme; + + root.classList.remove("light", "dark"); + root.classList.add(activeTheme); + localStorage.setItem("theme", theme); + } + + applyTheme(); + media.addEventListener("change", applyTheme); + return () => media.removeEventListener("change", applyTheme); + }, [theme]); + + return { theme, setTheme } as const; +} + +export { useTheme }; +export type { Theme }; diff --git a/client/src/main.tsx b/client/src/main.tsx index 9b1aa69de2d..1c467c7d849 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -5,6 +5,12 @@ import "./index.css"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { RouterProvider } from "react-router-dom"; import { router } from "./router.tsx"; + +// Initialize theme +const theme = localStorage.getItem("theme") || "system"; +const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; +document.documentElement.classList.add(theme === "system" ? systemTheme : theme); + // Create a client const queryClient = new QueryClient();