From bf175ad125b3d4c18863e01ab01e7557546fa7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBeeRad=E2=80=9D?= Date: Tue, 17 Mar 2026 19:32:06 +1100 Subject: [PATCH] feat: add persistent light/dark mode toggle - Add CSS custom property token system to globals.css (dark + light variants) - Add anti-FOUC inline script in layout.tsx head to apply stored theme before hydration - Create useTheme hook (src/hooks/useTheme.ts) backed by usePersistentState - Add Sun/Moon toggle button to LeftToolbar above Settings - Wire useTheme into ThreePanelLayout and pass theme/onThemeToggle to LeftToolbar - Migrate hardcoded hex values to CSS vars in LeftToolbar and ThreePanelLayout Co-Authored-By: Claude Sonnet 4.6 --- app/globals.css | 73 ++++++++++++++++++++-- app/layout.tsx | 26 +++++++- src/components/layout/LeftToolbar.tsx | 23 +++++-- src/components/layout/ThreePanelLayout.tsx | 7 ++- src/hooks/useTheme.ts | 28 +++++++++ 5 files changed, 144 insertions(+), 13 deletions(-) create mode 100644 src/hooks/useTheme.ts diff --git a/app/globals.css b/app/globals.css index 69debcc..92a8651 100644 --- a/app/globals.css +++ b/app/globals.css @@ -36,6 +36,67 @@ @tailwind components; @tailwind utilities; +:root, +html[data-theme="dark"] { + color-scheme: dark; + --rah-bg-base: #0a0a0a; + --rah-bg-surface: #111111; + --rah-bg-subtle: #121212; + --rah-bg-panel: #141414; + --rah-bg-hover: #151515; + --rah-bg-elevated: #161616; + --rah-bg-active: #1a1a1a; + --rah-bg-modal: #141414; + --rah-border: #1a1a1a; + --rah-border-strong: #2a2a2a; + --rah-border-stronger: #3a3a3a; + --rah-scrollbar: #353535; + --rah-scrollbar-hover: #4a4a4a; + --rah-text-base: #e5e5e5; + --rah-text-secondary: #c7c7c7; + --rah-text-muted: #7a7a7a; + --rah-text-soft: #a3a3a3; + --rah-text-active: #f0f0f0; + --rah-text-inverse: #0a0a0a; + --rah-accent-green: #22c55e; + --rah-accent-green-hover: #16a34a; + --rah-accent-green-soft: rgba(34, 197, 94, 0.14); + --rah-accent-green-soft-strong: rgba(34, 197, 94, 0.22); + --rah-backdrop: rgba(0, 0, 0, 0.85); + --rah-shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.05); + --rah-shadow-floating: 0 4px 12px rgba(0, 0, 0, 0.4); +} + +html[data-theme="light"] { + color-scheme: light; + --rah-bg-base: #ffffff; + --rah-bg-surface: #fafafa; + --rah-bg-subtle: #f7f7f7; + --rah-bg-panel: #f5f5f5; + --rah-bg-hover: #f0f0f0; + --rah-bg-elevated: #ececec; + --rah-bg-active: #e8e8e8; + --rah-bg-modal: #ffffff; + --rah-border: #e5e5e5; + --rah-border-strong: #d4d4d4; + --rah-border-stronger: #bdbdbd; + --rah-scrollbar: #d4d4d4; + --rah-scrollbar-hover: #b5b5b5; + --rah-text-base: #111111; + --rah-text-secondary: #333333; + --rah-text-muted: #6b7280; + --rah-text-soft: #4b5563; + --rah-text-active: #000000; + --rah-text-inverse: #ffffff; + --rah-accent-green: #16a34a; + --rah-accent-green-hover: #15803d; + --rah-accent-green-soft: rgba(22, 163, 74, 0.12); + --rah-accent-green-soft-strong: rgba(22, 163, 74, 0.2); + --rah-backdrop: rgba(0, 0, 0, 0.5); + --rah-shadow-modal: 0 10px 36px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.04); + --rah-shadow-floating: 0 6px 20px rgba(0, 0, 0, 0.16); +} + /* Modern Clean Design System - RA-H */ * { margin: 0; @@ -46,8 +107,8 @@ html, body { height: 100%; overflow: hidden; - background-color: #0a0a0a; - color: #e5e5e5; + background-color: var(--rah-bg-base); + color: var(--rah-text-base); /* Geist Sans for optimal reading/writing experience */ font-family: 'Geist', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; @@ -71,21 +132,21 @@ html, body { } ::-webkit-scrollbar-track { - background: #0a0a0a; + background: var(--rah-bg-base); } ::-webkit-scrollbar-thumb { - background: #353535; + background: var(--rah-scrollbar); border-radius: 2px; } ::-webkit-scrollbar-thumb:hover { - background: #4a4a4a; + background: var(--rah-scrollbar-hover); } /* Utility classes */ .border-divider { - border-color: #1a1a1a; + border-color: var(--rah-border); } .text-muted { diff --git a/app/layout.tsx b/app/layout.tsx index bbad70a..b5b29f0 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -13,7 +13,31 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - + + +