#TIL
Today I learned that HTML has a native modal, known as the <dialog>
element.
The HTML element represents a dialog box or other interactive component, such as a dismissible alert, inspector, or subwindow.
AN HTML POP UP? NO WAY! (yes way).
As always, I demonstrate the usage in React.js, which means that some functionality translates directly, and other functionality does not. For example, according to the docs:
The ::backdrop CSS pseudo-element can be used to style behind a element when the dialog is displayed with HTMLDialogElement.showModal(). For example, to dim unreachable content behind the modal dialog.
As you will see in a moment, using a <dialog>
in React requires a little bit of a different usage to dim the background. But, truthfully I think it is still easier than with vanilla JS.
The Modal
Looks great, doesn't it? And you will see in just a moment that the necessary code is relatively simple!
The Code
import { useState } from "react";
import "./styles.css";
export default function App() {
const [isOpen, setIsOpen] = useState(false);
const openDialog = () => {
setIsOpen(true);
};
const closeDialog = () => {
setIsOpen(false);
};
return (
<div className="App">
<h1>HTMLs Native Dialog</h1>
<h2>A simple modal</h2>
{isOpen && (
<>
<div className="overlay" />
<dialog open>
<p>BOOM</p>
<p>And just like that youve got a modal</p>
<p>
Easy peezy lemon squeezy{" "}
<span role="img" aria-label="lemon emojis">
๐๐๐
</span>
</p>
<button onClick={closeDialog}>Close</button>
</dialog>
</>
)}
<button className="open-btn" onClick={openDialog}>
Open Dialog
</button>
</div>
);
}
Breaking it Down
The open or closed state of the modal is handled by the useState
hook provided by React and toggled by the openDialog()
and closeDialog()
functions.
import { useState } from "react";
...
const [isOpen, setIsOpen] = useState(false);
const openDialog = () => {
setIsOpen(true);
};
const closeDialog = () => {
setIsOpen(false);
};
The HTML for a <dialog>
element looks something like:
<dialog open>
<p>BOOM</p>
<p>And just like that youve got a modal</p>
<p>
Easy peezy lemon squeezy{" "}
<span role="img" aria-label="lemon emojis">
๐๐๐
</span>
</p>
<button onClick={closeDialog}>Close</button>
</dialog>
The most import part of the above code is the open
property in the opening <dialog>
tag, written as <dialog open>
. That's how the browser knows to display the modal.
And the full return
statement with the conditional render handled by the isOpen
property in state.
return (
<div className="App">
<h1>HTMLs Native Dialog</h1>
<h2>A simple modal</h2>
{isOpen && (
<>
<div className="overlay" />
<dialog open>
<p>BOOM</p>
<p>And just like that youve got a modal</p>
<p>
Easy peezy lemon squeezy{" "}
<span role="img" aria-label="lemon emojis">
๐๐๐
</span>
</p>
<button onClick={closeDialog}>Close</button>
</dialog>
</>
)}
<button className="open-btn" onClick={openDialog}>
Open Dialog
</button>
</div>
);
Why Should I Care?
The cool and most important thing about the <dialog>
element is better accessibility. Sure, you can build your own modal, but then you've got to work extra hard to make it available to screen readers and other accessibility tools.
Another cool benefit is not having to add a bunch of z-index
properties to your CSS in order to properly display the modal and any overlays you wish to add. "Modal behaviour" is baked right into the <dialog>
element.
Added accessibility AND easier styling capabilities? Sounds like a WIN-WIN to me!
The CSS
Curious how I personally styled my modal and overlay? Here is the full CSS file:
.App {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
color: white;
background-color: rgb(0, 0, 0);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
}
body {
background-color: black;
}
dialog {
margin-top: 8rem;
width: 75%;
color: white;
background-color: black;
border: 3px solid rgb(72, 70, 224);
}
button {
background-color: rgb(72, 70, 224);
color: white;
border: none;
padding: 0.5rem;
border-radius: 0.5rem;
cursor: pointer;
font-size: 1rem;
font-weight: bold;
}
button:hover {
background-color: rgb(66, 247, 207);
color: black;
}
.open-btn {
width: 75%;
}
.overlay {
position: fixed;
margin: 0;
top: 0;
width: 100%;
height: 100vh;
background-color: rgba(9, 22, 39, 0.7);
}
@media only screen and (min-width: 500px) {
dialog {
width: 18rem;
}
.open-btn {
width: 10rem;
}
}
The Overlay
The only thing I added that that doesn't come "baked in" was the overlay. I got creative with my solution, but I would not call it "difficult" or "complex". Inside my conditional render of the <dialog>
element I added <div className="overlay" />
. And simply styled with CSS:
.overlay {
position: fixed;
margin: 0;
top: 0;
width: 100%;
height: 100vh;
background-color: rgba(9, 22, 39, 0.7);
}
Think of it as stretching a layer of slightly-transparent color across the entire screen when isOpen
is true
.
Conclusion
Check out this great Shopify article that features this and other useful HTML native elements. The <dialog>
element is number 6 in the article and I really liked tihs part:
Does it work?
The does a slightly more complex thingโฆand does it well. Browser support is somewhat patchy (notably no Internet Explorer, and Safari is pending at the time of writing), but there is a polyfill.Is it accessible?
Support is quite good, but it does need a little ARIA support to go to production. What's really good about the element is that most accessibility support is built in, making it a far better starting point than having to create your own fixed inline custom dialog component.
I hope you enjoyed my article on this awesome HTML element! As always let me know if you have any questions, comments, feedback, suggestions, etc!
Thanks again and see you next time!
Update
It appears that the <dialog>
element does not behave as intended on iOS. At least not on mobile iOS. I am going to do some further digging and update this article with the relevant information as soon as possible!
Top comments (2)
AFAIK the element is not supported by all browsers, e.g. Firefox. There's a polfill, though.
Thanks for your comment. I hinted at this in my article this was the case in the โupdateโ section, before I knew what the actual issue was. But yes, lack of browser support is the culprit and polyfills are the answer. I just have to learn about polyfills. Iโll make sure to update the update section to clarify. Thanks!