vom Master-Format bis zur Alpine.js-Komponente
Eigene Hyvä Page-Builder-Content-Types entstehen nicht durch Knockout-Widgets, sondern durch serverseitiges phtml-Rendering mit Tailwind-Klassen und gezielter Alpine.js-Interaktivität. Dieser Guide zeigt Registrierung, Rendering-Architektur und Performance-Patterns anhand echter Code-Beispiele.
Inhaltsverzeichnis
- 1. Warum Page Builder in Hyvä eine Sonderrolle einnimmt
- 2. Architektur: Master-Format-Renderer statt Knockout-Widget-Rendering
- 3. Einen eigenen Content-Type registrieren
- 4. Rendering-Template im Hyvä-Stil bauen
- 5. Alpine.js-Interaktivität in eigenen Content-Types einbauen
- 6. Master-Format-Parser und Platzhalter-Konvertierung verstehen
- 7. Konsistenz zwischen Admin-Editor-Preview und Frontend-Rendering
- 8. Performance eigener Content-Types
- 9. Migration bestehender Luma-Page-Builder-Inhalte auf Hyvä-Rendering
- 10. Zusammenfassung
- 11. FAQ
1. Warum Page Builder in Hyvä eine Sonderrolle einnimmt
In Luma wird Page-Builder-Content clientseitig über Knockout.js-Widgets gerendert: Jeder Content-Type bringt eine eigene RequireJS-Komponente mit, die im Browser das gespeicherte Master-Format in DOM-Elemente übersetzt. Das bedeutet zusätzliche JavaScript-Bundles, Knockout-Bindings und eine Rendering-Kette, die genau den Technologien widerspricht, die Hyvä bewusst vermeidet. Hyvä Page-Builder-Content-Types lösen dieses Problem grundlegend anders: Das Kompatibilitätsmodul hyva-themes/magento2-page-builder ersetzt die komplette clientseitige Rendering-Pipeline durch serverseitiges PHP-Rendering.
Für Redakteure ändert sich im Admin-Editor nichts, sie arbeiten weiterhin mit der gewohnten Drag-and-Drop-Oberfläche von Magento_PageBuilder. Der entscheidende Unterschied liegt im Frontend: Statt Knockout-Komponenten zu hydrieren, liest Hyvä den gespeicherten Content-Baum serverseitig ein und rendert jeden Knoten über ein eigenes phtml-Template. Damit werden Hyvä Page-Builder-Content-Types zu vollwertigen, CSP-konformen Bestandteilen des Themes, ohne dass zusätzliches Bundle-JavaScript oder jQuery-Abhängigkeiten geladen werden müssen.
2. Architektur: Master-Format-Renderer statt Knockout-Widget-Rendering
Page Builder speichert Inhalte im sogenannten Master-Format, einer HTML-Struktur mit data-content-type-Attributen, verschachtelten Containern und Style-Informationen. In Luma übernimmt ein clientseitiger Konverter diese Struktur und bindet für jeden Knoten eine Knockout-Komponente. Bei Hyvä Page-Builder-Content-Types übernimmt stattdessen ein PHP-Renderer den gesamten Baum: Er läuft den Structure-Tree einmal serverseitig durch, löst für jeden Knotentyp das passende Template auf und rendert das Ergebnis direkt in die Seite.
Diese Architekturentscheidung hat spürbare Konsequenzen. Es gibt keine Laufzeit-Hydrierung im Browser, keine zusätzlichen Netzwerk-Requests für Widget-Definitionen und keinen Layout-Shift durch nachträglich eingehängte Komponenten. Die Zuordnung von Content-Type-Name zu Template erfolgt deklarativ über content_type.xml, wodurch Hyvä Page-Builder-Content-Types genauso wie normale Block-Templates in den Hyvä-Rendering-Zyklus eingebettet sind und sich mit Tailwind-Klassen stylen lassen wie jedes andere Theme-Fragment.
3. Einen eigenen Content-Type registrieren
Ein neuer Content-Type benötigt mindestens zwei Deklarationsdateien: content_type.xml definiert Name, Label, Icon und die verfügbaren Appearances, während appearance.xml die Formularfelder festlegt, die Redakteure im Page-Builder-Editor ausfüllen können. Beide Dateien folgen dem gleichen deklarativen Muster wie andere Magento-Konfigurationsdateien und werden pro Modul unter etc/pagebuilder/ abgelegt.
<!-- app/code/Mironsoft/PageBuilder/etc/pagebuilder/content_type.xml -->
<?xml version="1.0"?>
<content_types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Hyva_PageBuilder:etc/pagebuilder/content_type.xsd">
<type name="feature-callout" label="Feature Callout" icon="icon-feature-callout" component="Magento_PageBuilder/js/content-type">
<appearances>
<appearance name="default" default="true"
template="Mironsoft_PageBuilder::content-type/feature-callout/default.phtml"/>
<appearance name="highlighted"
template="Mironsoft_PageBuilder::content-type/feature-callout/highlighted.phtml"/>
</appearances>
</type>
</content_types>
<!-- app/code/Mironsoft/PageBuilder/etc/pagebuilder/feature-callout/appearance.xml -->
<?xml version="1.0"?>
<appearances xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_PageBuilder:etc/appearance.xsd">
<appearance name="default">
<elements>
<element name="headline">
<converter component="Magento_PageBuilder/js/content-type/text/converter" property="headline"/>
</element>
<element name="text">
<converter component="Magento_PageBuilder/js/content-type/text/converter" property="text"/>
</element>
<element name="background_color">
<converter component="Magento_PageBuilder/js/converter/style-attribute" property="background-color"/>
</element>
</elements>
</appearance>
</appearances>
Der template-Attributwert in content_type.xml ist die entscheidende Hyvä-spezifische Ergänzung: Er verweist auf das phtml-Template, das der PHP-Renderer für diese Appearance verwendet. Ohne diesen Eintrag fällt Hyvä auf ein generisches Fallback-Template zurück, das die Formularfelder unformatiert ausgibt. Wer Hyvä Page-Builder-Content-Types sauber registriert, achtet außerdem auf einen eindeutigen Modul-Namespace im name-Attribut, damit keine Kollisionen mit Core-Content-Types wie banner oder row entstehen.
4. Rendering-Template im Hyvä-Stil bauen
Das eigentliche Rendering-Template ist ein gewöhnliches phtml-Template, das die im Master-Format hinterlegten Feldwerte als Array erhält. Der wichtigste Unterschied zu einem generischen Page-Builder-Theme: Statt der Core-CSS-Klassen wie pagebuilder-content-type oder Bootstrap-Grid-Klassen verwenden Hyvä Page-Builder-Content-Types ausschließlich Tailwind-Utility-Klassen, die zum bestehenden Design-System des Themes passen.
<?php
/** @var \Magento\Framework\View\Element\Template $block */
/** @var array $data */
// $data is provided by the Hyvä PageBuilder renderer with all master-format field values
$data = $block->getData('content_type_data') ?? [];
$headline = (string) ($data['headline'] ?? '');
$text = (string) ($data['text'] ?? '');
$backgroundColor = (string) ($data['background_color'] ?? '#0f172a');
?>
<div class="not-prose rounded-2xl p-6 sm:p-8 my-6" style="background-color: <?= $block->escapeHtmlAttr($backgroundColor) ?>;">
<?php if ($headline !== ''): ?>
<p class="text-white text-xl font-bold mb-2"><?= $block->escapeHtml($headline) ?></p>
<?php endif; ?>
<?php if ($text !== ''): ?>
<div class="text-white text-sm prose prose-invert max-w-none">
<?= /* @noEscape */ $text ?>
</div>
<?php endif; ?>
</div>
Bewährt hat sich außerdem, Abstands- und Typografie-Skalen aus der Tailwind-Konfiguration wiederzuverwenden statt eigene Pixelwerte zu definieren. So bleiben Hyvä Page-Builder-Content-Types visuell konsistent mit dem restlichen Theme, auch wenn Redakteure Inhalte frei zusammenstellen. HTML-Felder wie text müssen bewusst ungeschützt ausgegeben werden, da sie bereits redaktionell erzeugtes, formatiertes HTML enthalten, während einfache String-Felder immer über escapeHtml laufen.
5. Alpine.js-Interaktivität in eigenen Content-Types einbauen
Sobald ein Content-Type mehr als statisches Markup braucht, etwa Tabs oder ein Akkordeon, kommt Alpine.js ins Spiel, das in Hyvä ohnehin global geladen ist. Kein zusätzliches Bundle, kein separates RequireJS-Modul: Die Interaktivität wird direkt als x-data-Attribut im Template deklariert, wodurch Hyvä Page-Builder-Content-Types interaktiv bleiben, ohne die CSP-Konfiguration zu verletzen.
<?php
/** @var \Mironsoft\PageBuilder\Block\ContentType\Tabs $block */
// Tabs are stored as repeatable child elements in the master format
$tabs = $block->getTabs(); // array<int, array{label: string, html: string}>
?>
<div class="not-prose my-6" x-data="{ active: 0 }">
<div class="flex flex-wrap gap-2 border-b border-slate-200 mb-4">
<?php foreach ($tabs as $index => $tab): ?>
<button
type="button"
@click="active = <?= (int) $index ?>"
:class="active === <?= (int) $index ?> ? 'border-orange-600 text-orange-700' : 'border-transparent text-slate-500'"
class="px-4 py-2 border-b-2 font-semibold text-sm transition-colors"
><?= $block->escapeHtml($tab['label']) ?></button>
<?php endforeach; ?>
</div>
<?php foreach ($tabs as $index => $tab): ?>
<div x-show="active === <?= (int) $index ?>" x-cloak class="prose prose-sm max-w-none">
<?= /* @noEscape */ $tab['html'] ?>
</div>
<?php endforeach; ?>
</div>
Wichtig ist, den aktiven Tab-Index nicht per Mustache-Ausdruck, sondern über x-show und Alpine-Bindings zu steuern, da Page-Builder-Templates serverseitig vorgerendert werden und keine Template-Interpolation zur Laufzeit stattfindet. Für Text-Ausgaben innerhalb von Alpine-Komponenten gilt dieselbe Regel wie im restlichen Theme: x-text statt geschweifter Klammern, damit auch bei deaktiviertem JavaScript kein unaufgelöster Platzhalter sichtbar wird. So bleiben Hyvä Page-Builder-Content-Types mit Alpine-Logik wartbar und CSP-sicher zugleich.
6. Master-Format-Parser und Platzhalter-Konvertierung verstehen
Page Builder legt Formatierungsinformationen wie Textfarbe, Ausrichtung oder Innenabstand als HTML-kodiertes data-pb-style-Attribut ab. Beim Rendern von Hyvä Page-Builder-Content-Types muss dieses Attribut zunächst dekodiert, dann in eine Liste von Key-Value-Paaren zerlegt und schließlich auf erlaubte Tailwind-Klassen gemappt werden. Wer diesen Schritt überspringt und Style-Attribute unkontrolliert in Inline-Styles kopiert, öffnet die Tür für CSP-Verstöße und inkonsistentes Styling.
<?php
declare(strict_types=1);
namespace Mironsoft\PageBuilder\Model\MasterFormat;
/**
* Decodes Page Builder master-format style attributes into whitelisted Tailwind utility classes.
*/
class StyleAttributeResolver
{
/** @var string[] */
private const ALLOWED_KEYS = ['text-align', 'background-color', 'padding'];
/**
* Convert a raw data-pb-style attribute string into Tailwind classes.
*
* @param string $rawStyle
* @return string
*/
public function toTailwindClasses(string $rawStyle): string
{
// Master format stores style as HTML-encoded "key: value;" pairs
$decoded = html_entity_decode($rawStyle, ENT_QUOTES | ENT_HTML5);
$pairs = array_filter(array_map('trim', explode(';', $decoded)));
$classes = [];
foreach ($pairs as $pair) {
[$key, $value] = array_pad(explode(':', $pair, 2), 2, '');
$key = trim($key);
if (!in_array($key, self::ALLOWED_KEYS, true)) {
continue;
}
$classes[] = $this->mapToTailwind($key, trim($value));
}
return implode(' ', array_filter($classes));
}
/**
* Map a single CSS key/value pair to its Tailwind utility equivalent.
*
* @param string $key
* @param string $value
* @return string
*/
private function mapToTailwind(string $key, string $value): string
{
return match ($key) {
'text-align' => 'text-' . $value,
'padding' => 'p-4',
default => '',
};
}
}
Typische Fallstricke entstehen beim Kopieren von Inhalten aus Word oder Google Docs in den Page-Builder-Editor: Sonderzeichen werden doppelt HTML-kodiert, und geschachtelte Content-Types können ihrerseits kodierte Anführungszeichen enthalten, die beim ersten Decode-Durchlauf nicht vollständig aufgelöst werden. Für robuste Hyvä Page-Builder-Content-Types empfiehlt sich daher, den Decode-Schritt einmal zentral im Renderer auszuführen statt in jedem einzelnen Template erneut.
7. Konsistenz zwischen Admin-Editor-Preview und Frontend-Rendering sicherstellen
Der Page-Builder-Editor im Admin nutzt weiterhin ein eigenes, Knockout-basiertes Preview-Template, unabhängig davon, dass das Frontend über Hyvä phtml-basiert rendert. Weichen Preview- und Frontend-Template optisch voneinander ab, verlieren Redakteure das Vertrauen in die What-you-see-is-what-you-get-Vorschau, und Korrekturschleifen zwischen Redaktion und Entwicklung nehmen zu.
In der Praxis bewährt sich, gemeinsame Werte wie Abstände, Schriftgrößen und Farbpaletten in einer zentralen Konfiguration zu pflegen und sowohl im Knockout-Preview-Template als auch im phtml-Frontend-Template zu referenzieren, statt sie zweimal unabhängig zu pflegen. So bleibt die Vorschau für Hyvä Page-Builder-Content-Types auch nach Design-Anpassungen synchron mit dem tatsächlichen Rendering im Shop.
Als Testroutine empfiehlt sich ein regelmäßiger visueller Vergleich zwischen Admin-Preview-iFrame und der veröffentlichten Seite, idealerweise automatisiert über Screenshot-Diffs in der CI-Pipeline. Das deckt Abweichungen auf, bevor Redakteure sie in der Produktion bemerken.
8. Performance eigener Content-Types
Da Hyvä Page-Builder-Content-Types ohnehin ohne zusätzliches Bundle-JavaScript auskommen, verschiebt sich die Performance-Optimierung auf Bilder und schwergewichtige Komponenten wie Slider. Bilder innerhalb eines Content-Types sollten grundsätzlich mit loading="lazy" ausgestattet werden, während Slider-Content-Types zusätzlich von einem Intersection-Observer profitieren, der Bildquellen erst beim Erreichen des Viewports nachlädt.
// Lazy-load slider images inside Page Builder content types only once visible
document.addEventListener('DOMContentLoaded', () => {
const sliders = document.querySelectorAll('[data-pagebuilder-slider]');
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) {
return;
}
const slider = entry.target;
slider.querySelectorAll('img[data-src]').forEach((img) => {
img.src = img.dataset.src;
img.removeAttribute('data-src');
});
obs.unobserve(slider);
});
}, { rootMargin: '200px 0px' });
sliders.forEach((slider) => observer.observe(slider));
});
Da Hyvä kein RequireJS ausliefert, entfällt zusätzlich der Overhead durch Modul-Definitionen und asynchrones Laden von AMD-Abhängigkeiten, die in Luma für jeden Slider-Content-Type nötig wären. In der Praxis senkt dieses Vorgehen den Largest Contentful Paint spürbar, weil Hyvä Page-Builder-Content-Types erst dann Bildlast erzeugen, wenn Besucher tatsächlich scrollen.
9. Migration bestehender Luma-Page-Builder-Inhalte auf Hyvä-Rendering
Der große Vorteil bei der Migration: Das im Master-Format gespeicherte HTML selbst ändert sich beim Wechsel von Luma zu Hyvä nicht, da es unabhängig vom Rendering-Layer in der Datenbank liegt. Der Umstieg auf Hyvä Page-Builder-Content-Types betrifft ausschließlich die Rendering-Schicht im Frontend, nicht die gespeicherten Inhalte selbst.
Breaking Changes entstehen typischerweise bei Content-Types, die in Luma auf jQuery-UI-Plugins oder Drittanbieter-Slider-Bibliotheken setzen, für die es noch kein Hyvä-Template gibt. Fehlt die Zuordnung in content_type.xml, rendert Hyvä ein leeres oder generisches Fallback statt des erwarteten Inhalts, was im schlimmsten Fall erst bei einer Stichprobe auffällt.
Als Teststrategie empfiehlt sich, vor dem Theme-Wechsel alle im CMS und in Kategorien verwendeten Content-Type-Appearances systematisch aufzulisten und gegen die vorhandenen Hyvä-Templates abzugleichen. Erst wenn für jede eingesetzte Appearance ein passendes phtml-Template existiert, lässt sich sicher sagen, dass Hyvä Page-Builder-Content-Types die komplette bestehende Content-Bibliothek abdecken.
Viele Teams unterschätzen, wie unterschiedlich Content-Type-Rendering je nach gewähltem Ansatz ausfällt. Die folgende Übersicht zeigt gängige Stolperfallen bei der Entwicklung eigener Hyvä Page-Builder-Content-Types im direkten Vergleich zum empfohlenen Pattern.
| Aufgabe | Unsicher / Ineffizient | Empfohlenes Pattern | Vorteil |
|---|---|---|---|
| Frontend-Rendering | Generische Page-Builder-CSS-Klassen | Eigenes phtml-Template mit Tailwind-Klassen | Konsistentes Design-System, kein CSS-Bloat |
| Interaktivität | Zusätzliches Bundle-JavaScript / Knockout-Widget | Alpine.js x-data direkt im Template | Kein zusätzliches Bundle, CSP-konform |
| Bilder im Content-Type | Alle Bilder eager laden | loading="lazy" plus Intersection Observer | Bessere LCP-Werte, weniger Datenvolumen |
| Admin-Preview | Preview-Template weicht vom Frontend ab | Gemeinsame Design-Tokens für Preview und Frontend | Konsistentes WYSIWYG für Redakteure |
| Content-Type-Registrierung | Core-Templates direkt überschreiben | Eigene content_type.xml mit eigenem Namespace | Update-sicher, keine Core-Konflikte |
| Style-Attribute | Inline-Styles unkontrolliert übernehmen | Style-Attribute validieren und auf Tailwind mappen | Kein CSS-Chaos, CSP-sauber |
Mironsoft
Hyvä-Frontend-Entwicklung und Page-Builder-Integration für Magento 2
Eigene Page-Builder-Content-Types für euer Hyvä-Theme?
Wir registrieren, rendern und stylen eigene Hyvä Page-Builder-Content-Types, migrieren bestehende Luma-Inhalte und sorgen für performantes, CSP-konformes Rendering ohne zusätzliches Bundle-JavaScript.
Content-Type-Entwicklung
Eigene Content-Types mit content_type.xml, phtml-Templates und Alpine.js registrieren
Page-Builder-Migration
Bestehende Luma-Inhalte analysieren und auf Hyvä-Rendering überführen
Hyvä-Frontend-Design
Tailwind-basiertes Styling und Performance-Optimierung für Content-Bausteine
10. Zusammenfassung
Eigene Hyvä Page-Builder-Content-Types lösen ein grundlegendes Architekturproblem: Sie ersetzen die clientseitige Knockout-Rendering-Kette aus Luma durch serverseitiges phtml-Rendering, das ohne zusätzliches Bundle-JavaScript auskommt und sich mit Tailwind-Klassen konsistent stylen lässt. Die Registrierung erfolgt deklarativ über content_type.xml und appearance.xml, das eigentliche Rendering übernimmt ein gewöhnliches phtml-Template mit escapten Feldwerten.
Interaktivität kommt über Alpine.js hinzu, das in Hyvä ohnehin global verfügbar ist, während Performance-Optimierungen sich vor allem auf Lazy Loading von Bildern und Slidern konzentrieren. Das Master-Format selbst bleibt beim Wechsel von Luma zu Hyvä unverändert in der Datenbank, weshalb eine Migration in erster Linie die Rendering-Schicht betrifft, nicht die gespeicherten Inhalte.
Wer Hyvä Page-Builder-Content-Types von Anfang an konsequent nach diesen Patterns aufbaut, spart sich spätere Refactorings: Preview- und Frontend-Templates bleiben synchron, Style-Attribute werden kontrolliert statt roh übernommen, und jeder neue Content-Type fügt sich nahtlos in das bestehende Design-System des Themes ein.
Hyvä Page-Builder-Content-Types: Das Wichtigste auf einen Blick
Content-Type-Registrierung
content_type.xml und appearance.xml deklarieren Name, Appearances und Formularfelder, das template-Attribut verweist auf das phtml-Rendering-Template.
Master-Format-Rendering
Ein PHP-Renderer läuft den Structure-Tree serverseitig durch, statt Knockout-Widgets im Browser zu hydrieren. Style-Attribute werden kontrolliert dekodiert und gemappt.
Alpine.js-Interaktivität
Tabs, Akkordeons und ähnliche Interaktionen laufen über x-data, x-show und x-text, ohne zusätzliches Bundle-JavaScript zu laden.
Performance & Migration
Lazy Loading für Bilder und Slider, gemeinsame Design-Tokens für Preview und Frontend, systematischer Appearance-Abgleich vor dem Theme-Wechsel.