Skip to content

Dialog

A styled native <dialog>. showModal() gives a browser-owned focus trap, Escape close, and inert background.

Are you sure you want to continue? This action cannot be undone.

Live example
<button
  type="button"
  class="re-button"
  id="open-modal"
  data-re-dialog-trigger
  data-re-dialog-target="modal"
>
  Open modal
</button>

<dialog
  class="re-dialog"
  id="modal"
  aria-labelledby="modal-title"
  data-re-dialog-close-on-backdrop
>
  <header class="re-dialog__header">
    <h2 class="re-dialog__title" id="modal-title">Confirm</h2>
    <button
      class="re-dialog__close"
      id="modal-close-x"
      aria-label="Close"
      data-re-dialog-close
      value="dismissed"
    >
      ×
    </button>
  </header>
  <div class="re-dialog__body">
    <p>Are you sure you want to continue? This action cannot be undone.</p>
  </div>
  <footer class="re-dialog__footer">
    <button
      type="button"
      class="re-button"
      data-variant="ghost"
      id="modal-cancel"
      data-re-dialog-close
      value="cancel"
    >
      Cancel
    </button>
    <button
      type="button"
      class="re-button"
      data-variant="danger"
      id="modal-confirm"
      data-re-dialog-close
      value="confirm"
    >
      Delete
    </button>
  </footer>
</dialog>

Quick edit

Live example
<button
  type="button"
  class="re-button"
  id="open-form-dialog"
  data-re-dialog-trigger
  data-re-dialog-target="form-dialog"
>
  Open form dialog
</button>

<dialog class="re-dialog" id="form-dialog" aria-labelledby="form-title">
  <form method="dialog">
    <header class="re-dialog__header">
      <h2 class="re-dialog__title" id="form-title">Quick edit</h2>
    </header>
    <div class="re-dialog__body">
      <label class="re-field">
        <span class="re-field__label">Title</span>
        <input class="re-input" name="title" id="fd-title" required />
      </label>
    </div>
    <footer class="re-dialog__footer">
      <button
        type="submit"
        class="re-button"
        data-variant="ghost"
        value="cancel"
        formnovalidate
      >
        Cancel
      </button>
      <button type="submit" class="re-button" value="save" id="fd-save">Save</button>
    </footer>
  </form>
</dialog>
  • Keyboard — Tab/Shift+Tab cycle the focusable controls inside the browser-owned focus trap; Enter or Space activate the focused button. Escape dismisses the modal (a native cancel then close). In the method="dialog" form, Enter submits — the submitting button’s value becomes returnValue, and formnovalidate on Cancel lets it close past a required field.
  • Focus — opening calls native showModal(), which traps focus and renders the background inert; closing returns focus to the trigger. Focusable children (the close ×, footer buttons, form inputs) show a visible :focus-visible ring — the dialog box itself has no ring (outline: none), since focus lives on its contents.
  • Semantics — the native <dialog> carries aria-labelledby pointing at .re-dialog__title, so assistive tech announces the dialog name on open. The corner close button is an icon-only control labelled aria-label="Close".
  • Notes — backdrop-click dismissal is opt-in via data-re-dialog-close-on-backdrop and stays keyboard-equivalent to Escape; for a confirmation that must force a choice, prefer the alert dialog recipe.

See the accessibility guide for project-wide conventions.