Tip of the week #018: Create modals and dialogs with the <dialog> element
A native way to create modals and dialogs, accessible and keyboard-friendly, using only HTML and a tiny bit of Javascript.
The <dialog>
element is a nifty little thing. It's a relatively new HTML element, but it already has 96.66% browser support according to caniuse.com (at the time of writing), and its Baseline status is "Widely available":
<dialog>
Baseline Widely available
Supported in Chrome: yes.
Supported in Edge: yes.
Supported in Firefox: yes.
Supported in Safari: yes.
This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2022
It's a pain to create dialogs in the first place. At least if you want to make it keyboard accessible. With <dialog>
all of this is built in.
This is what the code looks like:
<dialog>
<p>Come on!</p>
</dialog>
How to use it
You need to add a bit more than just the <dialog>
to make it work. I suggest that you read the documentation for it (developer.mozilla.org), but here's a quick summary:
- Add the
<dialog>
element and content to it (e.g. a<p>
-tag) - Add a
<button>
inside. This will be our close button. - Add a
<button>
outside. This will be our open button. - Add a tiny bit of Javascript
Here's an example:
<dialog id="myDialog">
<button id="closeButton" autofocus>Close</button>
<p>Come on!</p>
</dialog>
<button id="showButton">Show</button>
<script>
const dialog = document.getElementById("myDialog");
const showButton = document.getElementById("showButton");
const closeButton = document.getElementById("closeButton");
showButton.addEventListener("click", () => {
dialog.showModal();
});
closeButton.addEventListener("click", () => {
dialog.close();
});
</script>
How to style it
You can style it just as you'd normally style other elements, but you should be aware of some things:
- It comes with some default styling (see the code block below)
- When the
<dialog>
is shown, it gets an attribute calledopen
, which then styles the dialog fromdisplay: none
todisplay: block
. If you want to change the display value of your dialog (e.g. toflex
orgrid
), be sure to use the attribute selectordialog[open]
to do so, otherwise it will be visible at all times. - You can style the backdrop (the dark shadowy thing that's behind the dialog) using the
::backdrop
psuedo selector - You don't have to worry about
z-index
. When opened, gets placed in thetop-layer
, safely above everything.
Default styling
The user agent stylesheet in Chrome looks like this:
dialog:modal {
overlay: auto !important;
}
dialog:-internal-dialog-in-top-layer {
position: fixed;
inset-block-start: 0px;
inset-block-end: 0px;
max-width: calc(100% - 2em - 6px);
max-height: calc(100% - 2em - 6px);
user-select: text;
visibility: visible;
overflow: auto;
}
dialog[open] {
display: block;
}
dialog {
display: none;
position: absolute;
inset-inline-start: 0px;
inset-inline-end: 0px;
width: fit-content;
height: fit-content;
background-color: canvas;
color: canvastext;
margin: auto;
border-width: initial;
border-style: solid;
border-color: initial;
border-image: initial;
padding: 1em;
}
dialog:-internal-dialog-in-top-layer::backdrop {
position: fixed;
inset: 0px;
background: rgba(0, 0, 0, 0.1);
}
Accessibility and keyboard navigation
The biggest benefits of using this element is that it's accessible and has keyboard navigation by default. You can press ESC
on your keyboard to close it. Also, when using TAB
all elements outside the <dialog>
becomes inert, meaning that they're not going to get focus. This is a good thing, as it prevents the user to focus something beneath the dialog. Just be sure to add a close button within the dialog with clear labelling, so that users have a way to back out of the dialog.
Demo
Here's a Codepen demo of the element in use. Note the red backdrop styling.