// preview in
saved for every page

Dialog

A window over the page for one focused task, like editing a profile or inviting people. Focus moves in when it opens, stays inside, and goes back when it closes.

Built on Base UIPattern WAI-ARIA Dialog (Modal)Client componentSource ↗

Installation

Set up Camaleo once (Installation), then import what you need. It renders on the client.

import { Dialog, DialogTrigger, DialogContent, DialogTitle, DialogDescription, DialogClose, DialogActions } from "@camaleo/react";

Anatomy

A dialog-trigger button opens a dialog-backdrop and this popup. Both render in a portal that keeps the skin, mode and brand of the page around the trigger.

Examples

Every example is a real file you can copy. Open them: the dialog wears the skin, mode and brand from the bar above.

minimal · light · #3fa34d

Project: Atlas

A form that closes on submit

Click it. Focus starts in the field; submitting saves, closes the dialog, returns focus to the trigger and a status message says what changed.

Opened by something else

When the state lives elsewhere (a menu item, a shortcut), pass open and onOpenChange. Focus still goes back to whatever opened it.

Long content

Scroll only the body, so the title and the buttons stay in view. Wrap it in a ScrollArea: its viewport takes focus, so the keyboard can scroll it.

Doesn't close on outside click

When people type a lot, a stray click shouldn't throw it away. disablePointerDismissal keeps Esc and the buttons working.

A close button in the corner

Every dialog needs a visible way out: touch screen readers have no Esc. Put the icon button last in the code so focus starts on the content.

Wider for a table

There is one default width. Set inline-size on DialogContent when a table needs room; on a phone it still fits the screen.

In context

Dialogs hold a task that belongs to something on the page. These are whole pieces of UI you can copy, in the skin you picked.

Marina Costa

marina@ateliernorte.com

Product designer in Porto Alegre. Into type, maps and coffee.

Edit profileCard · Avatar · Dialog · Field

Team

4 members · 8 seats

  • Marina CostaOwner
  • Bruno Takahashi
  • Carla Mendes
  • Diego Alves

Invite teammatesCard · Avatar · Badge · Dialog · NativeSelect

October report

Revenue, churn and signups from October 1 to 31, 2026.

Export a reportCard · Dialog · RadioGroup · Switch

Guidelines

DoOne task, a few fields.

People finish it and get back to what they were doing.

Don’tA whole settings page in a dialog.

It scrolls, hides the page it belongs to and can't be linked. Use a page.

DoA title that names the task.

"Invite teammates" and "Send invites" say what happens, also to a screen reader that reads only the title.

Don’t"Information" and "OK".

The title is the dialog's accessible name. A generic one tells nobody where they are.

Props

PropTypeDefaultDescription
openboolean—Whether the dialog is open. Use it with onOpenChange to control the state.
defaultOpenbooleanfalseWhether the dialog starts open, without controlling the state.
modal"trap-focus" | booleantruetrue traps focus, locks scrolling and blocks outside clicks. trap-focus only traps focus. false leaves the page free.
onOpenChange(open, eventDetails) => void—Called when the dialog opens or closes. eventDetails.reason says why, for example escape-key or outside-press.
disablePointerDismissalbooleanfalseStops an outside click from closing the dialog. Esc and the buttons still work.

DialogTrigger, DialogTitle, DialogDescription and DialogClose take the props of their element, plus render.

DialogContent decides where focus goes:

PropTypeDefaultDescription
initialFocusboolean | RefObject | (openType) => HTMLElement | boolean—The element that gets focus on open. By default, the first focusable element inside the dialog.
finalFocusboolean | RefObject | (closeType) => HTMLElement | boolean—The element that gets focus on close. By default, the trigger or the element that had focus before.
Also accepts every prop of the underlying primitive and native attributes.

Styling hooks

Restyle it without forking: target the attributes, or set the tokens on a parent, a skin or the popup itself.

Attributes

data-openpresent while open
data-closedpresent while closed
data-starting-stylepresent while opening
data-ending-stylepresent while closing
data-nestedpresent on a dialog opened from another

Tokens it reads

Color

--cml-border--cml-fg--cml-fg-muted--cml-focus-offset--cml-focus-ring--cml-focus-width--cml-radius-surface--cml-shadow-surface--cml-surface-padding--cml-surface-raised

Shape & size

--cml-border-width--cml-gap

Type & motion

--cml-duration-base--cml-ease-standard--cml-font-display--cml-font-weight-display--cml-motion-distance--cml-motion-scale

This component only

--cml-backdrop--cml-dialog-border
/* a dialog that fills the screen on phones */
@media (max-width: 40rem) {
  .checkout [data-slot="dialog-popup"] {
    inline-size: 100vw;
    max-block-size: 100dvh;
    border-radius: 0;
  }
}

Accessibility

Enter / SpaceOn the trigger, opens the dialog. Focus moves to the first field or button inside.
TabMoves to the next focusable element. Focus stays inside the dialog: after the last one it wraps to the first.
Shift + TabMoves to the previous focusable element, wrapping to the last.
EscapeCloses the dialog. Focus goes back to the element that opened it.
Was this page helpful? · Edit this page on GitHub