# Implementation Plan: System-Theme-Erkennung (Light/Dark Mode) ## Overview Integrate `next-themes` into the existing React + Vite + Tailwind + shadcn/ui app to enable automatic OS theme detection, manual theme switching (light/dark/system), and persistent user preference. The CSS variables in `src/index.css` will be restructured so `:root` defines the light theme and `.dark` defines the dark theme. A ThemeToggle component will be added to the Header, and an inline script in `index.html` will prevent FOUC. ## Tasks - [x] 1. Restructure CSS variables and set up testing dependencies - [x] 1.1 Restructure CSS custom properties in `src/index.css` - Move the current `:root` dark color values into the `.dark` block - Define new light theme values in `:root` (white backgrounds, dark text, matching design spec) - Ensure all design tokens are covered: background, foreground, card, primary, secondary, muted, accent, border, input, ring, destructive, popover, sidebar, chart-1 through chart-5 - Update hardcoded color values in component styles (`.glass-nav`, `.card-minimal`, `.project-card`, `.btn`, `.text-gradient`, etc.) to use CSS variables where possible - Add dark-mode-aware variants for component styles that use hardcoded HSL values - _Requirements: 4.1, 4.2, 4.3, 4.4_ - [x] 1.2 Install `fast-check` as a dev dependency - Run `npm install --save-dev fast-check` - _Requirements: (testing infrastructure)_ - [x] 2. Create ThemeProvider wrapper and integrate into App - [x] 2.1 Create `src/components/ThemeProvider.tsx` - Export a thin wrapper around `ThemeProvider` from `next-themes` - Configure with `attribute="class"`, `defaultTheme="system"`, `enableSystem={true}`, `storageKey="theme"` - Accept `children` and optional override props - _Requirements: 1.1, 1.2, 1.3, 3.1, 3.2_ - [x] 2.2 Wrap the App component tree with ThemeProvider in `src/App.tsx` - Import ThemeProvider and wrap it as the outermost provider around the existing component tree - _Requirements: 1.1, 2.3, 2.4, 2.5_ - [ ]* 2.3 Write property test: System-Theme-Auflösung (Property 1) - **Property 1: System-Theme-Auflösung** - Generate arbitrary system theme values (light/dark), set preference to "system", verify `resolvedTheme === systemTheme` - Use `fast-check` with minimum 100 iterations - **Validates: Requirements 1.1, 1.2, 1.3, 2.5** - [ ]* 2.4 Write property test: Explizite Theme-Wahl überschreibt System (Property 2) - **Property 2: Explizite Theme-Wahl überschreibt System** - Generate arbitrary combinations of system theme and explicit choice ("light"/"dark"), verify `resolvedTheme === expliziteWahl` - Use `fast-check` with minimum 100 iterations - **Validates: Requirements 2.3, 2.4** - [ ]* 2.5 Write property test: Persistenz-Round-Trip (Property 3) - **Property 3: Persistenz-Round-Trip** - Generate arbitrary valid theme values ("light", "dark", "system"), set via `setTheme()`, read from `localStorage`, verify equality and correct restoration on simulated reload - Use `fast-check` with minimum 100 iterations - **Validates: Requirements 3.1, 3.2** - [x] 3. Checkpoint - Ensure all tests pass, ask the user if questions arise. - [x] 4. Create ThemeToggle component - [x] 4.1 Create `src/components/ThemeToggle.tsx` - Build a button component using shadcn/ui `DropdownMenu` and `Button` - Use `useTheme()` hook from `next-themes` for `theme`, `setTheme`, `resolvedTheme` - Display Sun icon (lucide-react) for light, Moon for dark, Monitor for system - Implement mounted-state check to prevent hydration mismatch on icon rendering - Dropdown offers three options: "Hell" (light), "Dunkel" (dark), "System" (system) - Add `aria-label` describing current state (e.g., "Theme wechseln, aktuell: Dunkel") - Ensure keyboard navigability (Tab, Enter/Space) - Accept optional `className` prop for positioning flexibility - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 6.1, 6.2, 6.3_ - [ ]* 4.2 Write unit tests for ThemeToggle - Test that the toggle button renders in the DOM - Test that dropdown shows three options (Hell, Dunkel, System) - Test correct icon rendering per theme state - Test `aria-label` contains current theme state - Test keyboard interaction (focus, Enter/Space opens dropdown) - _Requirements: 2.1, 2.2, 2.6, 6.1, 6.2, 6.3_ - [x] 5. Integrate ThemeToggle into Header - [x] 5.1 Add ThemeToggle to desktop navigation in `src/components/Header.tsx` - Import ThemeToggle and place it in the `NavBody` actions area, before the "Kontakt" button - _Requirements: 2.1_ - [x] 5.2 Add ThemeToggle to mobile navigation in `src/components/Header.tsx` - Place ThemeToggle in the `MobileNavHeader`, positioned next to the hamburger toggle icon - _Requirements: 2.1_ - [ ]* 5.3 Write unit tests for Header ThemeToggle integration - Test that ThemeToggle is present in the rendered Header component - _Requirements: 2.1_ - [x] 6. Add FOUC-prevention inline script to `index.html` - [x] 6.1 Add inline `