Modal Windows for Everyone

Modal Dialog

  • A “pop-up” (takes over the mode of the page)
  • Containing interactive content

Maybe not a Modal

(Modalz, Modalz, Modalz)

Display content inline whenever possible.

Put content on its own route / page.

Don’t open a modal from another modal.

🚫

HTML Accessibility

<dialog
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
  id="this-modal-id"
>
  <h2 id="modal-title">The Modal Title</h2>
  <!-- Modal Contents -->
</dialog>

Semantics:
the element is a modal dialog

Label:
the element has a title

  • aria-labelledby="title-id"
  • aria-label="Modal Title"
  • Read aloud by screen readers

Visibility: Buttons

Open Button (outside of modal)

<button aria-controls="modal-id">
  Open {Modal Name}
</button>

aria-controls Is Poop

Visibility: Buttons

Close Button (inside modal)

<button>Close {Modal Name}</button>

Visibility: Buttons

Close Button (remixed)

<button>
  <span class="sr-only">
    Close {Modal Name}
  </span>
  <svg aria-hidden="true"></svg>
</button>

Visibility: Buttons

Scrim (optional), outside of Modal

<button aria-hidden="true">
  <span class="sr-only">
    Close {Modal Name}
  </span>
</button>

JavaScript Interactivity

Toggle Visibility

Open Closed
open inert
aria-hidden="true"

Future Feature: inert polyfill

function openModal () {
  modal.removeAttribute('aria-hidden')
  modal.removeAttribute('inert')
  modal.setAttribute('open', true)
}
function closeModal () {
  modal.setAttribute('aria-hidden', 'true')
  modal.setAttribute('inert', true)
  modal.removeAttribute('open')
}

Close Events

  • Click outside the modal
    (or <button> scrim)
  • Escape keyup event

Focus

  1. On open, focus on modal title or content wrapper in modal (tabindex="0")
  2. While open, trap focus inside modal
  3. On close, restore focus to open button ([aria-controls="id"])

CSS Visual Styles

Selectors

Connect CSS to aria-hidden attribute:

.modal[aria-hidden="true"] {
  visibility: hidden;
  /* Other CSS */
}

Visibility

visibility: hidden | visible
not
display: none | block

Performance

For smooth animations:

  • opacity
  • transform

Motion Accessibility

Respect motion preferences.

@media (prefers-reduced-motion: reduce) {
  * {
    transition-duration: 0 !important;
    animation-duration: 0 !important;
  }
}

Handle Scrolling Well

CSS-only Modals?

🙅

These are “clever,” but don’t communicate state to screen readers.

Progressive Enhancement

Put modal markup on its own page.

Put them in a dialog when JS works.

Resources

Thanks, Y’all!

James Steinbach

Senior UX Developer
at DockYard

jdsteinbach

Twitter | GitHub | Blog