Modal Dialog Overview
The Hyvä Theme module makes using modal dialogs efficient by utilizing Alpine.js, Tailwind CSS 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" aria-haspopup="dialog">
<?= $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>
Modals in Extensions
In themes it is okay to use @click="show"
without arguments, if there is only a single modal on the page.
But if there is more than one modal on a page, or if the modal is part of a Magento extension, a unique modal name needs to be passed to show()
.
See Showing modal dialogs below for details.
Heredocs in templates break HTML minification
At the time of writing, the Magento2 HTML minification is not able to process Heredocs like the one used in the example above.
We generally recommend turning off HTML minification, as the benefits it provides are negligible, and it tends to cause more problems than it worth.
For reference, please see issue 32153 in the Magento 2 GitHub repository.
If you still would like to use modals with inline content, you can use regular double-quoted PHP strings, too.
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
.
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.
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()
.
Or, if you extracted the Alpine.js view model properties into a function:
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 calledis
+ dialog name +Open
.
How to use the PHP Modal view model
Instantiate the PHP view model like any other in your .phtml template:
/** @var \Hyva\Theme\ViewModel\Modal $modalViewModel */
$modalViewModel = $viewModels->require(\Hyva\Theme\ViewModel\Modal::class);
To create a modal instance, call createModal
on the PHP view model. More about how to configure the modal further below:
Don’t forget to echo out the modal once you configured it.
For example:
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" aria-haspopup="dialog">
<?= $escaper->escapeHtml(__('Show Modal')) ?>
</button>
</div>
Otherwise, you will have to call the show()
method with the dialog ref-name (see getDialogRefName
and withDialogRefName
).
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" aria-haspopup="dialog">
<?= $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 without any arguments:
<button @click="show" type="button" class="btn mt-40" aria-haspopup="dialog"><?= $escaper->escapeHtml(__('Show Modal')) ?></button>
However, 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" aria-haspopup="dialog"><?= $escaper->escapeHtml(__('Show Modal')) ?></button>
Since extensions can not rely on only a single modal being present on a page, always specify a unique modal name or use the getShowJs
PHP method.
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.
Specifying the element to focus when the modal is closed
Available since Hyvä 1.2.9 and 1.3.5
The modal library attempts to handle this accessibility feature automatically, but due to the dynamic and flexible nature of JavaScript, it is not possible in some instances.
A DOM element selector string or a DOM element can be passed as a second argument to the show()
JS method, or as the single argument to the getShowJs
PHP method.
In JavaScript:
<button @click="show('modal2', '#dom-selector')"
type="button"
class="btn mt-40"
aria-haspopup="dialog"><?= $escaper->escapeHtml(__('Show Modal')) ?></button>
In PHP:
<button @click="<?= $escaper->escapeHtmlAttr($modal->getShowJs('#dom-selector')) ?>"
type="button" class="btn mt-40" aria-haspopup="dialog">
<?= $escaper->escapeHtml(__('Show Modal')) ?>
</button>
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.