Skip to content

Modal Dialog Overview

The Hyvä Theme module makes using modal dialogs efficient by utilizing Alpine.js, TailwindCSS and PHP View Models.

Available since 1.1.6

Modals are available since version 1.1.6 of Hyva_Theme. If you are using an older version, be sure to upgrade the theme module.
New features have been added to the dialog library over time. This documentation will always note the version a feature was released.
If you use the modal library in modules, be sure to record your theme version dependency accordingly in the module composer.json file.

The modals can be used with or without the view model Hyva\Theme\ViewModel\Modal.

Since the PHP view model makes creating modal dialogs more convenient, we assume it will be the default.

For more information how to use the modals without the PHP view model, please refer to this page of this section.

TL;DR

Here is a 10-minute video recording of a short demo how to use the modal.

Look below for example code.

Here is an example to get you started right now:

<?php

/** @var \Magento\Framework\Escaper $escaper */
/** @var \Hyva\Theme\Model\ViewModelRegistry $viewModels */
/** @var \Hyva\Theme\ViewModel\Modal $modalViewModel */
$modalViewModel = $viewModels->require(\Hyva\Theme\ViewModel\Modal::class);

?>
<div x-data="hyva.modal()">
    <button @click="show" type="button" class="btn mt-40"><?= $escaper->escapeHtml(__('Show')) ?></button>
<?= $modalViewModel->createModal()->withContent(<<<END_OF_CONTENT

<div id="the-label">{$escaper->escapeHtml(__('My Dialog'))}</div>
<div class="mt-20 flex justify-between gap-2">
    <button @click="hide" type="button" class="btn">
        {$escaper->escapeHtml(__('Cancel'))}
    </button>
    <button x-focus-first @click="alert('beep')" type="button" class="btn btn-primary">
        {$escaper->escapeHtml(__('I agree'))}
    </button>
</div>

END_OF_CONTENT
)->positionBottom()
 ->withAriaLabelledby('the-label')
 ->addDialogClass('border', 'border-10', 'border-blue-800')
?>
</div>

How to use

Include the modal logic

In order to use the modals, the JavaScript needs to be loaded in the page. This is done by including the layout XML handle hyva_modal.

<page>
    <update handle="hyva_modal"/>
</page>

Merge hyva.modal() with your code

Hyvä modal dialogs use Alpine.js.

If you only are interested in a modal and do not require any custom code, you can use hyva.modal() directly as your Alpine.js view model.

<div x-data="hyva.modal()">

</div>

If you also want to use custom state or logic, you will need to merge it with the modal Alpine.js view model using the object spread operator or Object.assign().

<div x-data="{...hyva.modal(), counter: 0}">

</div>

Or, if you extracted the Alpine.js view model properties into a function:

<div x-data="{...hyva.modal(), ...myViewModelFunction()}">

</div>

The object returned by hyva.modal() has four properties.

Be sure not to overwrite them with your custom code.

  • show()
    The function to show the modal dialog.

  • hide()
    The function to hide the modal dialog.

  • overlay()
    The Alpine.js properties for the overlay DOM element.
    If you use the PHP view model you don't need to know more.

  • isDialogOpen The state property storing if the modal is visible (true) or hidden (false).
    The property name actually might be different, depending on the dialog name.
    However, it will always be called is + dialog name + Open.

How to use the PHP Modal view model

Instantiate the PHP view model like any other in your .phtml template:

/** @varHyvaThemeViewModelModal $modalViewModel */
$modalViewModel = $viewModels->require(HyvaThemeViewModelModal::class);

To create a modal instance, call createModal on the PHP view model. More about how to configure the modal further below:

$modal = $modalViewModel->createModal()

Don’t forget to echo out the modal once you configured it.

For example:

<?= /** @noEscape */ $modal->withContent('Foo') ?>

The easiest way to render the modal is when the contents are nested inside the node containing the x-data="hyva.modal()" attribute.

<div x-data="hyva.modal()">
    <?= $modalViewModel->createModal()->withContent('<h1>Test</h1>') ?>
    <button @click="show" type="button" class="btn mt-40">
        <?= $escaper->escapeHtml(__('Show Modal')) ?>
    </button>
</div>

Otherwise, you will have to call the show() method with the dialog ref-name (see setDialogRefName and getDialogRefName below).

Showing modal dialogs

Showing the modal usually requires some user interaction, most commonly a click.
If you use the PHP Modal view model, you can have the modal instance generate the required code with the getShowJs method.

<button @click="<?= $escaper->escapeHtmlAttr($modal->getShowJs()) ?>"
        type="button" class="btn mt-40">
    <?= $escaper->escapeHtml(__('Show Modal')) ?>
</button>
Under the hood

The getShowJs() method returns the JavaScript code show('dialog', $event).
The first argument is the automatically generated name for the modal instance.
Generating the name makes it easier, since each dialog on the page needs a unique name.

The $event argument is used to determine the element that was used to trigger the action so it can be focused after the modal is closed. This is important for accessibility and keyboard navigation.

If you don't use the PHP view model, you can call the show method in an event handler directly:

<button @click="show" type="button" class="btn mt-40"><?= $escaper->escapeHtml(__('Show Modal')) ?></button>

If multiple modals within one Alpine.js view model are used, the modal name needs to be passed as the first argument.

<button @click="show('modal2', $event)" type="button" class="btn mt-40"><?= $escaper->escapeHtml(__('Show Modal')) ?></button>

If you use the PHP Modal view model method getShowJs, generating a unique name and passing it as the argument is taken care of automatically.

Hiding modal dialogs

with JavaScript

To hide the most recent modal dialog call the hide method of the Alpine.js view model.

<button @click="hide" type="button" class="btn mt-40"><?= $escaper->escapeHtml(__('Cancel')) ?></button>

If multiple modals are visible, modals will be hidden in the reverse order they were displayed.

Imagine them as a stack of dialogs where hide pops the top modal off the stack.

The top modal can be hidden from outside the Alpine.js view model that contains the dialog with the global function window.hyva.modal.pop(). This can be handy in event observers or other callback methods.

with the escape key

Pressing the Escape key automatically hides the last modal.

Be sure to include at least one hide trigger besides the Escape key for mobile users.

by click-away

For modals with a backdrop, clicking outside the dialog will automatically hide the modal. Be sure to include at least one hide trigger besides for mobile users where the dialog might take up all the available screen.

Configuring a modal

The PHP Modal view model uses a fluent interface to configure the dialog.

/** @var \Hyva\Theme\ViewModel\Modal $modalViewModel */
$modal = $modalViewModel->createModal()
    ->withTemplate('My_Module::dialog-content.phtml')
    ->overlayDisabled()
    ->positionBottom()
    ->addDialogClass('pb-10')
    ->initiallyVisible();

Be sure to type hint the $modalViewModel so your IDE support you with auto-completion for the available method names.

Please refer to the Modal API Reference for more information.