From 894bc51ec522e35af19abd56579bede7fb8884e4 Mon Sep 17 00:00:00 2001 From: Christian Rodrigues Date: Thu, 9 Oct 2025 10:58:32 -0700 Subject: [PATCH] client(feat): add theme toggle component to navbar for dark/light mode switching --- client/components/ui/nav-bar.tsx | 33 ++++++++------ client/components/ui/theme-toggle.tsx | 62 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 client/components/ui/theme-toggle.tsx diff --git a/client/components/ui/nav-bar.tsx b/client/components/ui/nav-bar.tsx index 5b785bc..601ec72 100644 --- a/client/components/ui/nav-bar.tsx +++ b/client/components/ui/nav-bar.tsx @@ -8,6 +8,7 @@ import { useState } from "react"; import Button from "./button"; import useAuth from "@/lib/hooks/use-auth"; import { auth } from "@/firebase/firebase"; +import ThemeToggle from "./theme-toggle"; export default function NavBar() { const { user } = useAuth(); @@ -52,19 +53,25 @@ export default function NavBar() { - {user.photoURL ? ( - {"user auth.signOut()} - /> - ) : ( - // TODO: Set default profile icon - No Image - )} + +
+ {/* Theme toggle */} + + + {/* user avatar / sign out */} + {user.photoURL ? ( + {"user auth.signOut()} + /> + ) : ( + No Image + )} +
); diff --git a/client/components/ui/theme-toggle.tsx b/client/components/ui/theme-toggle.tsx new file mode 100644 index 0000000..bc3aa19 --- /dev/null +++ b/client/components/ui/theme-toggle.tsx @@ -0,0 +1,62 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { Sun, Moon } from "lucide-react"; + +/** + * ThemeToggle + * - toggles "dark" class on document.documentElement + * - persists choice in localStorage under "theme" + * - only supports "light" and "dark" (no "system" option) + */ +export default function ThemeToggle() { + const [theme, setTheme] = useState<"light" | "dark">("light"); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + const saved = localStorage.getItem("theme"); + if (saved === "light" || saved === "dark") { + applyTheme(saved); + setTheme(saved); + } else { + // default to system preference but store as concrete theme + const prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; + const initial = prefersDark ? "dark" : "light"; + applyTheme(initial); + setTheme(initial); + } + }, []); + + const applyTheme = (t: "light" | "dark") => { + const root = document.documentElement; + if (t === "dark") root.classList.add("dark"); + else root.classList.remove("dark"); + }; + + const toggle = () => { + const next = theme === "dark" ? "light" : "dark"; + localStorage.setItem("theme", next); + applyTheme(next); + setTheme(next); + }; + + if (!mounted) return null; + + const title = theme === "dark" ? "Theme: dark" : "Theme: light"; + + return ( + + ); +} \ No newline at end of file