# Design-Dokument – System-Theme-Erkennung (Light/Dark Mode)

## Übersicht

Dieses Feature integriert die bereits installierte Bibliothek `next-themes` (v0.3.0) in die bestehende React + Vite + Tailwind + shadcn/ui-Webseite, um eine automatische Erkennung des Betriebssystem-Themes (hell/dunkel) sowie manuelles Umschalten zu ermöglichen.

Aktuell definiert `:root` in `src/index.css` das dunkle Farbschema. Es existiert zwar ein `.dark`-Block, dieser wird aber nicht aktiv genutzt. Die Tailwind-Konfiguration verwendet bereits `darkMode: ["class"]`, was perfekt mit `next-themes` zusammenarbeitet.

Kernentscheidungen:
- `next-themes` wird als ThemeProvider eingesetzt (bereits als Dependency vorhanden, `attribute: "class"` für Tailwind-Kompatibilität)
- Das aktuelle `:root`-Farbschema wird zum Light Theme umstrukturiert, ein neues Dark Theme wird unter `.dark` definiert
- Ein Theme-Toggle-Button wird im Header platziert (Desktop und Mobile)
- Ein Inline-Script in `index.html` verhindert Theme-Flackern beim Laden (FOUC-Prevention)

## Architektur

```mermaid
graph TD
    A[index.html – Inline-Script] -->|Setzt class auf html| B[html-Element]
    B --> C[ThemeProvider – next-themes]
    C --> D[App.tsx]
    D --> E[Header mit ThemeToggle]
    D --> F[Alle Seiten & Komponenten]
    
    G[localStorage – theme] <-->|Lesen/Schreiben| C
    H[prefers-color-scheme] -->|System-Erkennung| C
    
    I[index.css – :root Light] --> F
    J[index.css – .dark Dark] --> F
```

### Datenfluss

1. Beim Laden der Seite liest ein Inline-Script in `index.html` den `localStorage`-Wert (`theme`) aus. Falls keiner vorhanden ist, wird `prefers-color-scheme` geprüft. Die entsprechende CSS-Klasse (`dark` oder keine) wird auf `<html>` gesetzt – noch bevor React rendert.
2. `ThemeProvider` aus `next-themes` übernimmt die Verwaltung im React-Baum. Er synchronisiert den Zustand mit `localStorage` und reagiert auf Änderungen der System-Einstellung via `matchMedia`-Listener.
3. Der `ThemeToggle`-Button im Header nutzt den `useTheme()`-Hook, um zwischen `light`, `dark` und `system` zu wechseln.
4. Alle Komponenten nutzen weiterhin `hsl(var(--...))` CSS-Variablen – der Wechsel erfolgt rein über CSS-Klassen.

## Komponenten und Schnittstellen

### 1. ThemeProvider-Wrapper (`src/components/ThemeProvider.tsx`)

Dünner Wrapper um `ThemeProvider` aus `next-themes`:

```typescript
interface ThemeProviderProps {
  children: React.ReactNode;
  defaultTheme?: string;       // Standard: "system"
  storageKey?: string;          // Standard: "theme"
  enableSystem?: boolean;       // Standard: true
  disableTransitionOnChange?: boolean; // Standard: false
}
```

Konfiguration:
- `attribute="class"` – Tailwind-kompatibel
- `defaultTheme="system"` – Erstbesucher erhalten System-Theme
- `enableSystem={true}` – Automatische Erkennung aktiv
- `storageKey="theme"` – localStorage-Schlüssel

Wird in `App.tsx` als äußerster Wrapper um den gesamten Komponentenbaum eingebunden.

### 2. ThemeToggle-Komponente (`src/components/ThemeToggle.tsx`)

Button-Komponente mit Dropdown-Menü (shadcn/ui `DropdownMenu`):

```typescript
interface ThemeToggleProps {
  className?: string;
}
```

Funktionalität:
- Zeigt ein Icon basierend auf dem aktuellen Theme (Sun, Moon, Monitor aus `lucide-react`)
- Dropdown mit drei Optionen: „Hell", „Dunkel", „System"
- Nutzt `useTheme()` Hook für `theme`, `setTheme`, `resolvedTheme`
- `aria-label` beschreibt den aktuellen Zustand
- Tastatur-bedienbar (Tab, Enter/Space)
- Mounted-Check verhindert Hydration-Mismatch (Icon wird erst nach Mount gerendert)

### 3. Anpassungen am Header (`src/components/Header.tsx`)

- ThemeToggle wird im Desktop-NavBody neben dem „Kontakt"-Button eingefügt
- ThemeToggle wird im Mobile-Nav-Header neben dem Hamburger-Menü eingefügt
- Positionierung: rechts, vor dem Kontakt-Button (Desktop) bzw. links vom Toggle-Icon (Mobile)

