Repo

An alert dialog interrupts the user to confirm an action, such as "Delete this project?". It is a Dialog with role="alertdialog" and different rules for dismissal, focus and labelling.

Use it only when the user must answer before they carry on. For a form, a picker or anything the user can safely ignore, use a Dialog.

Usage

Give it a short title and a description that says what happens next. Put a DialogFooter with the buttons inside. Put Cancel first. A modal dialog focuses its first focusable element when it opens, so the safe choice gets focus.

Delete this project?

This deletes the project and all its files. You cannot undo this.

<button class="zui-button zui-button-color-destructive" onclick="document.getElementById('alert-dialog-demo').showModal()">
Delete project
</button>

<dialog
id="alert-dialog-demo"
class="zui-dialog zui-alert-dialog"
role="alertdialog"
closedby="closerequest"
aria-labelledby="alert-dialog-demo-title"
aria-describedby="alert-dialog-demo-description"
>
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title" id="alert-dialog-demo-title">Delete this project?</h2>
  <p class="zui-dialog-description" id="alert-dialog-demo-description">
    This deletes the project and all its files. You cannot undo this.
  </p>
</div>
<div class="zui-dialog-footer">
  <button class="zui-button zui-button-variant-outline" onclick="this.closest('dialog').close('cancel')">Cancel</button>
  <button class="zui-button zui-button-color-destructive" onclick="this.closest('dialog').close('delete')">Delete project</button>
</div>
</dialog>

In plain HTML, give the <dialog> all of these: role="alertdialog", aria-labelledby pointing at the title, and aria-describedby pointing at the description. The wrappers do this for you.

How it differs from Dialog

Dialog Alert Dialog
Role dialog alertdialog. A screen reader reads the title and description as soon as it opens
Click outside Closes it Does nothing
Esc Closes it Closes it. Treat Esc as Cancel
Initial focus First focusable element First focusable element. Put Cancel first so it gets focus
Close button (X) Optional None. The user must pick an answer
Title and description Optional Title is required: it is the accessible name. Description is optional but recommended
Position and size Several Centred, one width

Dismissal

closedby defaults to "closerequest": Esc closes the dialog and a click on the backdrop does not. Browsers that do not support closedby already behave this way for modal dialogs. Set closedby="none" if Esc must not close it either. Then Cancel is the only way out, and you must give one.

Reacting to the answer

The native close event fires for every way out. Read dialog.returnValue to see which button the user picked. In the plain HTML example above, Cancel closes with 'cancel' and Delete with 'delete'.

dialog.addEventListener('close', () => {
  if (dialog.returnValue === 'delete') deleteProject()
})

Accessibility

Props

Prop Type Default Description
title string — Required. Names the dialog
description string — Says what happens next
open boolean false Opens the dialog. Not used in Astro: call showModal()
onClose () => void — Called when the dialog closes (onclose in Svelte, @close in Vue)
id string generated Required in Astro, so you can open it. Title and description ids are {id}-title and {id}-description
shape default | hard | squircle default Corner shape, as on Dialog
closedby closerequest | none | any closerequest How the user can dismiss it. any lets a click on the backdrop close it, which an alert dialog should not allow

The Svelte component needs Svelte 5.20 or newer, because it uses $props.id() for the generated ids.

CSS custom properties

Set these on :root, on a wrapper, or on the element. See Component custom properties. All the Dialog properties work too.

Property Default Description
--zui-alert-dialog-max-width var(--zui-dialog-max-width, 26rem) Maximum width

Theme

Copy this CSS to your project: