Skip to content

Commit d3e5ec7

Browse files
authored
feat: add dark/light theme switcher (#20)
Closes #6 - Adds useTheme hook with localStorage persistence and system preference fallback - Adds Moon/Sun toggle button in sidebar header - Enables Tailwind darkMode: 'class'
1 parent 86bb6fa commit d3e5ec7

2 files changed

Lines changed: 27 additions & 0 deletions

File tree

src/App.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,25 @@ import {
5050
GripVertical,
5151
PlayCircle,
5252
Shield as ShieldIcon,
53+
Moon,
54+
Sun,
5355
} from "lucide-react";
56+
57+
// ── theme ─────────────────────────────────────────────────────────────────────
58+
function useTheme() {
59+
const [dark, setDark] = useState<boolean>(() => {
60+
const saved = localStorage.getItem("theme");
61+
if (saved) return saved === "dark";
62+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
63+
});
64+
65+
useEffect(() => {
66+
document.documentElement.classList.toggle("dark", dark);
67+
localStorage.setItem("theme", dark ? "dark" : "light");
68+
}, [dark]);
69+
70+
return { dark, toggle: () => setDark((d) => !d) };
71+
}
5472
import UnixTimeConverter from "./components/UnixTimeConverter";
5573
import JsonValidator from "./components/JsonValidator";
5674
import { Base64SideBySide } from "./components/Base64SideBySide";
@@ -197,6 +215,7 @@ function Layout({ tools: defaultTools }: { tools: Tool[] }) {
197215
const [isShortcutsOpen, setIsShortcutsOpen] = useState(false);
198216
const [isSidebarExpanded, setIsSidebarExpanded] = useState(true);
199217
const [isDragActive, setIsDragActive] = useState(false);
218+
const { dark, toggle: toggleTheme } = useTheme();
200219
const { state, startTour, hideWelcome, notifyDragComplete } = useOnboarding();
201220
const [tools, setTools] = useState<Tool[]>(() => {
202221
const savedOrder = localStorage.getItem("toolsOrder");
@@ -398,6 +417,13 @@ function Layout({ tools: defaultTools }: { tools: Tool[] }) {
398417
>
399418
<PlayCircle size={18} />
400419
</button>
420+
<button
421+
onClick={toggleTheme}
422+
className="p-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
423+
title={dark ? "Switch to light mode" : "Switch to dark mode"}
424+
>
425+
{dark ? <Sun size={18} /> : <Moon size={18} />}
426+
</button>
401427
<button
402428
onClick={() => setIsSidebarExpanded(false)}
403429
className="p-1.5 hover:bg-gray-100 rounded"

tailwind.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/** @type {import('tailwindcss').Config} */
22
export default {
33
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
4+
darkMode: 'class',
45
theme: {
56
extend: {
67
animation: {

0 commit comments

Comments
 (0)