Skip to main content
Fully semantic, zero-JavaScript modal dialogs using the native <dialog> element. Use commandfor and command="show-modal" attributes to open dialogs declaratively. Focus trapping, z-index management, and keyboard shortcuts all work out of the box.

Basic Usage

<button commandfor="demo-dialog" command="show-modal">Open dialog</button>

<dialog id="demo-dialog" closedby="any">
  <form method="dialog">
    <header>
      <h3>Title</h3>
      <p>This is a dialog description.</p>
    </header>
    <div>
      <p>Dialog content goes here. You can put any HTML inside.</p>
      <p>Click outside or press Escape to close.</p>
    </div>
    <footer>
      <button type="button" commandfor="demo-dialog" command="close" class="outline">
        Cancel
      </button>
      <button value="confirm">Confirm</button>
    </footer>
  </form>
</dialog>

Structure

Dialogs support a semantic structure with three main sections:
  • <header> - Title and optional description
  • <div>, <p>, or <section> - Main content area with auto-scroll
  • <footer> - Action buttons aligned to the right
These sections can be direct children of <dialog> or wrapped in a <form method="dialog">.

With Form Fields

Forms inside dialogs work naturally. Use method="dialog" to close the dialog on submit:
<button commandfor="demo-dialog-form" command="show-modal">Open form dialog</button>

<dialog id="demo-dialog-form">
  <form method="dialog">
    <header>
      <h3>Edit form</h3>
    </header>
    <div class="vstack">
      <label>Name <input name="name" required></label>
      <label>Email <input name="email" type="email"></label>
    </div>
    <footer>
      <button type="button" commandfor="demo-dialog-form" command="close" class="outline">
        Cancel
      </button>
      <button value="save">Save</button>
    </footer>
  </form>
</dialog>

Commands

Opening a Dialog

Use commandfor with command="show-modal" to open a dialog:
<button commandfor="my-dialog" command="show-modal">Open</button>

Closing a Dialog

Use command="close" to close from inside:
<button commandfor="my-dialog" command="close">Cancel</button>
Or use method="dialog" on a form:
<form method="dialog">
  <button>Close</button>
</form>

Handling Return Values

Buttons with a value attribute pass that value when closing:
<button value="confirm">Confirm</button>
<button value="cancel">Cancel</button>
Listen to the close event to get the return value:
const dialog = document.querySelector("#demo-dialog");
dialog.addEventListener('close', (e) => {
  console.log(dialog.returnValue); // "confirm" or "cancel"
});
Or use inline event handler:
<dialog id="my-dialog" onclose="console.log(this.returnValue)">
  <!-- dialog content -->
</dialog>

Closing Behavior

Control how the dialog can be closed with the closedby attribute:
<!-- Close by any method (click outside, Escape key, or buttons) -->
<dialog closedby="any">

<!-- Only close via buttons/programmatic methods -->
<dialog closedby="none">

<!-- Browser default (Escape key + buttons) -->
<dialog>

Styling

Dialogs include:
  • Centered positioning with responsive max-width (32rem)
  • Smooth scale and fade animations
  • Backdrop blur/overlay effect
  • Auto-scroll for content that exceeds viewport height (max 85vh)
  • Rounded corners and drop shadow
  • Proper z-index management

Opening Programmatically

While the commandfor attribute provides zero-JavaScript behavior, you can also open dialogs with JavaScript:
const dialog = document.querySelector('#my-dialog');
dialog.showModal(); // Opens as modal (with backdrop)
dialog.show();      // Opens as non-modal (no backdrop)

Accessibility

The native <dialog> element provides:
  • Automatic focus trapping
  • Escape key to close
  • Focus restoration when closed
  • Screen reader announcements
  • Proper ARIA semantics
Always include a way to close the dialog, either through a close button, form submission, or closedby="any" to allow clicking the backdrop.