How to Style Dialogs with the CSS :modal Pseudo-Class

The CSS :modal pseudo-class allows developers to target and style elements that enter a modal state, during which interaction with the rest of the document is blocked. When applied to HTML <dialog> elements, :modal differentiates a dialog opened as an interactive, focus-trapping modal from one displayed as a standard, non-modal inline element. This article covers how the :modal pseudo-class works with the native <dialog> element, why it is necessary, and practical examples of its implementation.

The Distinction Between Modal and Non-Modal Dialogs

The native <dialog> element can be opened in two distinct ways using JavaScript:

  1. dialog.show(): Opens the dialog as a non-modal element. Users can still interact with the rest of the page, and the dialog behaves similarly to a standard positioned container.
  2. dialog.showModal(): Opens the dialog in the top layer of the browser. It restricts interaction to the dialog itself, traps focus, creates a default dark ::backdrop, and activates the :modal state.

Both methods apply the HTML open attribute to the <dialog> tag (e.g., <dialog open>), which means the CSS selector dialog[open] cannot distinguish between a modal and a non-modal dialog. The :modal pseudo-class solves this issue by matching the element only when it is displayed via showModal().

Why Use the :modal Pseudo-Class?

Using :modal prevents unwanted style bleeding between standard inline alerts and true overlay dialogs. It provides several functional advantages:

  • Conditional Visual Hierarchy: You can assign prominent drop shadows, centered fixed positions, or larger dimensions exclusively when the element takes over the screen.
  • Responsive Behavior: A dialog can function as an inline drawer or card on wide desktop screens using .show(), while switching to a full-screen overlay using .showModal() on mobile viewports.
  • Separation of Concerns: You avoid relying on custom JavaScript classes (such as .is-modal) to track state, leveraging native browser-level state detection instead.

Implementation Example

Consider an application with both inline popovers and full-screen prompt dialogs using the same HTML markup:

<dialog id="userDialog">
  <h2>Confirm Action</h2>
  <p>Are you sure you want to proceed?</p>
  <button id="closeBtn">Close</button>
</dialog>

Using CSS, you can define base styles for <dialog>, and then apply specific enhancements exclusively when opened modally:

/* Base styles applied whenever the dialog is open */
dialog[open] {
  padding: 1.5rem;
  border: 1px solid #ccc;
  border-radius: 8px;
}

/* Styles applied ONLY when opened via showModal() */
dialog:modal {
  max-width: 500px;
  width: 90%;
  border: 2px solid #0056b3;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
  margin: auto;
}

/* Styling the backdrop that appears exclusively with modal dialogs */
dialog:modal::backdrop {
  background-color: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(3px);
}

Browser Support

The :modal pseudo-class is part of the CSS Selectors Level 4 specification and is supported across all modern evergreen browsers, including Chrome, Edge, Firefox, and Safari. It works out of the box with the HTML <dialog> element without requiring polyfills or vendor prefixes.