diff --git a/apps/web/src/app/dashboard/layout.tsx b/apps/web/src/app/dashboard/layout.tsx index 0e42402..7f91fc5 100644 --- a/apps/web/src/app/dashboard/layout.tsx +++ b/apps/web/src/app/dashboard/layout.tsx @@ -1,20 +1,24 @@ import { UserButton } from "@clerk/nextjs"; import Link from "next/link"; +import { ThemeToggle } from "@/components/theme-toggle"; export default function DashboardLayout({ children }: { children: React.ReactNode }) { return ( -
-
-

ForgeLens

+
+
+

ForgeLens

- +
+ + +
{children} diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index a2dc41e..2608917 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -3,6 +3,13 @@ :root { --background: #ffffff; --foreground: #171717; + --card-bg: #ffffff; + --card-border: #e5e7eb; + --text-primary: #111827; + --text-secondary: #6b7280; + --text-muted: #9ca3af; + --surface: #f9fafb; + --surface-hover: #f3f4f6; } @theme inline { @@ -12,15 +19,21 @@ --font-mono: var(--font-geist-mono); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +.dark { + --background: #0a0a0a; + --foreground: #ededed; + --card-bg: #1a1a2e; + --card-border: #2d2d44; + --text-primary: #f9fafb; + --text-secondary: #d1d5db; + --text-muted: #9ca3af; + --surface: #111827; + --surface-hover: #1f2937; } body { background: var(--background); color: var(--foreground); font-family: Arial, Helvetica, sans-serif; + transition: background-color 0.2s ease, color 0.2s ease; } diff --git a/apps/web/src/components/theme-toggle.tsx b/apps/web/src/components/theme-toggle.tsx new file mode 100644 index 0000000..284d57e --- /dev/null +++ b/apps/web/src/components/theme-toggle.tsx @@ -0,0 +1,101 @@ +"use client"; + +import { useState, useEffect } from "react"; + +type Theme = "light" | "dark" | "system"; + +function getSystemPreference(): "light" | "dark" { + if (typeof window === "undefined") return "light"; + return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; +} + +function applyTheme(theme: Theme) { + const resolved = theme === "system" ? getSystemPreference() : theme; + const root = document.documentElement; + if (resolved === "dark") { + root.classList.add("dark"); + } else { + root.classList.remove("dark"); + } +} + +export function ThemeToggle() { + const [theme, setTheme] = useState("system"); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + const stored = localStorage.getItem("forgelens-theme") as Theme | null; + if (stored && ["light", "dark", "system"].includes(stored)) { + setTheme(stored); + applyTheme(stored); + } else { + setTheme("system"); + applyTheme("system"); + } + setMounted(true); + }, []); + + useEffect(() => { + if (!mounted) return; + + if (theme === "system") { + const mq = window.matchMedia("(prefers-color-scheme: dark)"); + const handler = () => applyTheme("system"); + mq.addEventListener("change", handler); + applyTheme("system"); + return () => mq.removeEventListener("change", handler); + } else { + applyTheme(theme); + } + }, [theme, mounted]); + + const cycleTheme = () => { + const themes: Theme[] = ["light", "dark", "system"]; + const next = themes[(themes.indexOf(theme) + 1) % themes.length]; + setTheme(next); + localStorage.setItem("forgelens-theme", next); + }; + + if (!mounted) { + return ( + + ); + } + + return ( + + ); +}