Skip to content

Dialog

OverlaysSince 0.1.0
import { Dialog } from '@absoluteui/components/dialog';
import '@absoluteui/components/dialog/style.css';

Using Angular? See Dialog for Angular.

A modal dialog built on native <dialog>.showModal(). The browser provides top-layer rendering, an inert background, and focus containment. AbsoluteUI adds:

  • deterministic Escape and backdrop policy;
  • cancelable close events;
  • focus restoration;
  • scroll lock;
  • localization.
  • Tasks that must be completed or dismissed before returning to the page.
  • Confirmations of destructive actions (role=alertdialog).
  • Non-blocking information: use an inline message, alert, or toast.
  • Complex multi-step flows better served by a page or wizard.
  • Contextual content anchored to a trigger: use a popover.
import { Dialog, type DialogCloseDetail } from '@absoluteui/components/dialog';
const confirm = Dialog.create({
title: 'Delete project?',
description: 'This permanently removes the project.',
role: 'alertdialog',
actions: [
{ label: 'Cancel', value: 'cancel', autofocus: true },
{ label: 'Delete', value: 'delete', variant: 'danger' },
],
});
confirm.element.addEventListener('aui-close', (event) => {
const { returnValue } = (event as CustomEvent<DialogCloseDetail>).detail;
if (returnValue === 'delete') {
// perform deletion
}
});
confirm.open();
import { Dialog } from '@absoluteui/components/dialog';
let dirty = true;
const editor = Dialog.create({ title: 'Edit profile', closeOnBackdrop: true });
editor.element.addEventListener('aui-cancel', (event) => {
if (dirty) event.preventDefault();
});
editor.open();
dirty = false;
import { Dialog } from '@absoluteui/components/dialog';
import '@absoluteui/components/button/style.css';
import '@absoluteui/components/dialog/style.css';
const d = Dialog.create({ title: 'Settings', content: 'Preferences are saved automatically.' });
document.querySelector('#open')?.addEventListener('click', () => {
d.open();
});
import { Dialog } from '@absoluteui/components/dialog';
const confirm = Dialog.create({
title: 'Delete project?',
role: 'alertdialog',
actions: [
{ label: 'Cancel', value: 'cancel', autofocus: true },
{ label: 'Delete', value: 'delete', variant: 'danger' },
],
});
confirm.open();
<dialog class="aui-dialog aui-dialog--{size}" aria-labelledby aria-describedby [role=alertdialog]>
<header class="aui-dialog__header">
<h2 class="aui-dialog__title">title</h2>
<button class="aui-button aui-dialog__close" aria-label="Close">icon</button>
</header>
<p class="aui-dialog__description">description</p>
<div class="aui-dialog__body">content</div>
<footer class="aui-dialog__footer">action buttons</footer>
</dialog>
Dialog.create(options: DialogOptions, doc?: Document): Dialog

Global configuration key: dialog (see Configuration).

Option Type Default Description
title string Accessible name, rendered as the heading. Required.
description string Referenced by aria-describedby.
content Node | string Body content; strings render as text.
actions readonly DialogAction[] Footer buttons; clicking closes with the action value.
role 'dialog' | 'alertdialog' "dialog" Use alertdialog for urgent confirmations.
icons Partial<{ close: IconRef }> Icon slot overrides.
closeOnEscape boolean true Escape requests close (cancelable). Globally configurable.
closeOnBackdrop boolean false Backdrop click requests close (cancelable). Globally configurable.
showCloseButton boolean true Render the header close button. Globally configurable.
size 'sm' | 'md' | 'lg' "md" Maximum inline size. Globally configurable.
Property Type Description
element HTMLDialogElement The native dialog element.
body HTMLDivElement Consumer content area.
isOpen boolean Whether the dialog is open.
Method Description
open(): void Open modally; locks scroll; captures focus for restoration.
close(returnValue?: string): void Close programmatically (not cancelable).
update(options: Partial<DialogOptions>): void Merge and apply options.
destroy(): void Close if open, remove element, listeners, subscriptions.
Event Detail Bubbles Cancelable Description
aui-open null No No After the dialog opened.
aui-cancel { returnValue: string; reason: 'escape' | 'backdrop' | 'close-button' | 'action' } No Yes Before a user-initiated close; preventDefault keeps it open.
aui-close { returnValue: string; reason: DialogCloseReason } No No After the dialog closed.
Area Description
body Dialog.body (or content option).

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-dialog Native <dialog> element.
aui-dialog--sm | --md | --lg Size modifiers.
aui-dialog__header Title row with close button.
aui-dialog__title Heading (h2).
aui-dialog__close Close button.
aui-dialog__description Description paragraph.
aui-dialog__body Scrollable content area.
aui-dialog__footer Action buttons.
Slot Default icon
close close

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-backdrop | --aui-color-border | --aui-color-focus-ring | --aui-color-surface | --aui-color-text | --aui-color-text-muted | --aui-dialog-width-lg | --aui-dialog-width-md | --aui-dialog-width-sm | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-lg | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-semibold | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-lg | --aui-shadow-lg | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-space-4 | --aui-space-5

