The <dialog>
element in HTML is tremendous. We’ve got support across the board now, so using it is a smart plan. Just with basic usage, you get a centered modal dialog experience that comes up when you call it, a dimmed background, focus trapped within it, closes with the ESC key, and focus returning where it came from. You can style it all entirely predictably in CSS. Those things range from a little bit of a pain to downright hard to pull off if left to our own implementations. Now we get them all for free as they say.
You don’t automatically get a close button, so here’s a basic implementation that adds that.
<button class="show-dialog-button">Show Dialog</button>
<dialog id="dialog">
<div class="dialog-title">
Hi, I'm a dialog.
</div>
<button aria-label="Close Dialog" class="close-dialog-button">
<svg ...>
</button>
</dialog>
Code language: HTML, XML (xml)
Now we need two click handlers, one for opening and one for closing.
const showDialogButton = document.querySelector(".show-dialog-button");
const closeDialogButton = document.querySelector(".close-dialog-button");
const dialog = document.querySelector("dialog");
showDialogButton.addEventListener("click", () => {
dialog.showModal();
});
closeDialogButton.addEventListener("click", () => {
dialog.close();
});
Code language: JavaScript (javascript)
I think the naming of things here almost qualifies as a gotcha. So it’s showModal()
eh? Why not showDialog()
, since, ya know, it’s for the <dialog>
element? So to close it it must be closeDialog()
or hideDialog()
surely, right? No. Just close()
. Oh well I’m sure that was bikeshedded to death and there are probably ✨ reasons.
That code above is it really, that’s largely functional. And that, friends, is extremely cool. Here’s a quick live demo:
Here’s another little gotcha! Where do you want to position that close button?
I would think probably in the top right or top left of the dialog. So you might…
dialog {
position: relative;
.close-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
}
}
Code language: CSS (css)
Danger danger!
By setting the <dialog>
to position: relative;
, something we naturally do when I’m thinking about using position: absolute;
on a child element, we’ve introduced a potentially gnarly UX bug. Desktop browsers will now scroll the page all the way to the top when the dialog is opened, and you’ll see the dialog open and centered in the viewport. But on iOS, the window will not scroll up. This leads to a situation where you can open the dialog and… not see it at all.
The problem there is that, by default, the <dialog>
is position: fixed;
, which means it would show up no matter where the page is scrolled to (even without force-scrolling to the top). But we’ve (ok ok, I’ve) accidentally overridden it with relative
creating this unexpected behavior.
It’s a little tempting to look into locking the scroll position while the dialog is open, perhaps with a simple :has()
-based selector, but I’m not sure how much I care if the page can scroll while it’s open.
There is other little gotcha’s to think about as well, like the fact that content within dialogs are not find-on-page-able until opened. Which kinda makes sense, but content within details elements are, so it’s just something you need to know about and probably not accidentally hide content within you want available always. I’d listen to Scott O’Hara, myself.
If you want to see a <dialog>
in production use, look no further. Posts right here on Boost have a “Take Quiz” button the sidebar that opens a dialog with a custom Web Component that the Frontend Masters team have built to help build you a custom course path.
Any other gotchas you’ve found with the dialog element?
Oh hey ya know what, if someone were to make like 8-10 really cool designs for the <dialog>
and the ::backdrop
, that would make for a pretty sweet guest post, I’d say.
The method showModal() should not have been named showDialog() because the word “modal” in showModal() is an adjective (as in “a modal dialog”) meaning all interaction with content outside the dialog should be prevented.
The show() method, by contrast, will display the dialog non-modally, meaning interaction with content outside the dialog is allowed.
I think the confusion comes in because many people use “modal” as a noun that’s just a synonym for “dialog.” (Or perhaps it’s merely a shortening of “modal dialog” to just “modal.”) This is confusing and should be avoided because, as described above, dialogs can be modal or non-modal.
Yeah you gotta pick something!
Maybe
.pleaseShowTheDialogElementInAModalState()
and.prettyPleaseNowShowTheElementInANonModalState()
would offer the ultimately clarity.There is a way to do HTML-only close buttons: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#html-only_dialog