Design-Tokens
Das Frontend-Design steuert ein LESS-Token-System mit zentralen Variablen in custom.less. Tokens werden sowohl als LESS-Variablen (@colorPrimary) als auch als CSS-Custom-Properties (--color-primary) bereitgestellt — Admin-UI unter /admin/design erlaubt Live-Editing ohne Rebuild. Diese Seite zeigt alle Tokens, das Mapping zu CSS-Variablen und die Cache-Integration.
Token-Quelle
Die zentrale Datei liegt nicht im Theme, sondern global im CMS:
_public/src/css/frontend/custom.lessSie wird beim Frontend-CSS-Build von allen Themes gelesen und als Bestandteil des Bundles kompiliert. Redakteure koennen den Inhalt ohne Deploy direkt im Admin unter /admin/design editieren — die Aenderungen landen dann in der DB (website.custom_less) und werden beim naechsten Cache-Rebuild aus der DB gelesen.
Farben (17 Tokens)
| LESS-Variable | CSS-Variable | Default | Zweck |
|---|---|---|---|
@colorPrimary | --color-primary | #0368DB | Buttons, Links, Akzente |
@colorPrimaryHover | --color-primary-hover | darken(#0368DB, 8%) | Button-Hover |
@colorDanger | --color-danger | #D40D12 | Fehler, Loeschen |
@colorSuccess | --color-success | #45BF55 | Erfolg |
@colorWarning | --color-warning | #FFC144 | Warnung |
@colorText | --color-text | #333333 | Haupt-Textfarbe |
@colorTextLight | --color-text-light | #8A92A6 | Helle Textfarbe, Borders, Inputs |
@colorWhite | --color-white | #ffffff | Hintergruende |
@colorBackground | --color-background | #ffffff | Seiten-Hintergrund |
@colorBorder | --color-border | #8A92A6 | Input-Borders, Trennlinien |
@colorScrollbar | --color-scrollbar | #A3A3A5 | Scrollbar |
@colorOverlay | --color-overlay | rgba(0, 0, 0, 0.8) | Loader/Modal-Overlay |
@colorMobileMenu | --color-mobile-menu | #014034 | Mobile-Menu-Hintergrund |
@colorCookieAccent | --color-cookie-accent | #11327a | Cookie-Consent-Akzent |
@colorCookieText | --color-cookie-text | #58585a | Cookie-Consent-Text |
@colorLink | --color-link | #337ab7 | Alternative Link-Farbe |
@colorError | --color-error | #ff0000 | Error-Border (Inputs) |
Typografie (8 Tokens)
| LESS-Variable | CSS-Variable | Default | Zweck |
|---|---|---|---|
@fontBody | --font-body | 'Inter', sans-serif | Haupt-Schrift |
@fontIcon | --font-icon | 'Font Awesome 6 Pro' | Icon-Font |
@fontSizeBase | --font-size-base | 14px | Standard-Schriftgroesse |
@fontSizeSmall | --font-size-small | 12px | Kleine Schrift |
@fontWeightLight | --font-weight-light | 300 | Duenne Schrift |
@fontWeightRegular | --font-weight-regular | 400 | Normal |
@fontWeightMedium | --font-weight-medium | 500 | Mittel/Strong |
@lineHeightBase | --line-height-base | 1.429em | Zeilenabstand |
Spacing (7 Tokens)
| LESS-Variable | CSS-Variable | Default | Zweck |
|---|---|---|---|
@spacingBase | --spacing-base | 8px | Basis-Einheit |
@spacingSmall | --spacing-small | 10px | Klein |
@spacingMedium | --spacing-medium | 20px | Mittel |
@spacingLarge | --spacing-large | 30px | Gross |
@spacingXLarge | --spacing-xlarge | 40px | Extra-gross |
@spacingSection | --spacing-section | 40px | .ns-row margin-bottom |
@spacingForm | --spacing-form | 30px | .form-group margin-bottom |
Layout (5 Tokens)
| LESS-Variable | CSS-Variable | Default | Zweck |
|---|---|---|---|
@radiusBase | --radius-base | 4px | Standard-Border-Radius |
@radiusRound | --radius-round | 25px | Pill-Shape (runde Buttons) |
@inputHeight | --input-height | 44px | Hoehe von <input>/<select> |
@buttonLineHeight | --button-line-height | 44px | Line-Height fuer .btn |
@scrollbarWidth | --scrollbar-width | 6px | Scrollbar-Breite |
Breakpoints (2 Tokens)
| LESS-Variable | Default | Verwendung |
|---|---|---|
@breakpointDesktop | 991px | Tablet-zu-Mobile-Breakpoint fuer Navigation, Layout |
@breakpointMobile | 768px | Klassischer Mobile-Cut |
Beide Breakpoints sind nur als LESS-Variablen verfuegbar — CSS-Variables werden nicht generiert (Media-Queries akzeptieren keine CSS-Variables im Selektor).
Pagebuilder nutzt eigene Breakpoints
Die hier gelisteten Tokens (991px/768px) gelten fuer das Frontend-Theme-Layout (Navigation, Mobile-Menu). Das Pagebuilder-Responsive-System (Column-Widths, Spacing, Flex) nutzt einen eigenen festen Satz: 1200px / 992px / 768px fuer Desktop/Laptop/Tablet/Mobile. Diese Pagebuilder-Breakpoints werden von cache_compress_css.php fuer Media-Queries generiert und sind nicht aus custom.less-Tokens editierbar — sie sind Teil der Pagebuilder-Architektur.
Shadow (1 Token)
| LESS-Variable | CSS-Variable | Default |
|---|---|---|
@shadowCard | --shadow-card | 0px 4px 20px rgba(0, 0, 0, 0.08) |
:root — CSS-Custom-Properties-Block
Am Ende der Token-Definition generiert custom.less einen :root {}-Block, der alle Tokens ausser Breakpoints als CSS-Custom-Property verfuegbar macht (Breakpoints gehen nicht, siehe Abschnitt weiter unten):
:root {
--color-primary: @colorPrimary;
--color-primary-hover: @colorPrimaryHover;
--color-text: @colorText;
--font-body: @fontBody;
--font-size-base: @fontSizeBase;
--spacing-base: @spacingBase;
--spacing-section: @spacingSection;
--radius-base: @radiusBase;
--shadow-card: @shadowCard;
/* ... alle weiteren */
}Warum beides?
- LESS-Variablen (
@colorPrimary): Compile-Zeit, koennen in Mixins und Berechnungen (darken(),fade()) verwendet werden. - CSS-Custom-Properties (
var(--color-primary)): Runtime, koennen dynamisch via JavaScript geaendert werden (z. B. Theme-Switch), vererben durch DOM-Tree.
Komponenten, die nur Werte referenzieren, koennen beides nutzen. Dynamische Theme-Overrides laufen ueber CSS-Variables:
/* Dark-Theme-Override auf Body */
body.dark-theme {
--color-text: #f0f0f0;
--color-background: #0b0f1a;
}Menu-Editor Token-Mapping
Das menueditor-Plugin nutzt ein zweites Set von CSS-Variablen mit --me-*-Prefix, die aus den Design-Tokens abgeleitet werden:
.me-wrapper {
--me-bg: @colorBackground;
--me-primary: @colorPrimary;
--me-primary-fade: fade(@colorPrimary, 5%);
--me-text: @colorText;
--me-text-light: @colorTextLight;
--me-border: fade(@colorBorder, 25%);
--me-dropdown-bg: @colorBackground;
--me-dropdown-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
--me-spacing: @spacingBase;
--me-radius: @radiusBase;
--me-nav-height: 56px;
/* ... */
}Das erlaubt dem Menu-Editor eigene Abweichungen (z. B. me-dropdown-shadow ist spezifischer als shadow-card), ohne die globalen Tokens zu aendern. Das Menu-LESS lebt unter _public/src/css/frontend/menu.less und nutzt var(--me-*).
Details zum Menu-Editor-System liegen im php-backend-check-Skill im Abschnitt "Menu Editor".
Live-Editing via /admin/design
Der Design-Token-Editor unter /admin/design zeigt den kompletten Inhalt von custom.less in einem CodeMirror-Editor mit LESS-Syntax-Highlighting und Syntax-Validierung:
1. /admin/design oeffnen
2. Token-Werte anpassen (z. B. @colorPrimary: #E11D48;)
3. "Speichern" klicken
4. Core validiert LESS via Less_Parser
5. Neuer Wert landet in website.custom_less (DB-Spalte)
6. Core triggert Cache-Rebuild via rebuildCssCache()
7. Neue Tokens sind nach dem Rebuild wirksam — ohne DeployDer Rebuild in Schritt 6 laeuft im Normalfall automatisch direkt nach dem Save. Wenn er fehlschlaegt (z. B. APCu-Permission, DB-Verbindungsproblem), zeigt das Frontend weiter den alten Wert — in dem Fall manuell /admin/cache → "Cache leeren" triggern. Siehe Haeufige Fehler unten.
API-Actions (/api/backend/design)
| Action | Methode | Zweck |
|---|---|---|
load_less | GET | Custom LESS aus DB laden (Fallback: Datei) |
save_less | POST | LESS validieren, in DB speichern, Cache-Rebuild |
reset_less | POST | Default aus Datei laden, in DB schreiben |
Nach jedem Save wird der CSS-Cache via rebuildCssCache() neu generiert, damit die neuen Token-Werte ins Production-Bundle fliessen.
Cache-Integration
Der CSS-Build-Prozess (ueber das cache_settings-Plugin) laedt Custom-LESS aus der DB, falls vorhanden:
// cache_compress_css.php (Auszug)
function loadCustomLessFromDatabase() {
$q = query("SELECT custom_less FROM website LIMIT 1");
if (num_rows($q) > 0) {
$row = fetch_assoc($q);
return $row['custom_less'] ?? '';
}
return '';
}Wenn die DB-Spalte leer ist, faellt der Build auf die Datei _public/src/css/frontend/custom.less zurueck. Das heisst:
- Fresh-Install: Datei-Tokens sind aktiv
- Nach erstem Save via
/admin/design: DB-Tokens ueberschreiben die Datei-Tokens (Datei bleibt unveraendert) - Reset via Admin: lockes DB-Snapshot, Datei wieder als Quelle
Migration 013_add_custom_less_to_website.sql legt die Spalte an.
Komponenten nutzen Tokens
Widgets und Theme-Komponenten referenzieren die Tokens direkt:
// Im Widget-Template oder Theme-LESS
.my-widget {
background: @colorPrimary;
color: @colorWhite;
padding: @spacingMedium;
border-radius: @radiusBase;
font-family: @fontBody;
font-size: @fontSizeBase;
}Oder per CSS-Custom-Property (z. B. fuer dynamische Switches):
.my-widget {
background: var(--color-primary);
color: var(--color-white);
padding: var(--spacing-medium);
}Haeufige Fehler
Breakpoints als CSS-Variable nutzen
@media (max-width: var(--breakpoint-mobile)) funktioniert nicht — Media-Queries akzeptieren keine CSS-Custom-Properties im Selektor. Breakpoints immer als LESS-Variable verwenden: @media (max-width: @breakpointMobile).
Token in custom.less geaendert, Frontend zeigt alten Wert
Cache-Rebuild fehlt. Entweder in Admin-UI nochmal "Speichern" klicken (triggert Rebuild automatisch) oder /admin/cache → "Cache leeren".
Datei-Aenderungen an custom.less werden ignoriert
Sobald einmal in der DB ein Wert steht (website.custom_less != ''), ignoriert der Build die Datei. Entweder "Reset"-Button in /admin/design (laedt Datei-Default zurueck) oder DB-Spalte manuell leeren: UPDATE website SET custom_less = NULL.
LESS-Syntax-Fehler beim Save
Der save_less-Endpoint validiert via Less_Parser. Syntax-Fehler werden mit Zeilennummer zurueckgegeben, Save wird abgelehnt — der alte Wert bleibt aktiv. Fuer komplexe Aenderungen: vorher lokal mit dem LESS-Compiler testen.
AI-generiertes CSS ueberschreibt Tokens
Der AI-CSS-Generator schreibt direkt in custom_css-Felder pro Widget/Row/Column (nicht in die globalen Tokens). Wenn eine AI-Response in Richtung "Set primary color to red" geht, sollte das nicht ueber @colorPrimary: red; im Widget-CSS landen — das ueberschreibt nur lokal. Fuer globale Token-Aenderungen immer den Design-Editor nutzen.
Siehe auch
- Theme-Struktur — wo Theme-CSS liegt und welche
custom.less-Datei - Environment-Variablen —
.envohne Token-Config - Widgets › Custom-CSS-Konvention —
.widget_{id}-Wrappers nutzen dieselben Tokens _public/src/css/frontend/custom.less— Datei-Quelle im Repo_public/extensions/core/backend/design/— Design-Plugin