Under Construction: The content on this site draft material. It is not the official content of this site and should not be followed at this time.

Not Legal Advice: The content on this site is provided for informational purposes only and does not constitute legal advice. Consult a qualified attorney for advice specific to your situation.

Purpose

Dialogs present supplemental content, forms, or confirmations in a focused context. Unlike alert dialogs, standard dialogs may contain any content and are not announced immediately by screen readers.

Usage Guidance

  • Use dialogs sparingly; they interrupt workflow
  • A dialog should have a clear heading and a close mechanism
  • Avoid dialogs that contain other dialogs (nested modals)
  • Provide a visible close button (× with aria-label="Close dialog")

Accessibility Considerations

  • role="dialog" on the container
  • aria-modal="true" prevents screen readers from navigating outside the dialog
  • aria-labelledby references the dialog’s heading
  • Trap focus within the dialog while it is open
  • On close, return focus to the trigger element

Keyboard Support

Key Action
Tab Move focus forward among interactive elements
Shift+Tab Move focus backward among interactive elements
Escape Close the dialog

Screen Reader Behavior

Screen readers announce the dialog’s label and role when it opens. aria-modal="true" prevents navigation outside the dialog (support varies; use inert on the background as a supplement).

ARIA Requirements

  • role="dialog" on the container
  • aria-modal="true" on the container
  • aria-labelledby pointing to the dialog heading

Common Mistakes

  • Not trapping focus (user tabs into background content)
  • Not restoring focus to the trigger on close
  • Using only Escape to close without a visible close button
  • Animating the dialog without respecting prefers-reduced-motion

Live Example


Code Example

<!-- Trigger -->
<button type="button" data-dialog-open="my-dialog">Open Dialog</button>

<!-- Dialog -->
<div
  id="my-dialog"
  role="dialog"
  aria-modal="true"
  aria-labelledby="my-dialog-title"
  hidden
>
  <div class="dialog__inner">
    <div class="dialog__header">
      <h2 id="my-dialog-title">Dialog Title</h2>
      <button
        type="button"
        aria-label="Close dialog"
        data-dialog-close
      >✕</button>
    </div>
    <div class="dialog__body">
      <!-- Dialog content -->
    </div>
    <div class="dialog__footer">
      <button type="button" data-dialog-close>Cancel</button>
      <button type="submit">Confirm</button>
    </div>
  </div>
</div>

See main.js for the focus-trapping JavaScript implementation.