Skip to content

Commit b386b26

Browse files
authored
Merge pull request elizaOS#1555 from zkfriendly/main
feat: add theme toggle functionality with dark and light mode support
2 parents 53ecdbf + f308a60 commit b386b26

File tree

4 files changed

+62
-1
lines changed

4 files changed

+62
-1
lines changed

client/src/components/app-sidebar.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import { Calendar, Home, Inbox, Search, Settings } from "lucide-react";
22
import { useParams } from "react-router-dom";
3+
import { ThemeToggle } from "@/components/theme-toggle";
34

45
import {
56
Sidebar,
67
SidebarContent,
8+
SidebarFooter,
79
SidebarGroup,
810
SidebarGroupContent,
911
SidebarGroupLabel,
1012
SidebarMenu,
1113
SidebarMenuButton,
1214
SidebarMenuItem,
13-
SidebarTrigger,
1415
} from "@/components/ui/sidebar";
1516

1617
// Menu items.
@@ -51,6 +52,9 @@ export function AppSidebar() {
5152
</SidebarGroupContent>
5253
</SidebarGroup>
5354
</SidebarContent>
55+
<SidebarFooter>
56+
<ThemeToggle />
57+
</SidebarFooter>
5458
</Sidebar>
5559
);
5660
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Moon, Sun } from "lucide-react";
2+
import { Button } from "@/components/ui/button";
3+
import { useTheme } from "@/hooks/use-theme";
4+
5+
export function ThemeToggle() {
6+
const { theme, setTheme } = useTheme();
7+
8+
return (
9+
<Button
10+
variant="ghost"
11+
size="icon"
12+
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
13+
>
14+
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
15+
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
16+
<span className="sr-only">Toggle theme</span>
17+
</Button>
18+
);
19+
}

client/src/hooks/use-theme.tsx

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useEffect, useState } from "react";
2+
3+
type Theme = "dark" | "light" | "system";
4+
5+
function useTheme() {
6+
const [theme, setTheme] = useState<Theme>(
7+
() => (localStorage.getItem("theme") as Theme) || "system"
8+
);
9+
10+
useEffect(() => {
11+
const media = window.matchMedia("(prefers-color-scheme: dark)");
12+
13+
function applyTheme() {
14+
const root = window.document.documentElement;
15+
const systemTheme = media.matches ? "dark" : "light";
16+
const activeTheme = theme === "system" ? systemTheme : theme;
17+
18+
root.classList.remove("light", "dark");
19+
root.classList.add(activeTheme);
20+
localStorage.setItem("theme", theme);
21+
}
22+
23+
applyTheme();
24+
media.addEventListener("change", applyTheme);
25+
return () => media.removeEventListener("change", applyTheme);
26+
}, [theme]);
27+
28+
return { theme, setTheme } as const;
29+
}
30+
31+
export { useTheme };
32+
export type { Theme };

client/src/main.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import "./index.css";
55
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
66
import { RouterProvider } from "react-router-dom";
77
import { router } from "./router.tsx";
8+
9+
// Initialize theme
10+
const theme = localStorage.getItem("theme") || "system";
11+
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
12+
document.documentElement.classList.add(theme === "system" ? systemTheme : theme);
13+
814
// Create a client
915
const queryClient = new QueryClient();
1016

0 commit comments

Comments
 (0)