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

Modals are dialogs that overlay the main page content. They are used for tasks requiring undivided attention: forms, confirmations, image previews, and content-heavy overlays.

Accessibility Considerations

This component is identical to Dialog in ARIA implementation. See the Dialog component for full documentation. The modal pattern adds:

  • Scroll lock (overflow: hidden on <body> while open)
  • Backdrop (semi-transparent overlay) with click-to-dismiss
  • Focus management (focus to first element on open; restore on close)
  • inert attribute on background content (progressive enhancement)

Keyboard Support

See Dialog keyboard support.

ARIA Requirements

  • role="dialog" or role="alertdialog"
  • aria-modal="true"
  • aria-labelledby referencing the modal heading

Live Example

See the Dialog component for a live interactive example. The Dialog and Modal implementations share the same JavaScript pattern.


Full Implementation Notes

The main.js initDialogs() function handles:

  1. Opening: shows the overlay, traps focus, locks scroll
  2. Keyboard: Escape closes; Tab/Shift+Tab cycle within
  3. Backdrop: click outside the inner panel to close
  4. Closing: removes the modal, restores scroll, returns focus to trigger
<!-- Modal structure -->
<div
  id="my-modal"
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-heading"
  hidden
  class="modal-overlay"
>
  <div class="modal-panel">
    <div class="modal-header">
      <h2 id="modal-heading">Modal Title</h2>
      <button type="button" aria-label="Close modal" data-dialog-close>✕</button>
    </div>
    <div class="modal-body">
      <!-- Modal content here -->
    </div>
  </div>
</div>
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.5);
  display: flex; align-items: center; justify-content: center;
  z-index: 200;
}
.modal-panel {
  background: #fff;
  border-radius: 0.75rem;
  max-width: 32rem; width: 90%;
  max-height: 90vh; overflow-y: auto;
  padding: 2rem;
  box-shadow: 0 20px 25px rgba(0,0,0,0.15);
}
@media (prefers-reduced-motion: no-preference) {
  .modal-panel {
    animation: modal-enter 200ms ease;
  }
}
@keyframes modal-enter {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}