Understanding Existing Alpine Components in Hyvä: Mobile Menu and Mini Cart as Learning Examples
Understanding Existing Alpine Components in Hyvä: Mobile Menu and Mini Cart as Learning Examples
~8 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026
The fastest way to learn a new pattern is often to read an existing, working example. Two components that show up in practically every Hyvä theme and combine all the fundamentals from chapter 14: the mobile menu and the mini cart.
The mobile menu as a learning example
A typical mobile menu consists of a toggle button and a panel that gets shown and hidden - exactly the pattern from chapter 14, just with a few extra details for accessibility and body scroll locking:
<div x-data="{ mobileMenuOpen: false }">
<button
type="button"
@click="mobileMenuOpen = !mobileMenuOpen"
:aria-expanded="mobileMenuOpen"
aria-label="Open menu"
class="lg:hidden"
>
<span x-show="!mobileMenuOpen">☰</span>
<span x-show="mobileMenuOpen">✕</span>
</button>
<nav
x-show="mobileMenuOpen"
x-cloak
@click.outside="mobileMenuOpen = false"
class="fixed inset-0 z-40 bg-white p-6"
>
<!-- menu items -->
</nav>
</div>New details compared to chapter 14: :aria-expanded (shorthand for x-bind:aria-expanded) dynamically binds an HTML attribute to an Alpine value - important for screen readers. @click.outside closes the menu as soon as a click happens anywhere outside it. x-cloak prevents a brief flash of the unstyled element before Alpine has loaded (via the CSS rule [x-cloak] { display: none; }, already included in Hyvä's base CSS).
The mini cart as a learning example
The mini cart demonstrates another important pattern: state that doesn't originate purely client-side, but comes from a server-side data source. The initial cart contents are already supplied as a PHP array (via a ViewModel) when the page loads, and embedded as JSON inside x-data:
<?php
/** @var \Magento\Framework\Escaper $escaper */
/** @var \Magento\Framework\Serialize\Serializer\Json $json */
?>
<div x-data="{ items: <?= /* @noEscape */ $json->serialize($cartViewModel->getCartData()) ?> }">
<span x-text="items.length"></span>
</div>This pattern - embedding server-rendered initial data as JSON inside x-data, then working with it client-side from there - is the standard way to feed Alpine with "real" Magento data, without needing an extra AJAX request on the initial page load.
Achtung: JSON embedded in an x-data attribute must be produced by a real JSON serializer (e.g. \Magento\Framework\Serialize\Serializer\Json), not by manual string concatenation - otherwise you risk broken JSON or XSS holes via unescaped quotation marks.
What we take from this for the team page
For our own team page starting in chapter 17, we'll use a very similar pattern: team member data comes from the ViewModel, gets embedded as JSON in an x-data attribute, and category filtering then happens entirely client-side in Alpine - with no page reload and no extra server request.