Dialog
Dialogs interrupt the page to capture a decision or short form. They trap focus and return it when dismissed.
Try it
Usage
Register dialogs with installDialogs, then open them imperatively via openDialog or a trigger with data-dialog-open. Compose title, body, and actions with the sub-components.
import {
RuiDialog,
RuiDialogTitle,
RuiDialogBody,
RuiDialogActions,
RuiDialogClose,
installDialogs,
openDialog,
} from '@ecopages/radiant-ui/dialog';
installDialogs();
<RuiDialog id="edit-profile" label="Edit profile">
<RuiDialogTitle>Edit profile</RuiDialogTitle>
<RuiDialogBody>Update your display name and email.</RuiDialogBody>
<RuiDialogActions>
<RuiDialogClose>Cancel</RuiDialogClose>
<button type="button">Save</button>
</RuiDialogActions>
</RuiDialog>Custom markup
<rui-dialog> coordinates any light-DOM tree that matches its query contract. The
Rui* helpers stamp these targets; they are not required.
import '@ecopages/radiant-ui/dialog';
<rui-dialog open label="Edit profile">
<div data-ref="root" class="rui-dialog">
<div data-ref="backdrop" class="rui-dialog__backdrop"></div>
<div data-ref="dialog" class="rui-dialog__surface" role="dialog" aria-modal="true" tabindex={-1}>
<button type="button" data-dialog-close class="rui-dialog__close"></button>
<div data-dialog-title class="rui-dialog__title">Edit profile</div>
<div data-dialog-body class="rui-dialog__body">Update your display name and email.</div>
</div>
</div>
</rui-dialog>;BEM classes are presentation-only. Set alert on the host for role="alertdialog" on [data-ref="dialog"].
Alert dialogs
Set alert to true for destructive confirmations. Alert dialogs limit tab order to the dialog actions.
Return focus on close
Focus returns to the element that opened the dialog. Avoid stacking multiple modal layers.
Theming
Dialog surfaces map to semantic surface roles, never Tailwind palette steps. Override --rui-dialog-* on rui-dialog.
| Part | Default | Override |
|---|---|---|
| Backdrop | overlay scrim | theme --color-overlay |
| Surface fill / radius / shadow | --background, --radius-container, --shadow-modal | --rui-dialog-surface, --rui-dialog-radius, --rui-dialog-shadow |
| Surface padding / max width | --space-inset, 28rem | --rui-dialog-padding, --rui-dialog-max-width |
| Narrow viewport inset | 1rem | --rui-dialog-inset-x |
| Title | on-background | theme |
| Close button | on-surface | theme |
rui-dialog {
--rui-dialog-max-width: 40rem;
--rui-dialog-padding: var(--space-6);
}Accessibility
- Every dialog needs an accessible name via
labelorRuiDialogTitle. - Use
alertfor confirmations that require an explicit decision before continuing. - Do not open a dialog without a clear way to dismiss it; provide a close action.
API
RuiDialog is a custom element (<rui-dialog>) with a composition-first surface. Pass title / actions for the composite API, or compose the view helpers as children.
Attributes (<rui-dialog>)
| Attribute | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Whether the dialog is open. |
alert | boolean | false | Uses role="alertdialog" for workflow-interrupting confirmations. |
label | string | '' | Accessible name when there is no visible title. |
Light-DOM contract
| Target | Required | Host writes | Author owns |
|---|---|---|---|
[data-ref="root"] | yes | hidden when closed | the wrapper node |
[data-ref="backdrop"] | yes | — | the scrim node; click dismisses |
[data-ref="dialog"] | yes | aria-labelledby, aria-label, aria-describedby | role, aria-modal, tabIndex (view seeds these) |
[data-dialog-title] | no | id, aria-labelledby wiring | visible title text |
[data-dialog-body] | no | id, aria-describedby wiring | body content |
[data-dialog-close] | no | — | dismiss control; click emits rui-close |
Do not set hidden on [data-ref="root"] or aria-* on [data-ref="dialog"]. Nested hosts: none.
Events
| Event | Detail | Description |
|---|---|---|
rui-close | { reason: 'escape' | 'backdrop' | 'dismiss' } | Emitted when the dialog is dismissed. |
View helpers
| Component | Target stamped | Notes |
|---|---|---|
RuiDialog | DialogShell targets | Pass title / actions for composite API, or compose helpers as children. |
RuiDialogTitle | [data-dialog-title], data-ref="title" | Visible accessible name. |
RuiDialogBody | [data-dialog-body], data-ref="description" | aria-describedby target. |
RuiDialogActions | — | Action row; not queried by the host. |
RuiDialogClose | [data-dialog-close], data-ref="close" | Emits rui-close with reason dismiss. |
CSS classes
Public BEM classes (documented via @cssclass):
| Class | Description |
|---|---|
.rui-dialog | Root; hidden until open. |
.rui-dialog__backdrop | Scrim using the overlay role. |
.rui-dialog__surface | Modal panel: background + rounded-container + shadow-modal. |
.rui-dialog__title | Dialog title; aria-labelledby target. |
.rui-dialog__body | Dialog body; aria-describedby target. |
.rui-dialog__actions | Right-aligned action row. |
.rui-dialog__close | Dismiss button. |
Theme roles
| Part | CSS variables consumed |
|---|---|
| Backdrop | --overlay |
| Surface | --background, --on-background, --shadow-modal, --radius-container, --p-inset |
| Close button | --on-surface |