Modal Dialogs
Since Hyvä 1.5.0, modal dialogs are built on the native HTML <dialog> element, powered by the x-htmldialog Alpine plugin. The browser now handles focus trapping, Escape key handling, and backdrop click natively.
There are two ways to create a modal dialog:
x-htmldialogdirectly (recommended): Write a<dialog>element with thex-htmldialogdirective. Works anywhere Alpine.js is available, including CMS content.- PHP view model: The
Hyva\Theme\ViewModel\Modalclass generates the markup from PHP. Useful when you need to configure the dialog server-side from a.phtmltemplate.
Alpine.js (x-htmldialog)
The recommended way to create a modal is to use the x-htmldialog directive directly on a native <dialog> element:
<div x-data="{ open: false }">
<button @click="open = true" type="button" class="btn">Open</button>
<dialog x-show="open" x-htmldialog="open = false">
<p>Dialog content here.</p>
<button @click="open = false" type="button" class="btn">Close</button>
</dialog>
</div>
For full usage details, modifiers, and options, refer to the x-htmldialog plugin documentation.
PHP View Model
Setup
Include the hyva_modal layout handle to load the required JavaScript on the page:
Instantiate the view model in your .phtml template:
<?php
use Hyva\Theme\ViewModel\Modal;
/** @var Modal $modalViewModel */
$modalViewModel = $viewModels->require(Modal::class);
Quick start
<?php
use Hyva\Theme\ViewModel\Modal;
/** @var \Magento\Framework\Escaper $escaper */
/** @var Modal $modalViewModel */
$modal = $modalViewModel->createModal()
->withAriaLabel('My Dialog')
->positionBottom();
?>
<div x-data="hyvaModal">
<button
type="button"
class="btn"
@click="<?= $escaper->escapeHtmlAttr($modal->getShowJs()) ?>"
>
<?= $escaper->escapeHtml(__('Open')) ?>
</button>
<?= $modal->withContent(<<<END_OF_CONTENT
<p class="text-lg font-medium">{$escaper->escapeHtml(__('My Dialog'))}</p>
<div class="mt-6 flex justify-end gap-2">
<button @click="hide" type="button" class="btn">
{$escaper->escapeHtml(__('Cancel'))}
</button>
<button type="button" class="btn btn-primary">
{$escaper->escapeHtml(__('Confirm'))}
</button>
</div>
END_OF_CONTENT) ?>
</div>
Heredocs in templates break HTML minification
Magento's built-in HTML minification cannot process heredoc expressions. We recommend disabling minification. If that is not an option, assign the heredoc to a variable first.
Showing a modal
Use getShowJs() on the modal instance to generate the correct click handler. This ensures each modal gets a unique name and that the triggering element receives focus back when the dialog closes.
<button
type="button"
class="btn"
@click="<?= $escaper->escapeHtmlAttr($modal->getShowJs()) ?>"
>
<?= $escaper->escapeHtml(__('Open')) ?>
</button>
Modals in extensions
Always use getShowJs() in extensions rather than @click="show" without arguments. Extensions cannot assume only one modal is present on the page.
Closing a modal
The browser closes the dialog automatically when the user presses the Escape key or clicks the backdrop. Use the withCloseby() method to change this behaviour (see API reference).
To close the modal from a button inside the dialog, call hide:
<button type="button" class="btn" @click="hide" >
<?= $escaper->escapeHtml(__('Close')) ?>
</button>
Configuring a modal
The view model uses a fluent interface. Chain methods to configure positioning, content, styling, and accessibility:
<?php
$modal = $modalViewModel->createModal()
->withTemplate('My_Module::dialog-content.phtml')
->positionBottom()
->overlayDisabled()
->addDialogClass('pb-10')
->withAriaLabel('My dialog');
For the full list of available methods, refer to the Modal ViewModel API Reference.
Legacy Modal System (pre-1.5.0)
The modal system was rebuilt in Hyvä 1.5.0. Projects that cannot migrate immediately can install a compatibility module to restore the old hyva.modal() behaviour. See Legacy Modal System for installation instructions.