Zen-Garden-Contract
Gleiches HTML, anderes Aussehen: Die Admin-GUI (Settings > Themes, customCss plus
Token-Variablen) steuert das Frontend-Design, ohne Templates oder Svelte-Komponenten
anzufassen. Vorbild: CSS Zen Garden und die im Blog
The CSS Zen Garden dream, finally shipped
beschriebene Praxis - Design-Token und natives CSS statt Preprozessor-Hacks.
Pipeline
Section titled “Pipeline”- Theme liegt in der DB (
themes:tokens.cssVars+customCss). frontend/src/lib/server/themeCss.tsbaut daraus SSR-CSS (:root-Variablen plus sanitisiertes Custom-CSS) -buildThemeCss()/sanitizeCustomCss().frontend/src/routes/+layout.server.ts(fetchThemeState) loest das aktive Theme auf:theme_preset-Cookie (nur wenn aktiv) > Instanz-Default. Antwort alsthemeCss-String im Load-Data, TTL-cached (60 s).frontend/src/routes/+layout.svelteinjiziert<style data-theme-ssr>im<svelte:head>- wirksam ab dem ersten HTML-Byte, kein FOUC.- Der Client (
themePreset.ts) uebernimmt nur noch Nutzerwechsel perdocumentElementund Cookie; SSR-Werte werden nicht ueberschrieben, solange kein Wechsel passiert.
Kombination: Demo-Alias, Domain-Config, Themes
Section titled “Kombination: Demo-Alias, Domain-Config, Themes”Drei Admin-Bereiche wirken auf dasselbe SSR-Theme zusammen:
| Bereich | Admin-Pfad | Wirkung |
|---|---|---|
| Themes | Settings > Themes | Liefert Token + Custom-CSS; Vorschau-Link ?preview-theme=<slug> (nie persistiert) |
| Demo-Alias | Content > Demo-Aliasse | ?demo=<slug>; optionaler Params-Key "theme" mit Theme-Slug |
| Domain-Config | Content > Domain-Konfigurationen | Feld Theme pinnt einen Theme-Slug je Domain; leer = Instanz-Default |
Aufloesungsreihenfolge (resolveThemeSlug() in themeCss.ts):
?preview-theme= > Demo-Alias-theme > theme_preset-Cookie (Nutzerwahl) >
Domain-themeSlug > Instanz-Default.
Begruendung: Explizite Vorschau gewinnt immer; geteilte Sales-Demos (?demo=)
sehen bei allen Empfaengern identisch aus und schlagen deshalb ein ggf. veraltetes
Cookie; die bewusste Nutzerwahl schlaegt den Domain-Default. Unbekannte oder
inaktive Slugs fallen jeweils auf die naechste Stufe zurueck - nie auf einen
Fehler und nie auf Flackern. Die Primarfarben-Overlays (primary aus Alias oder
Domain) werden weiterhin als CSS-Variablen ueber das Theme gelegt.
Slots: Inhalt plus Theme-Design
Section titled “Slots: Inhalt plus Theme-Design”Layout-Slots (Admin: Settings > Layout-Slots) liefern den Inhalt, Themes das Design - dieselbe Zen-Garden-Trennung auf Komponentenebene:
| Slot | Position | Komponente | Config (Inhalt) | Design (Theme-CSS) |
|---|---|---|---|---|
global.banner |
unter der Topbar, jede Seite | core.banner |
text, href, variant |
[data-slot="global.banner"] |
content.before |
über dem Seiteninhalt | core.banner |
wie oben | [data-slot="content.before"] |
content.after |
unter dem Seiteninhalt | core.banner |
wie oben | [data-slot="content.after"] |
core.banner rendert text (Pflicht, leer = kein Banner), optional als Link
(href), in primary- oder muted-Variante. Inaktive Slots liefern die
Public-API nicht aus und rendern nichts - der leere [data-slot]-Hook mit
data-empty="true" bleibt als CSS-Anker bestehen. Slots laden clientseitig
nach (loadLayoutSlots()), das Theme-CSS gilt SSR-first: Inhalt erscheint
progressiv, Design sofort.
Stabile Selektoren (Contract)
Section titled “Stabile Selektoren (Contract)”Admin-CSS darf sich nur auf diese Hooks verlassen. Tailwind-Klassen, Tag-Namen und Komponenten-Interna sind keine API und koennen sich aendern.
| Selektor | Ort |
|---|---|
[data-region="topbar"] |
TopBar.svelte (header#topbar) |
[data-region="main"] |
+layout.svelte (main#app-main) |
[data-region="content"] |
+layout.svelte (Inhalts-Spalte; Flex-Container fuer Sektions-order) |
[data-region="footer"] |
Footer.svelte (footer) |
[data-slot="global.banner"], [data-slot="content.before"], [data-slot="content.after"] |
LayoutSlotHost.svelte (Wrapper mit display: contents, leer mit data-empty="true") |
[data-section="hero"] [data-hero="cta-primary"], [data-hero="cta-secondary"], [data-hero="visual"], [data-hero="content"] |
+page.svelte (Hero-CTAs, Visual-Rahmen, Textcontainer) |
[data-section="news"] [data-news="grid"] |
+page.svelte (News-Kartenraster) |
[data-region="topbar"] [data-topbar="brand"], [data-topbar="search"], [data-topbar="actions"] |
TopBar.svelte (Topbar-Bereiche; Flex-order erlaubt Umstellen) |
[data-region="catalog"] [data-catalog="header"], [data-catalog="filters"], [data-catalog="grid"] |
products/+page.svelte (Katalogtitel, Filterleiste, Produktraster) |
[data-card="media"], [data-card="body"] |
ProductCard.svelte (Kartenbild, Kartentext; z.B. horizontale Listen-Zeilen) |
[data-region="footer"] [data-footer="columns"], [data-footer="brand"], [data-footer="heading"] |
Footer.svelte (Spaltenraster, Markenspalte, Ueberschriften) |
[data-testid^="product-card-"] |
ProductCard.svelte (Produktkarten, stabiler Test-Hook) |
[data-section="hero"], [data-section="featured"], [data-section="top-selling"], [data-section="news"], [data-section="products"] |
+page.svelte (Startseiten-Sektionen) |
:root CSS-Variablen (--primary, --accent, --radius, --font-sans, …) |
layout.css Token-Layer + DB-cssVars |
Regeln:
- Contract-Attribute nie entfernen oder umbenennen - nur ergaenzen.
- Neues Admin-CSS nutzt Contract-Selektoren (Snippets im Theme-Dialog tun das bereits).
- Checkout-, Preis- und Pflichtseiten (Impressum, Datenschutz, Widerruf) duerfen per CSS nicht versteckt oder unbedienbar gemacht werden (ADR 005 D7).
Sanitierung
Section titled “Sanitierung”sanitizeCustomCss() entfernt: </style-Breakouts, externe @import-Regeln
(http:/url(), javascript:-, expression(- und -moz-binding-Vektoren.
Laengenlimit 50.000 Zeichen (Backend-Validierung identisch). CSP erlaubt
style-src 'self' 'unsafe-inline' - Inline-<style> aus SSR ist vorgesehen.
Showcase-Galerie (example3)
Section titled “Showcase-Galerie (example3)”Drei Referenz-Themes, gleiches HTML, kein Svelte-Eingriff. Seeds unter
infrastructure/seeds/theme-*.yaml, der Slot-Inhalt unter infrastructure/seeds/layout-slot-showcase.yaml,
Vorschau per ?preview-theme=<slug>:
| Theme | Slug | Charakter |
|---|---|---|
| Aurora Showcase | aurora-showcase |
Weiche Aurora-Gradients, Glas-Topbar, Ink-Footer |
| Beton Brutal | beton-brutal |
Neo-Brutalismus, per CSS-order umsortierte Sektionen (Shop zuerst), fette Typo, Graustufen-Bilder, CSS-Counter |
| Feuilleton | feuilleton |
Zeitungs-Layout: Serifen, Katalog als einspaltiges Verzeichnis mit Sticky-Filtern, horizontale Produktzeilen, News zuerst, Topbar umgestellt |
Offene Stufen
Section titled “Offene Stufen”- Pro-Domain-Theme (
DomainConfig.theme_slug) ist implementiert. Als naechste Pipeline-Stufe folgen weitere Layout-Tokens (Spacing, Container, Header-Hoehe, Dichte). - Sektions-Reihenfolge und Slot-Belegung sind bereits Admin-datengetrieben
(
homeSections,LayoutSlot); der Contract macht sie zusaetzlich CSS-adressierbar.