Skip to content

Dialogs

Invite teammate

They'll receive an email with a sign-up link.

Pick a role after they accept.

<button
type="button"
class="btn btn-primary"
commandfor="dialog-basic"
command="show-modal"
>
Open dialog
</button>
<dialog id="dialog-basic" class="dialog" closedby="any">
<div class="dialog-header">
<h2 class="dialog-title">Invite teammate</h2>
<button
type="button"
class="dialog-close"
commandfor="dialog-basic"
command="close"
aria-label="Close"
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
</button>
</div>
<p class="dialog-description">
They'll receive an email with a sign-up link.
</p>
<div class="dialog-body">
<p>Pick a role after they accept.</p>
</div>
<div class="dialog-footer">
<button
type="button"
class="btn btn-ghost"
commandfor="dialog-basic"
command="close"
>
Cancel
</button>
<button
type="button"
class="btn btn-primary"
commandfor="dialog-basic"
command="close"
>
Send invite
</button>
</div>
</dialog>

Small

Up to 24rem wide.

Medium

Default — up to 32rem.

Large

Up to 48rem — room for two-column forms.

Auto

Shrinks to fit its content.
<button type="button" class="btn" commandfor="dialog-sm" command="show-modal">
Small
</button>
<button type="button" class="btn" commandfor="dialog-md" command="show-modal">
Medium
</button>
<button type="button" class="btn" commandfor="dialog-lg" command="show-modal">
Large
</button>
<button
type="button"
class="btn"
commandfor="dialog-auto"
command="show-modal"
>
Auto
</button>
<dialog id="dialog-sm" class="dialog dialog-sm">
<div class="dialog-header">
<h2 class="dialog-title">Small</h2>
</div>
<div class="dialog-body">Up to 24rem wide.</div>
<div class="dialog-footer">
<button type="button" class="btn" commandfor="dialog-sm" command="close">
Close
</button>
</div>
</dialog>
<dialog id="dialog-md" class="dialog">
<div class="dialog-header">
<h2 class="dialog-title">Medium</h2>
</div>
<div class="dialog-body">Default — up to 32rem.</div>
<div class="dialog-footer">
<button type="button" class="btn" commandfor="dialog-md" command="close">
Close
</button>
</div>
</dialog>
<dialog id="dialog-lg" class="dialog dialog-lg">
<div class="dialog-header">
<h2 class="dialog-title">Large</h2>
</div>
<div class="dialog-body">Up to 48rem — room for two-column forms.</div>
<div class="dialog-footer">
<button type="button" class="btn" commandfor="dialog-lg" command="close">
Close
</button>
</div>
</dialog>
<dialog id="dialog-auto" class="dialog dialog-auto">
<div class="dialog-header">
<h2 class="dialog-title">Auto</h2>
</div>
<div class="dialog-body">Shrinks to fit its content.</div>
<div class="dialog-footer">
<button
type="button"
class="btn"
commandfor="dialog-auto"
command="close"
>
Close
</button>
</div>
</dialog>

Revenue report

<button
type="button"
class="btn"
commandfor="dialog-metabase"
command="show-modal"
>
Open report
</button>
<dialog id="dialog-metabase" class="dialog dialog-metabase">
<div class="dialog-header">
<h2 class="dialog-title">Revenue report</h2>
<button
type="button"
class="dialog-close"
commandfor="dialog-metabase"
command="close"
aria-label="Close"
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
</button>
</div>
<div class="dialog-body">
<iframe
src="https://metabase.example.com/public/dashboard/…"
style="width: 100%; aspect-ratio: 16 / 9; border: 0"
title="Revenue report"
></iframe>
</div>
</dialog>

Rename project

<button
type="button"
class="btn btn-primary"
commandfor="dialog-form"
command="show-modal"
>
Rename project
</button>
<dialog id="dialog-form" class="dialog">
<form method="dialog">
<div class="dialog-header">
<h2 class="dialog-title">Rename project</h2>
<button
type="button"
class="dialog-close"
commandfor="dialog-form"
command="close"
aria-label="Close"
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
</button>
</div>
<div class="dialog-body">
<div class="field">
<label class="field-label" for="dialog-form-name">Name</label>
<input id="dialog-form-name" class="input" name="name" required />
</div>
</div>
<div class="dialog-footer">
<button
type="submit"
class="btn btn-ghost"
value="cancel"
formnovalidate
>
Cancel
</button>
<button type="submit" class="btn btn-primary" value="save">Save</button>
</div>
</form>
</dialog>

Delete project?

This permanently removes all data. This cannot be undone.

<button
type="button"
class="btn btn-danger"
commandfor="dialog-destroy"
command="show-modal"
>
Delete project
</button>
<dialog id="dialog-destroy" class="dialog dialog-sm" closedby="closerequest">
<div class="dialog-header">
<h2 class="dialog-title">
<i class="ti ti-alert-triangle" aria-hidden="true"></i>
Delete project?
</h2>
</div>
<p class="dialog-description">
This permanently removes all data. This cannot be undone.
</p>
<div class="dialog-footer">
<button
type="button"
class="btn btn-ghost"
commandfor="dialog-destroy"
command="close"
>
Cancel
</button>
<button
type="button"
class="btn btn-danger"
commandfor="dialog-destroy"
command="close"
>
Delete
</button>
</div>
</dialog>

