Skip to content

Dialog

A modal window for a focused task — a short form, a decision or details — that blocks the page until it is closed.

Preview

Installation

pnpm add @syntara/react @syntara/tokens
import { Dialog, DialogTrigger } from '@syntara/react';

Usage

import { Button, Dialog, DialogTrigger, TextField } from '@syntara/react';

export function EditProfile() {
  return (
    <DialogTrigger>
      <Button variant="outline">Edit profile</Button>
      <Dialog
        title="Edit profile"
        description="Visible to everyone in your workspace."
        footer={({ close }) => <Button onPress={close}>Save</Button>}
      >
        <TextField label="Full name" />
      </Dialog>
    </DialogTrigger>
  );
}

Examples

Sizes

sm, md (default) and lg set the maximum width.

Long content

The body scrolls while the header and footer stay visible.

Accessibility

KeysAction
EnterorSpaceOn the trigger: opens the dialog and moves focus into it.
TaborShiftTabMoves between focusable elements; focus is trapped inside the dialog.
EscapeCloses the dialog and returns focus to the trigger.
  • role="dialog" named by the title (h2) and described by the description.
  • Everything outside the dialog is hidden from assistive technology and page scrolling is locked while it is open.
  • The close button is labelled "Close" and comes last in the tab order, after the footer actions.
  • Overlays render in a portal on <body>, outside any ThemeScope. When it opens, the overlay looks up data-syntara-theme, data-syntara-scheme, data-syntara-density, dir and lang — each on the nearest ancestor that has it — starting from its trigger (or from where the component is rendered, when controlled), and copies them onto its own root — so it uses the same tenant tokens and direction as the page region it came from, and nested overlays inherit from their parent overlay.
  • Right-to-left: give the ThemeScope a locale (e.g. locale="ar-AE"), which sets dir and wraps React Aria's I18nProvider, so arrow keys and start/end placement flip as well as the layout.
  • The backdrop blurs the page lightly and dims it with backdrop-filter: brightness(0.6), so it recedes in light and dark schemes alike. The panel is glass (--syntara-glass-bg + backdrop blur); only text.default and text.subtle sit directly on it, the two roles the theme engine solves to 4.5:1 over any backdrop.
  • Motion: the panel fades in and springs up from 0.94 scale with an 8px rise, then the header, body and footer fade up in turn; on narrow screens it rises from the bottom edge instead. With prefers-reduced-motion it appears without moving.

Guidelines

Do

  • Keep dialogs to one task with one primary action in the footer.
  • Use the render function's close() for Cancel buttons.
  • Use a Sheet for long forms or filters that benefit from the full viewport height.

Don’t

  • Don't open a dialog from another dialog; replace the content or use a step flow.
  • Don't use a Dialog to confirm destructive actions — use AlertDialog.
  • Don't put essential information only in the description; it is supporting text.

API reference

Dialog

titleRequired
ReactNode

Heading at the top; also the dialog's accessible name.

description
ReactNode

Supporting text under the title, linked with aria-describedby.

children
ReactNode | (({ close }) => ReactNode)

Body content. Laid out as a grid with field-gap spacing; scrolls when taller than the viewport allows.

footer
ReactNode | (({ close }) => ReactNode)

Actions aligned to the inline end. Stacked full-width on narrow screens.

size
  • sm
  • mddefault
  • lg

Maximum width of the panel.

isDismissable
boolean

Whether clicking the backdrop closes the dialog.

Default true

isKeyboardDismissDisabled
boolean

Whether Escape is ignored.

Default false

isOpen / defaultOpen / onOpenChange
boolean / boolean / (isOpen: boolean) => void

Controlled or uncontrolled open state, for dialogs not placed inside a DialogTrigger.

role
  • dialogdefault
  • alertdialog

alertdialog also hides the close button and ignores backdrop clicks. Prefer AlertDialog.

className / style
string / CSSProperties

Applied to the panel.

DialogTrigger

childrenRequired
ReactNode

A pressable trigger (usually a Button) followed by the Dialog, Sheet, AlertDialog or Popover it opens.

isOpen / defaultOpen / onOpenChange
boolean / boolean / (isOpen: boolean) => void

Control the open state from outside.

Tokens

The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.

Colour4
text.defaultsurface.raisedtext.subtleborder.subtle
Type9
font.bodyfont.size.mdline-height.normalfont.headingfont.size.lgfont.weight.semiboldline-height.snugfont.heading-trackingfont.size.sm
Space and size7
space.16space.4card-insetspace.1control-heightfield-gapspace.2
Shape1
radius.container
Depth4
glass.blurshadow.overlayglass.opacityhairline
Motion7
motion.duration.normalmotion.easing-outmotion.duration.fastmotion.easingmotion.duration.springmotion.springmotion.duration.slow
Other2
sheenrim