Zum Inhalt

Child-Theme

Dies ist die wichtigste Seite für Entwickler. Sie legt fest, wie ein Projekt TWtheme erweitert — und was es nicht tun darf.

Die eine Regel

Projektspezifisches gehört ins Child-Theme, nie in den Parent.

Der Parent bleibt die Grundlage für Architektur, Blockregistrierung und Basisgestaltung. Er liefert bewusst eine neutrale Palette, einen Systemschriftstack und leere Erweiterungspunkte.

Was ein konkretes Projekt braucht — Marke, eigene Inhaltstypen, Rechtstexte, Tracking, Landingpages — kommt aus dem Child dazu.

Der Maßstab: Ein Child muss ein Parent-Update ohne manuelle Eingriffe überstehen. Jede Änderung am Parent bricht dieses Versprechen, weil sie beim nächsten Update verloren geht.

Die Rangfolge

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

  1. theme.json — Farben, Schriften, Abstände, Typografie, Blockstandards
  2. Template- und Part-Overrides — templates/*.html, parts/*.html
  3. Eigenes CSS und JS aus dem Child laden
  4. PHP-Hooks und Filter nutzen
  5. Nur wenn unvermeidbar: eine einzelne Parent-Template-Datei gezielt kopieren

Als vollständige Vorlage dient das Theme TWtheme Demo. Dort ist jeder dieser Wege einmal ausgeschrieben.

Die zwei Stolpersteine

Diese beiden kosten beim ersten Mal verlässlich Zeit.

Presets werden ersetzt, nicht zusammengeführt

Definiert das Child settings.color.palette, ersetzt dieses Array die Palette des Parents vollständig. Es genügt nicht, nur den geänderten Slug zu listen — dann sind alle anderen Farben weg.

Das Child muss die komplette Palette wiederholen und darin die gewünschten Werte ändern:

{
  "settings": {
    "color": {
      "palette": [
        { "slug": "primary", "name": "Primary", "color": "#7c3aed" },
        { "slug": "primary-200", "name": "Primary 200", "color": "#f3e8ff" },
        { "slug": "white", "name": "White", "color": "#ffffff" },
        { "slug": "secondary", "name": "Secondary", "color": "#1a1d21" },
        { "slug": "light", "name": "Light", "color": "#eef1f5" },
        { "slug": "light-600", "name": "Light 600", "color": "#7d8794" },
        { "slug": "dark", "name": "Dark", "color": "#1a1d21" },
        { "slug": "grey", "name": "Grey", "color": "#4a5058" },
        { "slug": "danger", "name": "Danger", "color": "#d64545" }
      ]
    }
  }
}

Dasselbe gilt für fontFamilies und fontSizes.

Die vollständige Liste der Slugs steht unter Design-Tokens.

Die functions.php des Childs läuft vor der des Parents

WordPress lädt die functions.php des Childs zuerst. Zu diesem Zeitpunkt existieren TWTHEME__DIR, TWTHEME__PATH, TWTHEME__OPTIONS und twtheme_include_files() noch nicht.

Code, der sie braucht, gehört in after_setup_theme oder später:

add_action('after_setup_theme', 'projekt_setup', 20);
function projekt_setup(): void
{
    load_child_theme_textdomain('projekt', get_stylesheet_directory() . '/languages');

    // Ab hier sind die Parent-APIs verfügbar.
    twtheme_include_files(['my-cpt'], 'includes/');
}

add_filter() auf Parent-Filter ist dagegen auf Dateiebene richtig — der Parent wendet sie erst beim Booten an, die Registrierung muss also vorher stehen:

// Richtig auf Dateiebene: greift, wenn der Parent den Filter anwendet.
add_filter('twtheme_pricing_cards_post_type', static fn (): string => 'my_product');

1. theme.json

Der erste und beste Weg. Alles, was Farbe, Schrift, Abstand oder Layout betrifft, gehört hierhin — nicht ins Stylesheet.

Das Child darf überschreiben und ergänzen:

  • settings.color.palette
  • settings.typography.fontFamilies und fontSizes
  • styles.*

Slugs stabil halten. Die bestehenden Slugs primary, primary-200, secondary, light, light-600, dark, grey, danger, white und base nicht ohne Migration entfernen — Blöcke und Stylesheets verweisen darauf. Neue Slugs nur ergänzen, wenn sie wirklich projektspezifisch sind.

2. Templates überschreiben

Der Parent liefert die Basis in templates/*.html und parts/*.html. Das Child darf jede dieser Dateien durch eine gleichnamige ersetzen.

Zwei Hinweise:

  • Nur überschreiben, was sich tatsächlich unterscheidet. Eine kopierte Datei bekommt keine Verbesserungen aus Parent-Updates mehr.
  • Das Blockmarkup erweitern, nicht ohne Not zerlegen. Je näher die Struktur am Parent bleibt, desto einfacher ist ein späterer Abgleich.

Welche Templates es gibt: Templates. Ausführlich unter Templates überschreiben.

3. Eigenes CSS und JS

Das Child lädt seine Assets selbst und nennt die Parent-Handles als Abhängigkeit, damit die eigenen Regeln gewinnen:

add_action('wp_enqueue_scripts', 'projekt_enqueue_assets', 30);
function projekt_enqueue_assets(): void
{
    $path = get_stylesheet_directory() . '/style.css';

    if (! is_readable($path)) {
        return;
    }

    wp_enqueue_style(
        'projekt',
        get_stylesheet_directory_uri() . '/style.css',
        function_exists('twtheme_main_style_deps') ? twtheme_main_style_deps() : [],
        (string) filemtime($path)
    );
}

twtheme_main_style_deps() statt eines festen ['twtheme-main']: Wählt das Child das Haupt-Stylesheet des Parents über den Filter twtheme_load_main_stylesheet ab, fällt der Handle aus der Liste. Mit einer festen Angabe würde WordPress ihn über die Abhängigkeit doch wieder laden — oder das eigene Stylesheet gar nicht ausgeben.

Für das CSS selbst gilt:

  • Auf --wp--preset--* und die --tw-*-Variablen des Themes aufbauen.
  • Keine externen CSS-Frameworks einführen.
  • Spezifität niedrig halten.

Siehe CSS-Architektur.

Für JavaScript: Eigene Skripte anmelden und Parent-Handles als Abhängigkeit nutzen. Der Parent bleibt modulbasiert und wird mit esbuild gebaut.

4. Hooks und Filter

Bevorzugte Einstiegspunkte sind after_setup_theme, init, wp_enqueue_scripts und die Filter des Parents.

Ein paar häufige Fälle:

// Ein Modul vom Laden ausnehmen, statt den Ordner im Parent zu löschen
add_filter('twtheme_addons_skip', static function (array $skip): array {
    $skip[] = 'megamenu';

    return $skip;
});

// Einen Feature-Filter des Parents belegen
add_filter('twtheme_pricing_cards_post_type', static fn (): string => 'my_product');

// Einen Erweiterungspunkt in den Parent-Templates füllen
add_action('twtheme_footer_notice', static function (): void {
    echo '<p>' . esc_html__('Impressum · Datenschutz', 'projekt') . '</p>';
});

Der vollständige Katalog: Hooks und Filter.

Module besser über die Oberfläche abschalten

Der Filter twtheme_addons_skip ist der Weg für Fälle, die im Code festgeschrieben sein sollen. Für den Normalfall genügt Theme → Module — das ist nachvollziehbarer und im Team sichtbar.

Eigene Blöcke

Der Parent registriert seine Blöcke metadatenbasiert über block.json. Ein Child darf eigene Blöcke registrieren, sollte aber:

  • keine Parent-Blocknamen überschreiben
  • eine eigene Kategorie oder ein eigenes Präfix verwenden; twtheme/ bleibt dem Parent vorbehalten

Siehe Eigener Block.

Eigene Inhaltstypen

Projektspezifische Inhaltstypen gehören ins Child. Die Engine des Parents steht dafür bereit — TwthemeCreatePostType und TwthemeCreateTaxonomy, aufgerufen aus after_setup_theme.

Im Parent liegt eine vollständige, abgeschaltete Vorlage: includes/posttypes/example-posttype.php. Sie ist zum Abschreiben gedacht, nicht zum Aktivieren.

Siehe Inhaltstypen anlegen.

Der Build bleibt getrennt

Der Parent baut mit npm run build:js und npm run build:css. Ein Child darf eine eigene Pipeline haben, darf aber die Artefakte des Parents nicht ersetzen.

Siehe Build-Kette.

Nicht erlaubt

  • Parent-Dateien im Projektbetrieb ändern
  • Externe CSS-Frameworks einführen
  • Zentrale Token-Slugs entfernen, ohne Daten und Markup zu migrieren
  • Blocknamen des Parents überschreiben

Wann ist ein Child fertig

  • Es funktioniert nach einem Parent-Update ohne manuelle Nacharbeit.
  • Editor und Frontend zeigen keine Rückschritte.
  • Alle Overrides sind dokumentiert und auf möglichst wenige Dateien begrenzt.

Weiter