30 Aug 2026 · 3 min read
Hyva architecture: the three pillars in practice
Inside a Hyva theme — Tailwind CSS, Alpine.js, and PHP view models — and how the three pillars replace Knockout, RequireJS, and client-side view models without touching your backend.
Decision brief first, then the engineering view. Part 4 of the Hyva series — a rewrite of the earlier deep-dive post to fit the series arc.
Decision brief: what you are signing up to build
Adopting Hyva means rebuilding your storefront's presentation layer on three technologies: Tailwind CSS, Alpine.js, and PHP view models. Your PHP backend, modules, admin, and database stay exactly as they are. There is no new application server, no GraphQL contract to design, no second deploy pipeline.
For a developer who already knows Magento, the learning curve is short: Tailwind is utility classes, Alpine is a few x-* attributes, and view models are plain PHP. The rebuild is mostly translation of existing Luma templates, not new architecture. That bounded scope is why Hyva migrations are predictable in a way headless rebuilds are not.
The three pillars
Tailwind CSS instead of Less
Luma themes compile a large Less bundle through deep theme inheritance. Hyva compiles a single Tailwind stylesheet. You write utility classes in markup (flex items-center gap-4) and the build strips everything unused. The result is a small, deterministic CSS file — tens of kilobytes instead of hundreds.
A Hyva theme's tailwind.config.js and tailwind.source.css define the design tokens and the content globs. Change a color token once; it propagates everywhere. There is no "theme override cascade" to reason about.
Alpine.js instead of Knockout
Knockout binds a uiElement view model to the DOM and maintains a binding engine. Alpine declares behavior inline:
<div x-data="{ open: false }">
<button x-on:click="open = !open">Menu</button>
<ul x-show="open">…</ul>
</div>
State lives in the markup, initialized lazily, and torn down with the element. There is no global binding registry and no polling loop. For the interactive widgets Luma expressed in Knockout (minicart, modals, layered nav), Alpine is usually a few attributes plus a small script.
PHP view models instead of JS view models
Where Luma reached for Knockout to compute display state, Hyva reaches for PHP. A view model is a class implementing Hyva\Theme\ViewModel\ViewModelInterface:
class ProductBadge implements ViewModelInterface
{
public function __construct(private Stock $stock) {}
public function isLowStock(Product $product): bool
{
return $this->stock->getQty($product) <= $product->getMinQty();
}
}
Templates receive it via layout XML and call methods. Business logic stays in testable PHP; the template stays declarative. This is the pillar that most reduces long-term maintenance cost — the logic you would have debugged in the browser now lives where your test suite can reach it.
What actually changes in a module
Most Hyva work is swapping a PHTML template and, where needed, a Tailwind stylesheet. The PHP block behind the template is usually untouched. A typical compatibility change is:
- Add a
view/frontend/templatesoverride that uses Tailwind classes instead of Luma markup. - Provide any Alpine behavior inline or via a small
x-datacomponent. - Move display logic into a view model if it would otherwise live in the template.
Third-party modules that only inject JavaScript often need a Hyva compatibility module — a topic covered later in this series.
Performance payoff, concretely
The win is subtraction, not addition:
- No RequireJS bundle to download and evaluate.
- No Knockout binding engine to boot on every page.
- No
customer-datasection polling on every navigation. - One CSS file, one tiny JS file, predictable critical path.
Catalog and product pages — where Luma's Knockout graph is heaviest and where organic traffic lands — see the largest improvement. This is exactly why the performance-best-practices post in the Magento series ranks Hyva as a Medium–High win.
Where to go next
With the pillars understood, the next posts build them up concretely: setting up a Tailwind theme, overriding layout XML and PHTML, writing Alpine patterns, and moving logic into view models. Start with the theme and build pipeline.
Next in this series: Setting up a Hyva child theme and Tailwind pipeline —
/blog/magento2-hyva-theme-setup-tailwind