# Light/Dark Theme System Design ## Goal Add a light theme option alongside the existing dark theme, with user-togglable switching via the Settings page. The toggle persists in localStorage and applies globally across all pages including login. ## Architecture ### CSS Variables + `dark` class on `` Standard shadcn/Tailwind pattern: 1. **`:root`** defines light theme CSS variables (default) 2. **`.dark`** class on `` overrides with dark theme values 3. **`ThemeProvider`** React context manages the class toggle and localStorage sync 4. All components use semantic Tailwind utilities (`bg-background`, `text-foreground`, etc.) instead of hardcoded `bg-gray-900`, `text-white` ### Theme Provider ```tsx // src/contexts/ThemeContext.tsx const ThemeContext = createContext<{ theme: 'light' | 'dark'; toggleTheme: () => void }>() export function ThemeProvider({ children }) { const [theme, setTheme] = useState(() => localStorage.getItem('settings:darkMode') !== 'false' ? 'dark' : 'light') useEffect(() => { document.documentElement.classList.toggle('dark', theme === 'dark') localStorage.setItem('settings:darkMode', theme === 'dark' ? 'true' : 'false') }, [theme]) const toggleTheme = () => setTheme(t => t === 'dark' ? 'light' : 'dark') return {children} } ``` ### CSS Variable Definitions In `index.css`, using Tailwind v4 `@theme`: ```css /* Light theme (default) */ :root { --color-background: oklch(0.98 0 0); /* near white */ --color-foreground: oklch(0.15 0.01 240); /* near black */ --color-card: oklch(1.0 0 0); /* white */ --color-card-foreground: oklch(0.15 0.01 240); --color-muted: oklch(0.96 0.005 240); /* gray-100 */ --color-muted-foreground: oklch(0.45 0.01 240); /* gray-500 */ --color-border: oklch(0.90 0.005 240); /* gray-200 */ --color-input: oklch(0.90 0.005 240); --color-sidebar: oklch(0.97 0.003 240); /* gray-50 */ } /* Dark theme */ .dark { --color-background: oklch(0.10 0.01 240); --color-foreground: oklch(0.98 0.005 240); --color-card: oklch(0.13 0.01 240); --color-card-foreground: oklch(0.98 0.005 240); --color-muted: oklch(0.18 0.015 240); --color-muted-foreground: oklch(0.60 0.02 240); --color-border: oklch(0.22 0.015 240); --color-input: oklch(0.22 0.015 240); --color-sidebar: oklch(0.13 0.01 240); } ``` ### Semantic Color Mapping | Semantic Class | Dark Appearance | Light Appearance | Replaces | |---|---|---|---| | `bg-background` | `oklch(0.10)` near-black | `oklch(0.98)` near-white | `bg-gray-950`, body | | `bg-card` | `oklch(0.13)` dark gray | white | `bg-gray-900/80` | | `bg-muted` | `oklch(0.18)` medium dark | `oklch(0.96)` light gray | `bg-gray-800/50` | | `bg-sidebar` | `oklch(0.13)` dark | `oklch(0.97)` near-white | sidebar bg | | `text-foreground` | white | near-black | `text-white` | | `text-muted-foreground` | gray-400/500 | gray-500 | `text-gray-400/500` | | `border-border` | gray-800 | gray-200 | `border-gray-800` | ### What Stays the Same - **Accent colors**: sky-500, blue-600 gradients work on both themes - **Status badge colors**: `bg-red-500/20 text-red-400` etc. work on both - **Gradient buttons**: primary/destructive button gradients are fine - **Focus rings**: sky-500 focus rings work on both ## Scope ### Files to Create (2) - `src/contexts/ThemeContext.tsx` — ThemeProvider + useTheme hook ### Files to Modify (~20) - `src/index.css` — Light/dark CSS variable sets, conditional body styles - `src/App.tsx` — Wrap with ThemeProvider - `src/components/ui/Button.tsx` - `src/components/ui/Card.tsx` - `src/components/ui/Input.tsx` - `src/components/ui/Modal.tsx` - `src/components/ui/Table.tsx` - `src/components/layout/Sidebar.tsx` - `src/components/layout/Header.tsx` - `src/components/layout/MainLayout.tsx` - `src/pages/SettingsPage.tsx` — Use ThemeContext instead of local toggle - `src/pages/LoginPage.tsx` - `src/pages/SSOCallbackPage.tsx` - `src/pages/DashboardPage.tsx` - `src/pages/UserManagementPage.tsx` - `src/pages/FileManagementPage.tsx` - `src/pages/MyPage.tsx` - `src/pages/MenuManagementPage.tsx` - `src/pages/RoleManagementPage.tsx` - `src/pages/OrganizationManagementPage.tsx` ## Light Theme Visual Direction - **Clean & Professional** — white/gray backgrounds, subtle borders, soft shadows - Body: white background with very subtle blue-tinted grid pattern - Cards: white with light gray borders and subtle shadow - Sidebar: gray-50 background with clean borders - Header: white/gray-50 with bottom border - Text: gray-900 primary, gray-500 secondary - Sky-500 accent color stays the same