### 4. Inline-Script in `index.html`

Ein `<script>`-Block im `<head>`, der vor dem React-Bundle ausgeführt wird:

```javascript
// Liest localStorage("theme") aus
// Falls "system" oder nicht vorhanden: prüft prefers-color-scheme
// Setzt class="dark" auf <html> wenn Dark Mode aktiv
```

Dieses Script verhindert das sichtbare Flackern (FOUC) beim Seitenaufruf.

### 5. CSS-Variablen-Umstrukturierung (`src/index.css`)

- `:root` → Light Theme (helle Hintergründe, dunkle Texte)
- `.dark` → Dark Theme (das aktuelle `:root`-Farbschema wird hierhin verschoben)
- Alle bestehenden Komponentenstile (`.glass-nav`, `.card-minimal`, `.project-card`, `.btn`, etc.) werden auf Theme-Kompatibilität geprüft und ggf. angepasst
- Hardcodierte Farbwerte in Komponentenstilen werden durch CSS-Variablen ersetzt

### Komponentendiagramm

```mermaid
graph LR
    subgraph App.tsx
        TP[ThemeProvider]
        TP --> Router
    end
    
    subgraph Header
        TT[ThemeToggle]
        TT -->|useTheme| TP
    end
    
    subgraph CSS
        LT[":root – Light Theme"]
        DT[".dark – Dark Theme"]
    end
    
    TP -->|class auf html| CSS
```

## Datenmodelle

### Theme-Zustand

```typescript
type ThemeValue = "light" | "dark" | "system";

// Von next-themes bereitgestellt via useTheme()
interface ThemeState {
  theme: ThemeValue;           // Gewählte Präferenz ("light" | "dark" | "system")
  resolvedTheme: "light" | "dark"; // Tatsächlich angewendetes Theme
  setTheme: (theme: ThemeValue) => void;
  systemTheme: "light" | "dark";   // Aktuelle OS-Einstellung
}
```

### localStorage-Schema

| Schlüssel | Wert | Beschreibung |
|-----------|------|--------------|
| `theme` | `"light"` \| `"dark"` \| `"system"` | Gespeicherte Benutzerpräferenz |

### CSS-Custom-Properties (Light Theme – neu zu erstellen)

```css
:root {
  --background: 0 0% 100%;        /* Weiß */
  --foreground: 0 0% 9%;          /* Fast Schwarz */
  --card: 0 0% 98%;               /* Sehr helles Grau */
  --card-foreground: 0 0% 9%;
  --primary: 198 93% 42%;         /* Cyan-Blau (konsistent) */
  --primary-foreground: 0 0% 98%;
  --secondary: 0 0% 94%;          /* Helles Grau */
  --secondary-foreground: 0 0% 9%;
  --muted: 0 0% 94%;
  --muted-foreground: 0 0% 40%;
  --accent: 0 0% 94%;
  --accent-foreground: 0 0% 9%;
  --border: 0 0% 88%;
  --input: 0 0% 88%;
  --ring: 198 93% 42%;
  --destructive: 0 62% 50%;
  --destructive-foreground: 0 0% 98%;
  /* ... weitere Token analog */
}
```

### CSS-Custom-Properties (Dark Theme – bestehendes Schema)

```css
.dark {
  --background: 0 0% 0%;          /* Schwarz (aktuelles :root) */
  --foreground: 0 0% 92%;
  --card: 0 0% 6%;
  --card-foreground: 0 0% 92%;
  --primary: 180 40% 50%;
  --primary-foreground: 0 0% 3%;
  /* ... restliche aktuelle :root-Werte */
}
```


## Korrektheitseigenschaften (Correctness Properties)

*Eine Korrektheitseigenschaft ist ein Merkmal oder Verhalten, das über alle gültigen Ausführungen eines Systems hinweg gelten soll – im Wesentlichen eine formale Aussage darüber, was das System tun soll. Eigenschaften bilden die Brücke zwischen menschenlesbaren Spezifikationen und maschinell überprüfbaren Korrektheitsgarantien.*

### Property 1: System-Theme-Auflösung

*Für jede* beliebige System-Theme-Einstellung (hell oder dunkel) gilt: Wenn die Theme-Präferenz auf „system" steht (oder keine Präferenz gespeichert ist), dann soll das aufgelöste Theme (`resolvedTheme`) dem aktuellen System-Theme entsprechen.

**Validates: Requirements 1.1, 1.2, 1.3, 2.5**

### Property 2: Explizite Theme-Wahl überschreibt System

