A Modern HTML Solution Native Modals with <dialog>

I’ve decided to do a series on HTML tags that are off the beaten path. I’ll start with something that almost any site ends up needing after marketing gets involved: modals.

For years, creating modals (aka dialogs, popups, or overlays) has meant reaching for JavaScript libraries, ARIA roles, and custom accessibility code. But HTML has finally caught up. Thanks to the <dialog> element, we now have a native, semantic, and accessible way to create modals — no third-party dependencies required.

In this post, we’ll walk through how the <dialog> tag works, how to style it, and how to control it with JavaScript.

What is <dialog>?

The <dialog> element is a native HTML component introduced in the HTML Living Standard. It provides a built-in modal or popup dialog box with optional accessibility features and JavaScript methods for control.

It supports:

  • Native modal behavior (blocking interaction outside the dialog)
  • Built-in accessibility roles (automatically role="dialog")
  • JavaScript control via .showModal(), .close(), and .returnValue

Best of all, it works in all modern browsers (Chrome, Edge, Firefox, Safari, etc.).

<button id="openBtn">Open Dialog</button>

<dialog id="myDialog">
  <h2>Hello 👋</h2>
  <p>This is a native HTML dialog box.</p>
  <button id="closeBtn">Close</button>
</dialog>

<script>
  const dialog = document.getElementById('myDialog');
  document.getElementById('openBtn').addEventListener('click', () => dialog.showModal());
  document.getElementById('closeBtn').addEventListener('click', () => dialog.close());
</script>

Styling

The <dialog> element can be styled with CSS just like any other element. Here’s an example to make it look more like a modern modal:

dialog {
  border: none;
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: 0 4px 20px rgba(0,0,0,0.3);
  width: 90%;
  max-width: 400px;
}

dialog::backdrop {
  background: rgba(0, 0, 0, 0.6);
}

Notice we now have a ::backdrop pseudo element that handles the overlay, so we no longer have to absolutely position the ::before to create the overlay layer. You can also add entrance/exit animations with a bit of CSS and JS, though you’ll need to manage animation + close() behavior carefully.

How does it handle accessibility?

The <dialog> element comes with built-in accessibility features, including the role="dialog" and automatic keyboard focus trapping when opened using .showModal(). It also supports closing the dialog with the Escape key by default. However, to ensure an optimal experience, especially for screen reader and keyboard users, it’s a good practice to set focus to the first actionable element inside the dialog when it opens. For example, if your dialog has a close button, you can programmatically focus it like this:

const dialog = document.getElementById('myDialog');
const openBtn = document.getElementById('openBtn');
const closeBtn = document.getElementById('closeBtn');

openBtn.addEventListener('click', () => {
  dialog.showModal();
  closeBtn.focus(); // Move focus to the close button
});

This helps users navigate the dialog more easily and reinforces that it has appeared visually and functionally. While most assistive technologies handle <dialog> well, setting focus manually gives you a more consistent and polished user experience.

Summary

The <dialog> tag is a powerful, native tool for building modals in modern web development:

  • No libraries or ARIA gymnastics required
  • Built-in keyboard and accessibility support
  • Easy to use and customize
  • Works great with forms

If you’re building a modal in 2025, consider starting with <dialog> first. It might just be all you need.