diff --git a/src/components/ThemeSwitcher.tsx b/src/components/ThemeSwitcher.tsx new file mode 100644 index 0000000..f43aaef --- /dev/null +++ b/src/components/ThemeSwitcher.tsx @@ -0,0 +1,177 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Moon, Sun, Palette } from "lucide-react"; + +interface ThemeOption { + name: string; + label: string; + colors: { + background: string; + card: string; + foreground: string; + primaryCta: string; + secondaryCta: string; + accent: string; + backgroundAccent: string; + }; +} + +const PRESET_THEMES: Record = { + light: { + name: "light", label: "Light Mode", colors: { + background: "#ffffff", card: "#f9f9f9", foreground: "#000612e6", primaryCta: "#15479c", secondaryCta: "#f9f9f9", accent: "#e2e2e2", backgroundAccent: "#c4c4c4"}, + }, + dark: { + name: "dark", label: "Dark Mode", colors: { + background: "#0a0a0a", card: "#1a1a1a", foreground: "#ffffffe6", primaryCta: "#e6e6e6", secondaryCta: "#1a1a1a", accent: "#737373", backgroundAccent: "#737373"}, + }, + darkBlue: { + name: "darkBlue", label: "Dark Blue", colors: { + background: "#010912", card: "#152840", foreground: "#e6f0ff", primaryCta: "#cee7ff", secondaryCta: "#0e1a29", accent: "#3f5c79", backgroundAccent: "#004a93"}, + }, + emerald: { + name: "emerald", label: "Emerald", colors: { + background: "#000000", card: "#1f4035", foreground: "#ffffff", primaryCta: "#ffffff", secondaryCta: "#0d2b1f", accent: "#0d5238", backgroundAccent: "#10b981"}, + }, + violet: { + name: "violet", label: "Violet", colors: { + background: "#030128", card: "#241f48", foreground: "#ffffff", primaryCta: "#ffffff", secondaryCta: "#131136", accent: "#44358a", backgroundAccent: "#b597fe"}, + }, + ruby: { + name: "ruby", label: "Ruby", colors: { + background: "#000000", card: "#481f1f", foreground: "#ffffff", primaryCta: "#ffffff", secondaryCta: "#361311", accent: "#51000b", backgroundAccent: "#ff2231"}, + }, +}; + +export default function ThemeSwitcher() { + const [isOpen, setIsOpen] = useState(false); + const [currentTheme, setCurrentTheme] = useState("light"); + const [customColors, setCustomColors] = useState | null>(null); + const [showCustom, setShowCustom] = useState(false); + + useEffect(() => { + const saved = localStorage.getItem("theme"); + const savedCustom = localStorage.getItem("customTheme"); + if (saved) setCurrentTheme(saved); + if (savedCustom) { + setCustomColors(JSON.parse(savedCustom)); + setShowCustom(true); + } + }, []); + + const applyTheme = (theme: string) => { + const themeOption = PRESET_THEMES[theme]; + if (themeOption) { + Object.entries(themeOption.colors).forEach(([key, value]) => { + const cssKey = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`; + document.documentElement.style.setProperty(cssKey, value); + }); + setCurrentTheme(theme); + setShowCustom(false); + localStorage.setItem("theme", theme); + localStorage.removeItem("customTheme"); + } + }; + + const applyCustomTheme = (colors: Record) => { + Object.entries(colors).forEach(([key, value]) => { + const cssKey = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`; + if (value) document.documentElement.style.setProperty(cssKey, value); + }); + setCustomColors(colors); + setShowCustom(true); + localStorage.setItem("customTheme", JSON.stringify(colors)); + }; + + const handleColorChange = ( + colorKey: string, + value: string + ) => { + const updated = { ...customColors, [colorKey]: value }; + applyCustomTheme(updated); + }; + + return ( +
+ + + {isOpen && ( +
+
+

Select Theme

+ + {/* Preset themes */} +
+ {Object.entries(PRESET_THEMES).map(([key, theme]) => ( + + ))} +
+ + {/* Custom theme toggle */} + + + {/* Custom color picker */} + {showCustom && ( +
+
Custom Colors
+ {[ + { key: "background", label: "Background" }, + { key: "card", label: "Card" }, + { key: "foreground", label: "Foreground" }, + { key: "primaryCta", label: "Primary CTA" }, + { key: "secondaryCta", label: "Secondary CTA" }, + { key: "accent", label: "Accent" }, + { key: "backgroundAccent", label: "Background Accent" }, + ].map(({ key, label }) => ( +
+ + handleColorChange(key, e.target.value)} + className="w-8 h-8 rounded cursor-pointer" + aria-label={`Set ${label} color`} + /> + handleColorChange(key, e.target.value)} + className="flex-1 px-2 py-1 text-xs bg-background border border-accent rounded text-foreground" + placeholder="#000000" + /> +
+ ))} +
+ )} +
+
+ )} +
+ ); +}