Auch verfügbar in: EN English

Theming-System

Inhalt

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": {
    "neutral": { "50": "#f6f8fa", "…": "…", "900": "#0e1116" },
    "teal": { "400": "#3ba7a7", "500": "#3ba7a7", "600": "#2d8a8a", "700": "#236e6e" },
    "red": { "500": "#d9534f", "600": "#b43935" },
    "amber": { "500": "#f0b429", "600": "#cb8e14" },
    "green": { "500": "#3fb950", "600": "#2ea043" }
  },
  "radius": {
    "sm": "6px",
    "md": "10px",
    "lg": "16px",
    "full": "9999px"
  },
  "shadow": {
    "card": "0 4px 20px rgba(0,0,0,.35)",
    "lg": "0 12px 40px rgba(0,0,0,.45)"
  },
  "font": {
    "sans": "'Geist', 'Geist Variable', system-ui, -apple-system, 'Segoe UI', sans-serif",
    "mono": "'Geist Mono', 'Geist Mono Variable', ui-monospace, 'SF Mono', monospace"
  }
}

Ebene 2: Semantische Tokens

Speicherort: packages/theme/styles/semantic.css (für JavaScript gespiegelt in 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-background Seitenhintergrund --color-neutral-900
--color-surface Karten-/Panel-Hintergrund --color-neutral-800
--color-foreground Primärer Text --color-neutral-100
--color-muted Sekundärer Text --color-neutral-300
--color-brand Akzent / Interaktiv --color-teal-500
--color-brand-hover Hover-Zustand --color-teal-400
--color-danger Fehler, destruktive Aktionen --color-red-500
--color-warning Warnungen --color-amber-500
--shadow-card Karten-Elevation fester Wert in semantic.css
// packages/theme/src/semantic.ts (Auszug)
export const semantic = {
  bg: { base: "var(--color-neutral-900)", surface: "var(--color-neutral-800)" },
  fg: { primary: "var(--color-neutral-50)", muted: "var(--color-neutral-400)" },
  brand: { default: "var(--color-teal-500)", hover: "var(--color-teal-600)" },
  intent: { danger: "var(--color-red-500)", warning: "var(--color-amber-500)" },
} as const;

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. Im Prisma-Modell ThemeSettings, Feld tokensOverride, gespeichert
  2. Über Redis Pub/Sub und Server-Sent Events (/api/theme/events) an alle offenen Tabs übertragen

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


Ebene 4: Preset-Themes

Eingebaute benannte Presets aus packages/theme/presets/. Im Admin-Panel per Klick umschaltbar. Eigene Presets werden nicht in der Datenbank verwaltet, der aktuelle Stand lässt sich aber als JSON exportieren und wieder importieren.

Preset Beschreibung Ideal für
itsweber-dark Standard-Dark-Theme mit Teal-Akzent Allgemeine Nutzung
itsweber-light Helles Gegenstück zu itsweber-dark Helle Umgebungen
itsweber-brand Dunkles Navy mit grünem Akzent nach itsweber.de Offizielle Instanz
high-contrast Maximaler Kontrast bei Sehschwäche Barrierefreiheit
retro 80er-Synthwave mit Magenta und Cyan auf Violett 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
  • Geprüft: Vor dem Speichern wird auf verbotene Konstrukte geprüft (@import, url(javascript:…), expression(), </style>, <script, behavior:) und die Größe auf 100 KB begrenzt. Eine Syntaxprüfung findet nicht statt
  • 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

Die Startseite setzt sich aus per Drag-and-drop sortierbaren Blöcken zusammen, gepflegt unter /admin/page-blocks. 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
Shorts-Row Zeile mit Kurzvideos
Channel-Row Zeile mit Kanalkarten
CTA-Banner Call-to-Action-Streifen mit eigenem Text und Link
Community-Row Zeile mit Community-Beiträgen

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 + Redis-Publish → SSE /api/theme/events
Frontend: ersetzt <style id="theme-vars"> mit neuen CSS-Variablen
Ergebnis: Sofortiges Update, kein React-Rerender nötig

Das Frontend hält eine EventSource-Verbindung zu /api/theme/events. Beim Empfang eines theme:updated-Events lädt es das aktuelle Theme per tRPC nach und tauscht den Inhalt von <style id="theme-vars"> und <style id="theme-custom"> 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": {
    "teal": { "500": "#3ba7a7" }
  }
}
/* styles/semantic.css (Semantisches Mapping) */
--color-brand: var(--color-teal-500);
/* aus tokens.json entsteht: --color-teal-500: #3ba7a7 */
/* Komponentenverwendung */
.btn-primary {
  background-color: var(--color-brand);
}
.btn-primary:hover {
  background-color: var(--color-brand-hover);
}

Overrides adressieren die Primitives in Punktnotation. Ein Admin, der im Live-Editor color.teal.500 auf #3b82f6 (Blau) setzt, schreibt { "color.teal.500": "#3b82f6" } in ThemeSettings.tokensOverride. Daraus wird --color-teal-500: #3b82f6 in <style id="theme-vars">, und --color-brand übernimmt den Wert über die Kaskade.


Verfügbare Presets

Name Beschreibung Ideal für
itsweber-dark Standard-Dark mit Teal-Akzent Allgemeine Nutzung
itsweber-light Helles Gegenstück zu itsweber-dark Helle Umgebungen, Büros
itsweber-brand Dunkles Navy mit grünem Akzent nach itsweber.de Offizielle Instanz
high-contrast Maximaler Kontrast bei Sehschwäche Barrierefreiheit
retro 80er-Synthwave mit Magenta und Cyan auf Violett 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 Keine Änderung
glow drop-shadow(0 0 12px var(--color-teal-500)) Leuchten in Akzentfarbe
softGlow drop-shadow(0 0 6px rgba(59,167,167,.6)) Dezentes Leuchten
duotone grayscale(1) brightness(1.1) sepia(.3) hue-rotate(140deg) saturate(3) Einfarbig getönt
shadow drop-shadow(0 4px 8px rgba(0,0,0,.5)) Schatten für mehr Tiefe
brightness0 brightness(0) Komplett schwarz
invert invert(1) Farben umkehren
invertGlow invert(1) drop-shadow(0 0 12px var(--color-teal-500)) Umgekehrt mit Leuchten
mono grayscale(1) contrast(1.2) brightness(1.05) Graustufen
onlyWhite brightness(0) invert(1) Komplett weiß

Custom-CSS-Tipps

Hintergrundfarbverlauf auf der Seite

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

Schrift-Override

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

Radius-Stufe anpassen

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

Vollbreiter Hero-Hintergrund

/* Selektor an das echte Markup anpassen, eine Klasse .hero-block setzt der Code nicht */
.hero-block {
  background-image: url('/s3/play-assets/hero-bg.jpg');
  background-size: cover;
  background-position: center;
}

Sicherheit: Custom CSS mit @import (egal wohin), url(javascript:…), url(data:text/html…), expression(), behavior:, <script oder </style> wird beim Speichern mit Fehlermeldung abgelehnt, nicht stillschweigend bereinigt. Eine Syntaxprüfung gibt es nicht, fehlerhaftes CSS wird gespeichert und wirkt dann einfach nicht.