Dialog
A native dialog element, opened modally.
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><x-wsu::dialog id="drop-course" title="Drop CSC 2110?">
<p>Dropping after 8 November leaves a W on your transcript.</p>
<x-slot:footer>
<x-wsu::button variant="danger">Drop course</x-wsu::button>
<x-wsu::button variant="ghost" data-wsu-dialog-close>Keep it</x-wsu::button>
</x-slot:footer>
</x-wsu::dialog><Dialog v-model:open="confirming" title="Drop CSC 2110?">
<p>Dropping after 8 November leaves a W on your transcript.</p>
<template #footer>
<Button variant="danger" @click="drop">Drop course</Button>
<Button variant="ghost" @click="confirming = false">Keep it</Button>
</template>
</Dialog>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
| Attribute | Default | What it does |
|---|---|---|
id | required | Used by the open and close triggers. |
title | null | Rendered as the dialog heading. |
dismissible | true | Escape and the close button. |
Vue
| Prop | Type and default | What it does |
|---|---|---|
open | Boolean, false | Use v-model:open. |
title / titleLevel | String, '' / Number, 2 | Names the dialog. |
dismissible | Boolean, true | Allow Escape and the close button. |
closeLabel | String, 'Close dialog' | Accessible name for the close button. |