Skip to content

Dialog

A native dialog element, opened modally.

Preview

Code

The three render the same class names, the same ARIA and the same data attributes. That is checked when this site builds: a class in any of these samples that neither the markup contract nor the shipped stylesheet defines fails the build.

<dialog class="wsu-dialog" id="drop-course" aria-labelledby="drop-title">
  <div class="wsu-dialog__header">
    <h2 id="drop-title">Drop CSC 2110?</h2>
    <button class="wsu-btn wsu-btn--icon wsu-btn--ghost" type="button" data-wsu-dialog-close>
      <span class="wsu-sr-only">Close dialog</span>
      <svg class="wsu-icon" aria-hidden="true"><use href="/assets/wayne-ui/icons.svg#wsu-x"/></svg>
    </button>
  </div>

  <p>Dropping after 8 November leaves a W on your transcript.</p>

  <div class="wsu-dialog__footer">
    <button class="wsu-btn wsu-btn--danger" type="button">Drop course</button>
    <button class="wsu-btn wsu-btn--ghost" type="button" data-wsu-dialog-close>Keep it</button>
  </div>
</dialog>

<script>
  document.querySelector('#open').addEventListener('click', () => {
    document.querySelector('#drop-course').showModal()
  })
</script>

Why it works this way

  • The native element rather than a div with role="dialog". showModal gives focus trapping, the inert background, Escape and the top layer without any of it being reimplemented.
  • Entry and exit need @starting-style and transition-behavior: allow-discrete together. Without both, display flips instantly and there is nothing to transition.
  • Never transition visibility on something script focuses when it opens. A discrete property with allow-discrete does not commit its flip synchronously, so focus() runs against a still-hidden element and is silently ignored.

Accessibility

  • aria-labelledby points at the dialog heading (4.1.2).
  • Focus returns to the control that opened it on close (2.4.3).
  • Escape closes when dismissible, which the native element handles.

Success criteria in brackets refer to WCAG 2.2. Every one of them is checked by axe-core in both themes at three viewport widths before this page can be published. See Accessibility for what the gates are.

Props

Blade

AttributeDefaultWhat it does
idrequiredUsed by the open and close triggers.
titlenullRendered as the dialog heading.
dismissibletrueEscape and the close button.

Vue

PropType and defaultWhat it does
openBoolean, falseUse v-model:open.
title / titleLevelString, '' / Number, 2Names the dialog.
dismissibleBoolean, trueAllow Escape and the close button.
closeLabelString, 'Close dialog'Accessible name for the close button.