Light-Dismiss a Dialog

Web features: <dialog closedby>

Modern modal dialogs often support "light-dismiss," allowing users to close a dialog by clicking or tapping the backdrop (the area outside the dialog). The closedby attribute provides a declarative way to enable this behavior without custom JavaScript.

Implementation

To enable light-dismiss:

  1. Add closedby="any" to the <dialog> element.
  2. Open the dialog using dialog.showModal().

Attribute Values

  • any: Enables light-dismiss (clicking the backdrop), "close requests" (the Esc key), and developer mechanisms (e.g., dialog.close()).
  • closerequest: Enables "close requests" and developer mechanisms only. This is the default for modal dialogs.
  • none: Only developer mechanisms can close the dialog.

Styling the Backdrop

When a dialog is opened as a modal using showModal(), the browser generates a ::backdrop pseudo-element. This backdrop covers the entire viewport and sits directly behind the dialog.

/* Style the backdrop to indicate the dialog is modal */
dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px); /* Optional: add blur for modern browsers */
}

Example

<!-- MANDATORY: Use closedby="any" to enable light-dismiss behavior -->
<dialog id="myDialog" closedby="any" aria-labelledby="dialogTitle">
  <form method="dialog">
    <h2 id="dialogTitle">Feedback</h2>
    <p>Click outside this box or press Esc to dismiss.</p>
    <button type="submit">Close</button>
  </form>
</dialog>

<button onclick="document.getElementById('myDialog').showModal()">Open Dialog</button>

Constraints & Accessibility

  • MANDATORY: Use closedby="any" to enable light-dismiss declaratively.
  • MANDATORY: Always open modal dialogs with showModal(). This ensures the dialog is in the top layer, focus is trapped, and the Esc key is handled.
  • DO: Ensure that there is always a close button visible to provide people with a clear, alternative way to close the modal dialog.
  • DO: Use aria-labelledby or aria-label to provide an accessible name for the dialog.
  • DO NOT: Use closedby for non-modal dialogs (opened with show()), as they do not have a backdrop and won't trigger light-dismiss.
  • DO NOT: Use the click event for critical logic that should happen before closing; instead, listen for the close or cancel events.

Ensure that there aren't components in the modal dialog that themselves use the Esc key for interaction, as this may lead to the dialog being inadvertently closed. If the modal dialog does include components that use the Esc key (e.g. a menu or a combobox), ensure that when these components are focused, they capture the Esc key press and use it accordingly, and that the dialog is not closed accidentally.

Fallback strategies

MANDATORY: For browsers that do not yet support closedby, you must implement a fallback for light-dismiss by checking if a click occurred outside the dialog content's boundaries using the following script:

const dialog = document.querySelector('dialog');

// Fallback for browsers without closedby support
if (!('closedBy' in HTMLDialogElement.prototype)) {
  dialog.addEventListener('click', (event) => {
    // 1. When clicking the backdrop, the event target is the dialog element itself.
    // Ignore clicks where the target is a child element inside the dialog.
    if (event.target !== dialog) return;

    // 2. Check if the click coordinates fall within the dialog's content box.
    // This distinguishes between a click on the backdrop vs a click on the dialog's background/padding.
    const rect = dialog.getBoundingClientRect();
    const isDialogContent = (
      rect.top <= event.clientY &&
      event.clientY <= rect.top + rect.height &&
      rect.left <= event.clientX &&
      event.clientX <= rect.left + rect.width
    );

    if (isDialogContent) return;

    // 3. Since the click was outside the content area (on the backdrop), manually close the dialog.
    dialog.close();
  });
}