Beyond the Generic Modal: Styling the HTML dialog Element

Dialogue boxes are a staple of web interfaces, yet they often end up looking like generic framework components. The HTML dialog element, however, offers more than just functional modals; with CSS, it provides a canvas for brand expression and creative storytelling.

By applying techniques like ::backdrop, backdrop-filter, and targeted animations, you can transform these interruptions into memorable design moments. To showcase this, let's explore a real-world scenario: building a themed dialogue box for a client with a deep appreciation for retro '90s animation.

The Basics of dialog and ::backdrop

Before diving into elaborate styling, it's helpful to recall the core mechanics of the dialog element. It is designed for both modal and non-modal boxes and comes with built-in behaviors like closing with the Esc key, focus trapping, and show/hide methods.

The markup is straightforward:

<dialog>
  <h2>Keep me informed</h2>
  <!-- ... -->
  <button>Close</button>
</dialog>

By default, the box is hidden. Adding the open attribute will display it on page load:

<dialog open>
  <h2>Keep me informed</h2>
  <!-- ... -->
  <button>Close</button>
</dialog>

Typically, you'll want to control it with a button and a bit of JavaScript.

<dialog>
  <!-- ... -->
</dialog>
<button>Keep me informed</button>
const dialog = document.querySelector("dialog");
const showButton = document.querySelector("dialog + button");
showButton.addEventListener("click", () => {
  dialog.showModal();
});
const closeButton = document.querySelector("dialog button");
closeButton.addEventListener("click", () => {
  dialog.close();
});

One notable exception to needing JavaScript for closing is when a form inside uses method="dialog", which closes the modal automatically upon submission:

<dialog>
  <form method="dialog">
    <button>Submit</button>
  </form>
</dialog>

While the dialog element is accessible out of the box, it's best practice to add an aria-labelledby attribute. This helps screen readers announce the dialogue box correctly when it opens by linking to its title.

<dialog aria-labelledby="dialog-title">
  <h2 id="dialog-title">Keep me informed</h2>
  <!-- ... -->
</dialog>

Case Study: A Stone Tablet and Jungle Leaves

To demonstrate the potential, consider a design where the navigation menu looks like an ancient stone tablet. It would be a missed opportunity if the dialogue boxes didn't follow suit. The goal was to create a newsletter signup modal that felt like a three-dimensional tablet, complete with a backdrop of jungle leaves.

<dialog>
  <h2>Keep me informed</h2>
  <form>
    <label for="email">Email address</label>
    <input type="email" id="email" required>
    <button>Submit</button>
  </form>
  <button>x</button>
</dialog>

The process begins by giving the dialog element specific dimensions and applying the stone tablet background image using an inline SVG.

dialog {
  width: 420px;
  height: 480px;
  background-color: transparent;
  background-image: url("dialog.svg");
  background-repeat: no-repeat;
  background-size: contain;
}

Next, the visual theme extends to the overlay. Using the ::backdrop pseudo-element, we can place the leafy background image behind the modal.

dialog::backdrop {
  background-image: url("backdrop.svg");
  background-size: cover;
}
Dialog sitting on top of a safari jungle pattern as the backdrop. The dialog is styled with a cartoon stone tablet illustration with an email signup form inside.
Mike Worth, designed by Andy Clarke, Stuff & Nonsense.

Adding Interaction and Feedback

Beyond static styling, we can use CSS to offer functional feedback. To reassure users they're entering a valid email, we can combine the :has and :valid pseudo-class selectors. This lets us change the submit button's color from grey to green when the input inside contains a valid email address.

dialog:has(input:valid) button {
  background-color: #7e8943;
  color: #fff;
}

We can go a step further to reflect a brand's personality. In this case, when a valid email is detected, the modal plays a rubberband animation and its background image switches. Daniel Eden's Animate.css library is an excellent resource for such easy-to-apply CSS animations.

dialog:has(input:valid) {
  background-image: url("dialog-valid.svg");
  animation: rubberBand 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

@keyframes rubberBand {
  from { transform: scale3d(1, 1, 1); }
  30% { transform: scale3d(1.25, 0.75, 1); }
  40% { transform: scale3d(0.75, 1.25, 1); }
  50% { transform: scale3d(1.15, 0.85, 1); }
  65% { transform: scale3d(0.95, 1.05, 1); }
  75% { transform: scale3d(1.05, 0.95, 1); }
  to { transform: scale3d(1, 1, 1); }
}

This same selector can reach the backdrop too, altering its background image to provide a rich, interactive experience that feels cohesive with the rest of the interface.

dialog:has(input:valid)::backdrop {
  background-image: url("backdrop-valid.svg");
}

The result is a dialogue box that is more than a simple functional alert; it is an active part of the site's visual identity.

In the end, a dialogue box is another opportunity to tell your product's story. With the dialog element's powerful styling options for both the box and its ::backdrop, there is no reason these components can't be as distinctive and on-brand as the rest of your design.