Modal
Keeps a short, reversible task in focus without removing the person from the current context.
Design
Anatomy
Review changes 4
Check the details before applying this update.
The update can be reverted later from the activity log.
6.ds-modal-overlay) — full-screen backdrop that blocks page interaction.2 Container (
.ds-modal) — elevated surface with border-radius and shadow.3 Header (
.ds-modal__header) — contains optional heading, description, and close button.4 Title (
.ds-modal__title) — linked via aria-labelledby.5 Close button (
.ds-modal__close) — dismisses the modal.6 Body (
.ds-modal__body) — scrollable content area.7 Footer (
.ds-modal__footer) — action buttons aligned right.
Interactive example · React · shadcn/Base UI
Running with the React source recipe distributed through shadcn and based on Base UI.
Sizes · React · shadcn/Base UI
Three independent dialogs running through the shadcn recipe with Base UI primitives.
Custom body · React · shadcn/Base UI
Field, Input, and Button from the React output composed inside the Dialog.
Token mapping
| Property | Token | CSS variable |
|---|---|---|
| overlay background | component.modal.overlay.bg.default | --ds-modal-overlay-bg-default |
| surface | semantic.surface.elevated | --ds-surface-elevated |
| overlay padding | component.modal.overlay.padding.default | --ds-modal-overlay-padding-default |
| overlay z-index | component.modal.overlay.z-index.default | --ds-modal-overlay-z-index-default |
| border-radius | component.modal.radius.default | --ds-modal-radius-default |
| shadow | component.modal.shadow.default | --ds-modal-shadow-default |
| max-width | component.modal.max-width.{sm,md,lg} | --ds-modal-max-width-* |
| header spacing | component.modal.header.{padding-*,gap}.* | --ds-modal-header-*-* |
| body spacing/typography | component.modal.body.{padding-*,font-size,line-height,font-weight}.* | --ds-modal-body-*-* |
| description typography | component.modal.body.{font-size,line-height,font-weight}.* | --ds-modal-body-*-* |
| title typography | component.modal.title.{font-size,line-height,font-weight,letter-spacing}.* | --ds-modal-title-*-* |
| footer spacing | component.modal.footer.{padding-*,gap}.* | --ds-modal-footer-*-* |
| close target | component.modal.close.size.{sm,md,lg} | --ds-modal-close-size-* |
| close icon | component.modal.close.icon-size.{sm,md,lg} | --ds-modal-close-icon-size-* |
| close padding | component.modal.close.padding.{sm,md,lg} | --ds-modal-close-padding-* |
Usage
When to use
- A short edit or review must finish before returning to the previous context.
- The content requires focused attention but does not justify a dedicated page.
When not to use
- The task is long, has multiple steps, or must remain visible alongside the page.
- The confirmation is destructive or critical; that case belongs to the separate Alert Dialog contract.
React composition
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
<Dialog>
<DialogTrigger render={<Button type="button" />}>Abrir modal</DialogTrigger>
<DialogContent closeLabel="Fechar modal">
<DialogHeader>
<DialogTitle>Revisar alterações</DialogTitle>
<DialogDescription>Confirme antes de continuar.</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>Implementation
- Status
- Beta
- Distribution
- Source via shadcn
Installation
Configure the namespace once in the React integration guide.
npx shadcn@latest add @tis/dialog @tis/buttonpnpm dlx shadcn@latest add @tis/dialog @tis/buttonyarn dlx shadcn@latest add @tis/dialog @tis/buttonbunx --bun shadcn@latest add @tis/dialog @tis/buttonPublic contract
- Registry item
@tis/dialog- Provider
- Base UI
- Distribution
- Source copied into the application
- Status
- Beta
Accessibility
Keyboard interaction
| Key | Action |
|---|---|
Tab | Cycles focus inside the modal (focus trap) |
Shift+Tab | Cycles focus backwards inside the modal |
Escape | Closes the modal |
Enter | Activates the focused button |
Focus must be trapped inside the modal while it is open. When the modal closes, focus must return to the element that triggered it.
Accessibility
| WCAG criterion | Requirement | Status |
|---|---|---|
| 2.4.3 Focus Order (A) | Focus moves into the modal on open, is trapped inside, and returns to the trigger on close | ✓ |
| 2.4.11 Focus Appearance (AA) | Focus ring visible on all interactive elements inside the modal | ✓ |
| 4.1.2 Name, Role, Value (A) | role="dialog", aria-modal="true", aria-labelledby pointing to the title | ✓ |
| 1.4.11 Non-text Contrast (AA) | Close button has at least 3:1 contrast ratio against the modal surface | ✓ |
role="dialog" — identifies the modal as a dialog.aria-modal="true" — informs assistive technology that content behind the modal is inert.aria-labelledby — references the modal title element's id.aria-label="Close modal" — required on the close button.
Output responsibility
Preserve Base UI semantics and test real content, focus visibility, and accessible names in the consuming application.
Validation evidence
Covered by its independent Storybook output plus browser, responsive, keyboard, Axe, and bundle checks.