Theme-System¶
Phase 58 (v1.41.0) ersetzte das alte Hell/Dunkel-Paar durch ein
System aus sechs klassischen Themes auf einer einzigen
data-theme-Dimension, plus eine auto-Auswahl, die dem
Betriebssystem folgt. Phase 63 (v1.63.0) ergänzte sechs
empfohlene WCAG-AA-Presets, sodass der Picker insgesamt 12
Themes führt.
Empfohlene Presets (Phase 63 / v1.63.0)¶
Der Picker unter Einstellungen → Darstellung führt mit einem Empfohlen-Unterreiter:
- Hell:
catppuccin-latte,supabase,graphite - Dunkel:
catppuccin-mocha,soft-pop,amethyst-haze
Sie wurden aus tweakcn-Presets per
scripts/generate_preset_themes.py als vollständige
44-Token-Themes generiert, mit rechnerisch erzwungenem
WCAG AA (contrast.test.ts über alle 12 Themes). Die
klassischen sechs (light, dark, ocean, forest,
high-contrast, sepia) bleiben unverändert.
Funktionsweise¶
- Kanonische Farb-Tokens liegen in
frontend/src/styles/themes/theme-<id>.css, ein Block prodata-theme-Wert (light,dark,ocean,forest,high-contrast,sepia). Jede Datei definiert das vollständige semantische Token-Set - es gibt kein Durchfallen auf Hell. - Theme-unabhängige Tokens (Abstände, Radius, Schriften, die
Marken-Methodenpalette) und die Legacy-Aliase (
--bg,--surface,--fg,--danger, ...) liegen instyles/global.css :root. Die Aliase lösen über die kanonischen Tokens auf und folgen so automatisch dem aktiven Theme. - Die Theme-Dateien werden in
main.tsximportiert, Hell zuerst, damit das aktive Theme den Gleichstand der Spezifität gegen:rootgewinnt. frontend/src/lib/themes.tsist das Register:THEMES, die TypenThemeId/ThemeChoice,resolveTheme(choice, prefersDark)für dasauto-Mapping und die Vorschau-Farbproben.frontend/src/hooks/useTheme.tsbesitzt das angewandtedata-theme-Attribut und speichert die Wahl unteradaptive-learner.theme(migriert den altenadaptive-learner-theme-Schlüssel einmalig).index.htmlenthält ein kleines Inline-Skript, das das gespeicherte Theme vor dem ersten Paint anwendet (kein Flackern). Es spiegelt die Auflösung des Hooks; halte beide synchron.- Diagramme (Recharts) können CSS-Variablen nicht in SVG-Attributen
lesen, daher lesen
lib/chartTheme.ts+useChartThemedie berechneten Token-Werte und lesen beidata-theme-Wechsel neu.
Token-Set (von jedem Theme definiert)¶
Hintergründe (--bg-primary/secondary/surface/elevated/overlay),
Text (--fg-primary/secondary/muted/inverse), Ränder
(--border-primary/subtle/accent), interaktiv
(--interactive-bg/hover/active/disabled), Akzent
(--accent, -hover, -fg, -subtle, -rgb), Status-Paare
(--success/-bg, --error/-bg, --warning/-bg, --info/-bg),
Übungs-Feedback (--exercise-correct/-wrong/-selected/-matched),
--star, Diagrammreihen (--chart-1..6) und Schatten
(--shadow-card/-elevated/-md).
styles/themes/themes.test.ts schlägt fehl, wenn einem Theme eines
dieser Tokens fehlt oder es ein zusätzliches hat;
styles/contrast.test.ts prüft WCAG 2.1 AA über alle 12 Themes.
Die vollständige Token-Referenz steht in
Design-Token-Architektur.
Ein neues Theme hinzufügen¶
- Kopiere eine bestehende Datei, z. B.
cp theme-dark.css theme-midnight.css, und ändere den Selektor auf[data-theme="midnight"]. Behalte jedes Token - ändere nur die Werte. Keine Komponenten-Styles hier. - Registriere es in
lib/themes.ts: fügeTHEMESeinenThemeMeta-Eintrag hinzu (id, englischeslabel,familylight|dark und einswatchfür die Settings-Vorschau) und ergänze die id in derThemeId-Union. - Importiere es in
main.tsxnachtheme-light.css(die Reihenfolge zählt nur relativ zu Hell). - Erlaube es im Pre-Paint-Guard: ergänze die id im
valid-Array im Inline-<script>inindex.html. - i18n: füge
ui.themes.midnightin allen acht Katalogen unterbackend/config/i18n/*.yamlhinzu und führemake sync-i18naus. - Prüfe:
npx vitest run src/styles/themes src/styles/contrast- die Vollständigkeits- und Kontrast-Pins müssen grün bleiben (passe die Werte an, bis der Kontrast im neuen Theme AA erfüllt).
Das war's - der ThemePicker, das Pre-Paint-Skript, die Diagramme und jede Komponente übernehmen das neue Theme automatisch, weil sie alle die kanonischen Tokens lesen.
Regeln¶
- Keine fest codierten Farben in Komponenten.
styles/no-hardcoded-colors.test.tserzwingt das für.tsx-Styles (eine dokumentierte Allowlist deckt Diagramm-Resolver, dekoratives Konfetti und Daten-Farben ab). - Jedes Theme definiert jedes Token. Keine Lücken mit Vererbung von Hell - das war der F1-Audit-Fehler (undefinierte Tokens, die im Dunkelmodus helles Hex zeigten).
- Theme-Wechsel ist sofortig - ein
data-theme-Tausch, niemals ein Neuladen.