Auch verfügbar in: 🇬🇧 English

Theming-System

Inhalt

🇬🇧 English Version


ITSWEBER Play besitzt eine 6-Ebenen-Token-Architektur für Live-Theme-Bearbeitung ohne Seitenreloads. Jede visuelle Eigenschaft — Farben, Radien, Schatten, Schriften — durchläuft eine strukturierte Kette vom Rohwert bis zum gerenderten Pixel.


Die 6 Ebenen

Ebene 1: Primitive Tokens

Speicherort: packages/theme/tokens.json

Rohwerte ohne semantische Bedeutung: Farben, Schriftfamilien, Rahmenradien, Schattendefinitionen. Diese bilden das Fundament — sie werden niemals direkt in Komponenten verwendet. Komponenten referenzieren immer semantische Tokens (Ebene 2).

{
  "color": {
    "gray-900": "#0f0f0f",
    "gray-800": "#1a1a1a",
    "gray-100": "#f5f5f5",
    "brand-500": "#e05c1a",
    "brand-600": "#c44f14",
    "red-500": "#ef4444",
    "yellow-400": "#facc15"
  },
  "radius": {
    "sm": "0.25rem",
    "md": "0.5rem",
    "lg": "1rem",
    "full": "9999px"
  },
  "shadow": {
    "card": "0 2px 8px rgba(0,0,0,0.4)",
    "overlay": "0 8px 32px rgba(0,0,0,0.6)"
  },
  "font": {
    "sans": "'Inter', system-ui, sans-serif",
    "mono": "'JetBrains Mono', monospace"
  }
}

Ebene 2: Semantische Tokens

Speicherort: packages/theme/src/semantic.ts

Semantische Tokens ordnen benannte Rollen — bg, fg, brand, danger, warning — den Primitives zu. Tailwind-Klassen und Komponentenstile referenzieren ausschließlich diese Tokens, niemals rohe Hex-Werte.

Semantischer Token Rolle Zeigt auf (Dark-Standard)
--color-bg Seitenhintergrund color.gray-900
--color-surface Karten-/Panel-Hintergrund color.gray-800
--color-fg Primärer Text color.gray-100
--color-fg-muted Sekundärer Text color.gray-400
--color-brand Akzent / Interaktiv color.brand-500
--color-brand-hover Hover-Zustand color.brand-600
--color-danger Fehler, destruktive Aktionen color.red-500
--color-warning Warnungen color.yellow-400
--radius-card Karten-Eckenradius radius.md
--shadow-card Karten-Elevation shadow.card
// packages/theme/src/semantic.ts (Auszug)
export const semanticDark: SemanticTokens = {
  'color-bg':         tokens.color['gray-900'],
  'color-surface':    tokens.color['gray-800'],
  'color-fg':         tokens.color['gray-100'],
  'color-brand':      tokens.color['brand-500'],
  'color-danger':     tokens.color['red-500'],
  'radius-card':      tokens.radius['md'],
};

Ebene 3: Admin Live-Editor

Pfad: /admin/theme

Eine Colorpicker- und Schieberegler-Oberfläche ermöglicht Admins die Anpassung semantischer Tokens zur Laufzeit. Änderungen werden:

  1. In der Datenbanktabelle theme_settings.tokens_override gespeichert
  2. Per WebSocket an alle verbundenen Clients übertragen

Kein Deployment oder Neustart erforderlich. Siehe Live-Update-Mechanismus weiter unten.


Ebene 4: Preset-Themes

Eingebaute benannte Presets, die den vollständigen Satz semantischer Tokens abdecken. Im Admin-Panel per Klick umschaltbar. Eigene Presets können erstellt und in der Datenbank gespeichert werden.

Preset Beschreibung Ideal für
itsweber-dark Standard-Dark-Theme mit orangem Marken-Akzent Allgemeine Nutzung
itsweber-light Heller Hintergrund, dunkler Text, oranger Akzent Helle Umgebungen
high-contrast Maximaler Vordergrund-/Hintergrundkontrast Barrierefreiheit
retro Warme Bernsteintöne, Serif-ähnliche Überschriften Persönliche/Nischen-Instanzen

Ebene 5: Custom CSS

Freies CSS-Eingabefeld in der Admin-Sandbox. Wird nach allen Token-Ebenen angewendet und bietet volle Override-Möglichkeit. Merkmale:

  • Versioniert: Letzte 20 Revisionen werden gespeichert, jede einzeln wiederherstellbar
  • Validiert: Syntax wird vor der Anwendung geprüft; ungültiges CSS wird mit einer Fehlermeldung abgelehnt
  • Scoped: Innerhalb eines einzelnen <style>-Tags angewendet, keine Framework-Klassen erforderlich