Workspace settings

<Button commandfor="dialog-custom-r" command="show-modal">
Settings
</Button>
<Dialog.Container id="dialog-custom-r" size="lg">
<Dialog.Header>
<Dialog.Title>Workspace settings</Dialog.Title>
<Dialog.CloseButton />
</Dialog.Header>
<Dialog.Body>
<Field>
<Field.Label>Name</Field.Label>
<Input defaultValue="Acme Inc." />
</Field>
<Field>
<Field.Label>Billing email</Field.Label>
<Input type="email" defaultValue="ops@acme.test" />
</Field>
</Dialog.Body>
<Dialog.Footer>
<Button variant="ghost" commandfor="dialog-custom-r" command="close">
Cancel
</Button>
<Button commandfor="dialog-custom-r" command="close">
Save changes
</Button>
</Dialog.Footer>
</Dialog.Container>
import { useState } from "react";
import { Button, Dialog } from "@aortl/admin-react";
function ConfirmDelete({ onConfirm }: { onConfirm: () => void }) {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="danger" onClick={() => setOpen(true)}>
Delete
</Button>
<Dialog
open={open}
onOpenChange={setOpen}
size="sm"
closedby="closerequest"
title="Delete project?"
description="This cannot be undone."
actions={
<>
<Button variant="ghost" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button
variant="danger"
onClick={() => {
onConfirm();
setOpen(false);
}}
>
Delete
</Button>
</>
}
/>
</>
);
}
Part Renders Class
Dialog <dialog> dialog
Dialog.Container <dialog> dialog
Dialog.Header <div> dialog-header
Dialog.Title <h2> dialog-title
Dialog.Description <p> dialog-description
Dialog.Body <div> dialog-body
Dialog.Footer <div> dialog-footer
Dialog.CloseButton <button> dialog-close
Part Prop Type Default
Dialog open boolean
Dialog onOpenChange (open: boolean) => void
Dialog size "sm" | "md" | "lg" | "auto" | "metabase" "md"
Dialog closedby "any" | "closerequest" | "none" "any"
Dialog icon IconProp
Dialog title ReactNode
Dialog description ReactNode
Dialog actions ReactNode
Dialog dismissible boolean true
Dialog closeLabel string "Close"
Dialog classNames slots
Dialog.Title icon IconProp
Dialog.CloseButton icon IconProp X glyph

Dialog assembles header (title, icon, close button), description, body and footer around its children; dismissible={false} drops the X. classNames covers header, title, close, description, body, footer.

size and closedby live on the element, so they work on Dialog.Container too — the form to use when the layout doesn’t fit, most often to wrap everything in a <form>. Dialog.Title keeps its icon prop there.

Leave open off for the uncontrolled case, as the Invoker Commands pattern below does: no state, no effect, no ref. Pass open with onOpenChange when the open state belongs to React (a multi-step flow, an async submit, a deep-linked route): the wrapper bridges it to showModal() / close() and forwards the native close event back as onOpenChange(false), so Esc and backdrop clicks stay in sync with your state.

Plus native <dialog> attributes.

Class Effect
dialog Centred modal: 32rem max-width, 0.75rem radius, large shadow, capped to the viewport, scrim backdrop, 150ms fade and lift
dialog-sm 24rem max-width — confirms
dialog-lg 48rem — longer forms
dialog-auto Shrinks to content, still capped to the viewport
dialog-metabase 1138px wide with 2.75rem gutters, so a full-width body child lands at 1048px
dialog-header Title row: 1.25rem side padding, 0.75rem gap
dialog-title text-lg semibold, 0.5rem gap for a leading icon
dialog-description text-sm muted, tucked under the header
dialog-body The scrolling region; header and footer stay pinned
dialog-footer Action row: muted fill, top border, right-aligned, wrapping
dialog-close 1.75rem square X button

There is no dialog-md — it’s the unmodified dialog. Everything modal comes from the native element via showModal(): focus trap, scroll lock, Esc, and ::backdrop. The fade and lift use @starting-style with transition-behavior: allow-discrete, so the exit animation runs without JavaScript.

Open it without JavaScript through the Invoker Commands API: a <button commandfor="<dialog-id>" command="show-modal">, and command="close" to dismiss. The closedby attribute picks the dismissal set — "any" (the default) allows Esc and a backdrop click, "closerequest" drops the backdrop click, the right choice for an irreversible action, and "none" requires an explicit close.

Wrap the contents in <form method="dialog"> and any submit button inside closes the dialog, with the submitter’s value landing on the dialog’s returnValue — that’s the whole pattern for a form dialog, no handler required.

Write the close button’s aria-label yourself. Don’t put overflow: hidden on the root to clip the footer’s fill — it also clips any menu or select popup inside; the footer inherits the corner radius instead.

For an edge-anchored panel rather than a centred one, use a Drawer.