docs
TYPO3 v14
Erweitern  /  Theming: Tokens, SCSS, eigenes Theme

15 — Theming: Tokens, SCSS, eigenes Theme

Für die Entwicklung. Wie die Gestaltung organisiert ist und wie ein Theme auf yavi_core aufgebaut wird.


Kein Build-Schritt

SCSS wird serverseitig von scssphp kompiliert, das mit Bootstrap Package kommt. Kein npm, kein webpack, kein dist/. SCSS bearbeiten, Cache leeren, neu laden:

bash
ddev exec vendor/bin/typo3 cache:flush

Die kompilierte Datei landet in typo3temp/assets/bootstrappackage/css/theme-<hash>.css. Der Hash leitet sich aus der Quelle ab; eine veraltete Datei wird also nie ausgeliefert — aber erst, nachdem der Cache geleert wurde.

Structure und Skin

Das SCSS des Core ist entlang einer Linie geteilt:

OrdnerEnthältEin Theme …
Scss/Structure/Geometrie, Layout, Verhalten — was eine Karte zur Karte machtfasst es selten an
Scss/Skin/Farbe, Schatten, Rahmen, Dekoration — was sie nach diesem Theme aussehen lässtersetzt es je Komponentenfamilie

Ein Theme ersetzt die gesamte Skin-Datei einer Komponentenfamilie, statt einzelne Deklarationen zu überschreiben. Es gibt keine Cascade Layers; die Datei des Themes kommt schlicht später.

text
packages/yavi-lucerne/Resources/Public/Scss/
├── theme.scss          Einstiegspunkt
├── _variables.scss     die eigenen Tokens des Themes
├── _structure.scss     generiert: die Liste der Structure-Partials des Core
├── _skin.scss          generiert: die Liste der Skin-Partials, vom Core oder vom Theme
├── _custom.scss        freier Raum für dieses Theme
└── Skin/               die Skin-Dateien, die dieses Theme besitzt
    ├── audioplayer.scss
    ├── buttons.scss
    ├── cards.scss
    ├── carousel.scss
    ├── cta.scss
    └── menu.scss

theme.scss ist absichtlich kurz:

scss
@import "EXT:yavi_core/Resources/Public/Scss/structure";
@import "variables";
@import "structure";
@import "skin";
@import "custom";

Die Listen synchron halten

_structure.scss und _skin.scss sind generiert. Kommt im Core ein Partial hinzu, werden die Listen jedes Themes neu erzeugt:

bash
ddev exec php packages/yavi-core/Build/sync-theme-scss.php
ddev exec php packages/yavi-core/Build/sync-theme-scss.php --check   # schreibt nichts, scheitert bei Abweichung

Die Variante --check läuft in der Testsuite; ein vergessenes Partial lässt also den Build scheitern, statt stillschweigend aus einem Theme zu verschwinden.

Das Token-System

Jeder konfigurierbare Wert ist eine CSS Custom Property mit genau einem Eigentümer.

scss
// 1. yavi_core/_variables-defaults.scss — die theme-unabhängige Vorgabe
--card-radius: calc(12px * var(--theme-radius-scale, 1));

// 2. _variables.scss des Themes — der Wert dieses Themes
--card-radius: 25px;

// 3. Site Settings — vom ThemeCssVariablesRenderer in den Seitenkopf geschrieben
--card-radius: calc(18px * var(--theme-radius-scale, 1));

Regeln, die daraus folgen:

Fallback-Ketten

Ein Token, das aus mehreren Ebenen überschrieben werden darf, liest sie in der Reihenfolge ihrer Autorität:

scss
--megamenu-link-border-radius:
  var(--megamenu-link-radius,            /* Inline-Ueberschreibung - absolute px */
  var(--nav-menu-radius,                 /* Backend-Feld */
  calc(8px * var(--theme-radius-scale-mega, var(--theme-radius-scale, 1)))));  /* Theme */

Zu lesen als: Wer spezifischer ist, gewinnt, und ein nicht gesetztes Token fällt durch.

Erst literal, dann abgeleitet

Marken-Tokens werden zweimal deklariert:

scss
--nav-link-active-bg: #{derive($secondary, 1.0674, 0.7915, 6.13)};
--nav-link-active-bg: oklch(from var(--theme-secondary) calc(l * 1.0674) …);

Die erste Zeile ist eine statisch in SCSS berechnete Farbe für Browser ohne relative Farbsyntax; die zweite überschreibt sie dort, wo oklch(from …) unterstützt wird. Beide Zeilen werden gebraucht — die erste ist kein toter Code.

Ein neues Theme bauen

  1. Das Gerüst kopieren. yavi_lucerne als Vorlage nehmen: composer.json, ext_emconf.php, Configuration/Sets/Full/, Resources/Public/Scss/.
  2. Composer-Name und Extension Key setzen, z. B. webagentur-yahya/yavi-zurich / yavi_zurich.
  3. Das Set deklarieren:

``yaml # Configuration/Sets/Full/config.yaml name: yavi-zurich/full label: 'Yavi Zurich' dependencies: - yavi-core/full ``

  1. Einen eigenen Produktcode vergeben, in Configuration/Sets/Full/settings.yaml:

``yaml licence: product: yavi-zurich ``

Wird das vergessen, scheitert die Lizenzprüfung genau wie bei einem falschen Schlüssel.

  1. Das Design in settings.yaml deklarieren — Farben, Schriften, Höhen, Radien. Vollständig, nicht als Abweichung von Lucerne.
  2. Die benötigten Skins übernehmen, in Scss/Skin/, danach sync-theme-scss.php ausführen.
  3. Die Navigation formen, über die Nav-Tokens plus Skin/navigation.scss. Die acht Varianten selbst bleiben im Core: Sie werden über einen interpolierten Pfad eingebunden und lassen sich nicht je Theme ersetzen.

Templates überschreiben

Die Pfade für Templates, Partials und Layouts werden je Set konfiguriert. Ein Theme ergänzt eigene Pfade an einem höheren Index; TYPO3 fällt auf die Datei des Core zurück, wenn das Theme keine mitbringt. Nur die Datei kopieren, die tatsächlich geändert wird — jedes kopierte Template ist eines, das gepflegt werden muss, wenn sich das des Core ändert.

Wissenswertes, bevor eine Stunde in der Fehlersuche vergeht