Modal/Popup

A modal or popup dialog with minimal JavaScript.

How It Works

This modal is built using the semantic HTML <dialog> element with minimal inline JavaScript. It leverages the native accessibility features of the dialog element for proper focus management and screen reader support.

The implementation uses only inline event handlers for minimal JavaScript footprint, making it easy to copy and paste into any project.

Accessibility Features

ARIA Attributes Explained

  • aria-haspopup="dialog": Indicates the button opens a dialog popup
  • aria-controls="modal": Establishes a relationship between the button and the dialog it controls
  • aria-labelledby="modal-title": Associates the dialog with its title, announcing the title when the dialog opens
  • aria-describedby="modal-description": Provides additional context about the dialog's purpose
  • aria-modal="true": Tells assistive technologies that this is a modal window that blocks interaction with page content
  • aria-label="Close modal": Provides an accessible name for buttons that only have icon content

Keyboard Navigation

  • Tab key: Moves focus between interactive elements within the dialog
  • Escape key: Closes the dialog (built-in behavior of the dialog element)
  • Enter/Space: Activates buttons when they have keyboard focus

Focus Management

The <dialog> element with showModal() automatically:

  • Traps focus within the dialog when open
  • Returns focus to the triggering element when closed
  • Makes content outside the dialog inert and unfocusable

What Not To Add

For best accessibility practices, avoid these common mistakes:

  • tabindex="-1" on <dialog>: Not needed — the dialog element is already focusable by default
  • role="alertdialog": Only use this if the modal is urgent (e.g., errors or warnings that require immediate attention)
  • role="dialog": Redundant, as the dialog element already has this semantic role built-in
  • autofocus attributes: The dialog element handles focus management automatically when using showModal()

Additional Resources

Implementation Code

<!-- Modal trigger button -->
<button 
  class="modal-trigger" 
  onclick="document.getElementById('modal').showModal()"
  aria-haspopup="dialog"
  aria-controls="modal">
  Open Modal
</button>

<!-- Modal dialog -->
<dialog
  id="modal"
  class="modal-dialog"
  aria-labelledby="modal-title"
  aria-describedby="modal-description"
  aria-modal="true"
  closedBy="any">
  <div class="modal-content">
    <h2 id="modal-title">Modal Title</h2>
    <p id="modal-description">
      This is a modal dialog using the semantic HTML dialog element.
    </p>
    <button
      class="modal-close"
      aria-label="Close modal"
      onclick="this.closest('dialog').close()">
      &times;
    </button>
    <div class="modal-actions">
      <button 
        class="modal-button" 
        aria-label="Close modal"
        onclick="this.closest('dialog').close()">
        Close
      </button>
    </div>
  </div>
</dialog>