type DialogCloseReason = 'escape' | 'backdrop' | 'close-button' | 'action' | 'programmatic';
interface DialogAction { label: string; value: string; variant?: ButtonVariant; autofocus?: boolean }
interface DialogCloseDetail { returnValue: string; reason: DialogCloseReason }
interface DialogOptions {
title: string;
description?: string;
content?: Node | string;
actions?: readonly DialogAction[];
role?: 'dialog' | 'alertdialog';
icons?: Partial<{ close: IconRef }>;
closeOnEscape?: boolean;
closeOnBackdrop?: boolean;
showCloseButton?: boolean;
size?: 'sm' | 'md' | 'lg';
}

Pattern: WAI-ARIA APG Dialog (Modal) and Alert Dialog patterns, implemented with native <dialog>.showModal().

Key Context Action
Tab / Shift+Tab Dialog open Moves focus among dialog controls; background is inert (native modal).
Escape Dialog open, closeOnEscape=true Emits cancelable aui-cancel, then closes and restores focus.
Escape Dialog open, closeOnEscape=false No effect; the native close request is prevented.
Escape Nested overlay (e.g. combobox listbox) open Closes only the topmost overlay.
Enter / Space Action or close button focused Activates the button.
Element Role
<dialog> dialog (native) or alertdialog
title heading level 2
Attribute Element Value
aria-labelledby <dialog> id of the title heading
aria-describedby <dialog> id of the description (when provided)
aria-label close button Localized ‘Close’

On open, focus moves to the autofocus action or first focusable element. On close, focus returns to the element focused before opening.

Criterion Level Note
1.3.1 Info and Relationships A
2.1.1 Keyboard A
2.1.2 No Keyboard Trap A
2.4.3 Focus Order A
2.4.7 Focus Visible AA
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • Background inertness and top-layer rendering are provided by the browser.
  • Scroll lock uses scrollbar-gutter to avoid layout shift.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.

closed, open, and closing (an aui-cancel listener is pending).

From Trigger To
closed open() open (focus moves inside, scroll locked)
open Escape / backdrop / close button / action aui-cancel -> closed, unless prevented
open close() closed (no aui-cancel)
open consumer <form method="dialog"> submit closed (reason action)
  • Initial focus goes to the action with autofocus: true, otherwise the first focusable element (native dialog focusing steps).
  • For alert dialogs, set autofocus on the least destructive action.
  • On close, focus returns to the element that was focused before open().
  • Verified with axe (WCAG 2.2 AA) in 3 themes.
  • Manual screen-reader verification is pending.

Width is min(size token, 100vw - 2rem) and height is capped at 100dvh - 2rem; the body scrolls. Below 480px, footer actions stack full-width.

Logical properties throughout; the close button sits at the inline end.

Token-driven surface, border, radius, shadow, and backdrop. Forced-colors mode uses a CanvasText border.

configureAbsoluteUI({ components: { dialog: { closeOnBackdrop: true, size: 'lg' } } });

Instance options override global and theme defaults. All configurable options are dynamic and re-render the open dialog.

icons.close (default close), resolved through the global icon provider/registry.

Title, description, and string content use textContent. No HTML parsing, no inline styles. Verified under a strict CSP with Trusted Types enforced.

No persistence, network access, or telemetry.

Constant work per open/close: one showModal() call, one scroll-lock reference, and one focus restore point. Budget: open() under 16 ms in headless Chromium (performance test).

Not data-driven. Body content size is limited only by the consumer; the body scrolls.

  • closeOnEscape: false also blocks repeated Escape presses. Chrome would otherwise let the second Escape through without user activation.
  • Escape in a focused combobox that has a value but a closed listbox clears the combobox first, per the APG.
  • Destroying an open dialog releases the scroll lock and restores focus.
Code Category Cause
DIALOG_TITLE configuration Missing or blank title.
CFG_INVALID_VALUE / CFG_UNKNOWN_OPTION configuration Invalid or unknown configuration.
  • The close transition is not animated; the open transition uses @starting-style and is removed under reduced motion.
  • Only modal dialogs are supported.
  • Manual screen-reader verification is pending.
  • Non-modal dialogs.
  • HTML strings as content: pass a Node instead.