Zum Inhalt

Architektur

Vier Ideen tragen TWtheme. Wer sie kennt, findet sich im Rest der Dokumentation zurecht.

Die Ebenen

graph TB
  subgraph child [Child-Theme: das Projekt]
    ChildJson["theme.json — Marke"]
    ChildTpl["Template-Overrides"]
    ChildAssets["eigenes CSS und JS"]
    ChildHooks["Hooks und eigene Inhaltstypen"]
  end

  subgraph parent [Parent-Theme: TWtheme]
    Tokens["theme.json — neutrale Tokens"]
    Templates["Block-Templates und Parts"]
    Module["50 Module"]
    Engine["Feld-Engine"]
  end

  WP["WordPress-Kern"]

  child --> parent
  parent --> WP
  Module --> Engine

Der Parent ist die Grundlage und wird nie projektspezifisch geändert. Das Child liegt darüber und bringt alles mit, was nur dieses eine Projekt betrifft.

Ein Block-Theme

Kopf- und Fußbereich, Archive, Einzelansichten und Seiten sind Block-Templates — HTML-Dateien im Theme, die im Site-Editor bearbeitbar sind. Bearbeitet man ein Template dort, landet die Fassung in der Datenbank und hat Vorrang vor der Datei im Theme.

Alle Gestaltungswerte kommen aus der theme.json. WordPress erzeugt daraus CSS-Variablen, die im Editor und im Frontend gleich sind. Das SCSS des Themes liefert nur, was sich damit nicht ausdrücken lässt: Komponentenlogik und Feinheiten einzelner Blöcke.

Was daraus folgt: Eine Farbe ändert man nicht im Stylesheet, sondern in der theme.json. Sonst laufen Editor und Frontend auseinander.

Alles ist ein Modul

Der Funktionsumfang steckt in 50 Modulen unter includes/theme-options/. Jedes Modul ist ein Ordner mit einer gleichnamigen PHP-Datei und einer module.json, die seine Metadaten trägt.

Ein Modul kann mitbringen: Blöcke, Shortcodes, Felder in den Theme-Optionen, einen eigenen Inhaltstyp, eigene Assets, Templates.

graph LR
  Option["Option twtheme_modules"] --> Registry["Modul-Registry"]
  Registry --> Aktiv["aktive Module"]
  Aktiv --> PHP["PHP wird geladen"]
  Aktiv --> Blocks["Blöcke werden registriert"]
  Aktiv --> Fields["Optionsfelder erscheinen"]
  Registry --> Inaktiv["inaktive Module"]
  Inaktiv --> Nichts["nichts wird geladen"]

Entscheidend ist der rechte Zweig: Ein abgeschaltetes Modul wird nicht geladen. Seine Funktionen existieren nicht, seine Blöcke sind nicht registriert. Deshalb gehört vor jeden Aufruf einer Modulfunktion ein function_exists().

Der Modulzustand ist eine gewöhnliche Option

Welche Module laufen, steht in der WordPress-Option twtheme_modules auf der jeweiligen Seite. Das ist die einzige Quelle.

Das Theme ruft nirgends nach Hause und prüft keine Lizenz. Es gibt keinen ausgehenden Request. Ein TWhub kann dieselbe Option schreiben, ist aber nur einer von drei möglichen Schreibern:

graph LR
  Admin["Theme → Module"] --> Apply["twtheme_modules_apply_state()"]
  Hub["TW Hub Agent"] --> Apply
  CLI["WP-CLI"] --> Apply
  Apply --> Option["Option twtheme_modules"]

Fällt der Hub weg, bleibt der Zustand bestehen und die Seite Theme → Module voll bedienbar.

Felder statt ACF

TWtheme nutzt kein Advanced Custom Fields, sondern eine eigene Feld-Engine unter includes/engine/. Sie erzeugt aus Felddefinitionen im PHP-Array:

  • die Eingabefelder im Backend, über den FieldBuilder
  • Optionsseiten und Meta-Boxen
  • Inhaltstypen und Taxonomien
  • Shortcodes für einzelne Feldwerte, automatisch
  • die Datenquelle der Meta-Blöcke im Editor

Ein einmal definiertes Feld ist damit überall verfügbar: im Backend zum Ausfüllen, im Frontend als Block, als Shortcode und im Code.

Laden nach Kontext

Nicht jeder Seitenaufruf braucht alles. includes/bootstrap.php entscheidet anhand des Request-Kontexts, welche Module überhaupt geladen werden — eine REST-Anfrage braucht andere Teile als eine Frontend-Seite oder der Site-Editor.

Steuern lässt sich das über die Filter twtheme_boot_module_map und twtheme_boot_modules.

Wo was liegt

Ordner Inhalt
templates/ Block-Templates als HTML
parts/ Template-Parts als HTML
template-parts/ Bausteine in PHP, aus Code aufgerufen
styles/ Blockstil-Variationen
includes/theme-options/ die 50 Module
includes/engine/ Feld-Engine, Inhaltstypen, Import und Export
includes/helper/ öffentliche Helferfunktionen
includes/gutenberg/ Blockregistrierung und Editor-Assets
includes/plugins/ Anbindung an Fremdplugins
source/ SCSS- und JS-Quellen
assets/ gebaute CSS- und JS-Dateien
scripts/ Build-, Prüf- und Release-Werkzeuge
languages/ Übersetzungen

source/ und assets/ nicht verwechseln: Bearbeitet wird immer source/, ausgeliefert wird assets/. Dazwischen liegt der Build — siehe Build-Kette.

Die Reihenfolge beim Anpassen

Für jede Anpassung gibt es einen vorgesehenen Weg. Die Reihenfolge ist eine Rangfolge, nicht eine Auswahl: Was weiter oben geht, sollte nicht weiter unten gelöst werden.

  1. theme.json im Child — Farben, Schriften, Abstände, Blockstandards
  2. Template und Template-Part im Child mit gleichem Dateinamen überschreiben
  3. Eigenes CSS und JS aus dem Child laden
  4. PHP-Hooks und Filter nutzen
  5. Nur wenn unvermeidbar: eine einzelne Parent-Datei gezielt kopieren

Ausführlich unter Child-Theme.