Siehe Custom-CSS-Tipps für häufige Muster.


Ebene 6: Layout-Blöcke

Startseite und Landing-Pages setzen sich aus per Drag-and-drop sortierbaren Blöcken im Admin-Panel zusammen. Verfügbare Block-Typen:

Block Beschreibung
Hero Vollbreites Banner mit Titel, Untertitel und CTA-Button
Category-Chips Scrollbare Kategoriefilter-Zeile
Video-Grid Konfigurierbares Raster aus aktuellen/hervorgehobenen Videos
Channel-Spotlight Hervorgehobene Kanalkartenzeile
CTA-Banner Call-to-Action-Streifen mit eigenem Text und Link
Custom-HTML Freies HTML-Block (bereinigt)

Drag-and-drop-Neuordnung wird unterstützt. Änderungen treten sofort ohne Seitenreload in Kraft.


Live-Update-Mechanismus

Wenn ein Admin eine Theme-Änderung speichert, wird die Aktualisierung sofort an alle offenen Browser-Sessions weitergegeben:

Admin-Änderung → tRPC theme.update → DB-Speicherung + WebSocket-Broadcast
Frontend: ersetzt <style id="theme-vars"> mit neuen CSS-Variablen
Ergebnis: Sofortiges Update, kein React-Rerender nötig

Das Frontend lauscht auf einem dedizierten WebSocket-Kanal. Beim Empfang eines theme.updated-Events tauscht es den Inhalt des <style id="theme-vars">-Elements aus. Da CSS-Variablen kaskadieren, aktualisiert sich jedes Element, das diese Variablen verwendet, an Ort und Stelle — ohne Komponenten-State-Änderung, ohne Navigation.


Token-Mapping-Beispiel

// tokens.json (Primitives)
{
  "color": {
    "brand-500": "#e05c1a"
  }
}
// semantic.ts (Semantisches Mapping)
'color-brand': tokens.color['brand-500']
// → erzeugt: --color-brand: #e05c1a
/* Komponentenverwendung */
.btn-primary {
  background-color: var(--color-brand);
}
.btn-primary:hover {
  background-color: var(--color-brand-hover);
}

Ein Admin, der color-brand im Live-Editor auf #3b82f6 (Blau) ändert, schreibt { "color-brand": "#3b82f6" } in theme_settings.tokens_override, was zur Renderzeit über die Standard-Semantik-Ebene gemergt wird.


Verfügbare Presets

Name Beschreibung Ideal für
itsweber-dark Standard-Dark mit orangem Akzent Allgemeine Nutzung
itsweber-light Hell, klarer Kontrast, oranger Akzent Helle Umgebungen, Büros
high-contrast Maximaler Kontrast, WCAG-AA-konform Barrierefreiheit
retro Bernstein-/Erdtöne, warme Typografie Persönliche oder Nischen-Instanzen

Logo-Filter-Presets

Das Instanz-Logo unterstützt 10 CSS-Filter-Presets, angewendet über var(--logo-filter). Dies ermöglicht eine Anpassung des Logos an helle/dunkle Hintergründe ohne separate Bild-Assets.

Preset CSS-Filter-Wert Verwendungszweck
none none Standard — keine Änderung
drop-shadow drop-shadow(0 2px 4px rgba(0,0,0,0.6)) Tiefe auf dunklen Hintergründen
brightness-up brightness(1.3) Aufhellen für Dark-Themes
brightness-down brightness(0.7) Abdunkeln für Light-Themes
saturate saturate(2) Farbsättigung verstärken
desaturate saturate(0.3) Gedämpfter, monochromer Effekt
invert invert(1) Vollständige Farbumkehr
grayscale grayscale(1) Schwarz-Weiß-Logo
sepia sepia(0.8) Warmer Vintage-Ton
contrast contrast(1.5) Höherer Kontrast an Kanten

Custom-CSS-Tipps

Hintergrundfarbverlauf auf der Seite

body {
  background: linear-gradient(135deg, var(--color-bg) 0%, #1a0a00 100%);
}

Schrift-Override

:root {
  --font-sans: 'Nunito', system-ui, sans-serif;
}

Karten-Radius anpassen

:root {
  --radius-card: 1.5rem;
}

Vollbreiter Hero-Hintergrund

.hero-block {
  background-image: url('/api/assets/hero-bg.jpg');
  background-size: cover;
  background-position: center;
}

Sicherheit: Custom CSS ist auf den eigenen Origin der Instanz beschränkt. @import-Regeln, die auf externe Origins zeigen, werden entfernt. JavaScript innerhalb von <style>-Tags wird nicht ausgeführt. Ungültiges CSS wird vor dem Speichern abgelehnt.


© Benjamin Weber · ITSWEBER — play.itsweber.net · GitHub