*Für jede* beliebige System-Theme-Einstellung und *für jede* explizite Theme-Wahl (`"light"` oder `"dark"`) gilt: Das aufgelöste Theme (`resolvedTheme`) soll immer der expliziten Wahl entsprechen, unabhängig vom System-Theme.

**Validates: Requirements 2.3, 2.4**

### Property 3: Persistenz-Round-Trip

*Für jeden* gültigen Theme-Wert (`"light"`, `"dark"`, `"system"`) gilt: Wenn der Wert über `setTheme()` gesetzt wird, dann soll der im `localStorage` gespeicherte Wert identisch sein, und beim erneuten Laden soll dieser gespeicherte Wert korrekt als aktive Präferenz wiederhergestellt werden.

**Validates: Requirements 3.1, 3.2**

## Fehlerbehandlung

| Szenario | Verhalten |
|----------|-----------|
| `localStorage` nicht verfügbar (blockiert, privater Modus) | ThemeProvider fällt auf System-Theme-Erkennung zurück. Kein Fehler wird geworfen. Die Seite funktioniert normal, nur die Persistenz entfällt. |
| `prefers-color-scheme` nicht unterstützt (ältere Browser) | `next-themes` fällt auf den `defaultTheme` zurück (`"system"` → wird als `"light"` aufgelöst). |
| Ungültiger Wert im `localStorage` | `next-themes` ignoriert ungültige Werte und fällt auf `defaultTheme` zurück. |
| JavaScript deaktiviert | Das Inline-Script in `index.html` wird nicht ausgeführt. Die Seite zeigt das Standard-CSS (Light Theme in `:root`). Kein Theme-Toggle verfügbar, aber die Seite bleibt nutzbar. |
| Hydration-Mismatch (SSR/SSG) | Nicht relevant für Vite-SPA. Der Mounted-Check im ThemeToggle verhindert trotzdem Icon-Flackern. |

## Teststrategie

### Property-Based Tests (fast-check)

Bibliothek: `fast-check` (für TypeScript/Vitest)

Konfiguration:
- Minimum 100 Iterationen pro Property-Test
- Jeder Test referenziert die zugehörige Design-Property
- Tag-Format: **Feature: system-theme-detection, Property {number}: {property_text}**

Die drei Korrektheitseigenschaften werden als Property-Based Tests implementiert:

1. **Property 1** – Theme-Auflösungslogik bei „system"-Präferenz: Generiere zufällige System-Theme-Werte, setze Präferenz auf „system", prüfe `resolvedTheme === systemTheme`.
2. **Property 2** – Explizite Wahl überschreibt System: Generiere zufällige Kombinationen aus System-Theme und expliziter Wahl, prüfe `resolvedTheme === expliziteWahl`.
3. **Property 3** – Persistenz-Round-Trip: Generiere zufällige gültige Theme-Werte, speichere via `setTheme()`, lese aus `localStorage`, prüfe Gleichheit. Simuliere Neustart, prüfe ob Wert wiederhergestellt wird.

### Unit Tests (Vitest + React Testing Library)

Ergänzend zu den Property-Tests werden folgende Example-basierte Tests geschrieben:

- **ThemeToggle-Rendering**: Button ist im Header vorhanden (Req. 2.1)
- **Dropdown-Optionen**: Drei Optionen (Hell, Dunkel, System) sind vorhanden (Req. 2.2)
- **Icon-Zuordnung**: Korrektes Icon für jeden Theme-Zustand (Req. 2.6)
- **CSS-Token-Vollständigkeit**: Alle erforderlichen Custom Properties sind im Light Theme definiert (Req. 4.1)
- **Kontrastverhältnisse**: Schlüssel-Farbpaare erfüllen WCAG-AA (Req. 4.2)
- **Tastatur-Bedienbarkeit**: Tab-Navigation und Enter/Space funktionieren (Req. 6.1)
- **aria-label**: Beschreibt den aktuellen Zustand korrekt (Req. 6.2)
- **aria-live**: Zustandswechsel wird kommuniziert (Req. 6.3)

### Edge-Case Tests

- **localStorage blockiert**: Fallback auf System-Theme ohne Fehler (Req. 3.3)

### Integrationstests

- **matchMedia-Listener**: Simuliere OS-Theme-Wechsel, prüfe ob resolvedTheme sich aktualisiert (Req. 1.4)

### Nicht automatisiert testbar

- Visuell korrekte Darstellung der Komponentenstile im Light Theme (Req. 4.3) → Manuelle visuelle Prüfung
- Kein sichtbares Flackern beim Laden (Req. 5.2) → Manuelle visuelle Prüfung
