Skip to content
Pinky UI

overlays

Dialog

A standard modal dialog — scrim, focus trap and Escape — with no shared-element continuity to its trigger.

  • overlays
  • dialog
  • modal
  • confirmation
Related pattern

Plain modal semantics; Morph remains the shared-element continuity route.

Live preview

Quick usage

usage
import { Dialog } from "@pinky-ui/systems";

<Dialog open={open} onOpenChange={setOpen} title="Delete project?" description="This cannot be undone." footer={<button onClick={confirm}>Delete</button>} />

Presets

Default

Restrained production behaviour.

Default behaviour

Quiet

Reduced intensity for dense product screens.

disabled={true}

Install

Add @pinky-ui/systems as a dependency, or use the CLI to copy this component's source directly into your project — no dependency to manage, fully editable.

shell
npm install @pinky-ui/systems

Prefer to run the whole repository locally instead?

repository
git clone https://github.com/florash/Pinky-UI.git
cd Pinky-UI
npm install
npm run dev

Props

PropTypeDefaultDescription
openbooleanControlled open state.
classNamestringStyles the outer interaction surface.

Accessibility

  • Keyboard and touch paths match the pointer interaction.
  • Stable labels and values remain available without motion.

Performance

  • The panel mounts only while open; no scroll-lock or listener persists when closed.

Reduced motion

Motion resolves immediately while semantics, focus and state remain unchanged.

When to use

  • Confirmations, settings and content opened from more than one place

When not to use

  • A dialog that should read as the same object as its trigger — use Morph

Skill

Purpose

A standard modal dialog — scrim, focus trap, Escape, focus restoration — with no shared-element continuity to whatever opened it. Use it for confirmations, settings and anything that can be opened from more than one place.

Interaction anatomy

  • Opens as a plain fade-and-scale surface, not a shared-layout expansion.
  • Escape closes it; clicking the scrim closes it; a visible close button closes it.
  • Focus moves into the panel on open and returns to whatever triggered it on close.
  • Tab is trapped inside the panel while open.

Good for

  • Confirmations, especially destructive ones
  • Settings panels and forms opened from more than one entry point
  • Any modal content where "this is the same object as the trigger" isn't true

Avoid for

  • A surface that should read as the same object as what opened it — that's [[morph]]
  • Content large enough to need its own scroll region and a persistent trigger relationship — consider a sheet instead on mobile

Usage

tsx
<Dialog
  open={open}
  onOpenChange={setOpen}
  title="Delete project?"
  description="This cannot be undone."
  footer={<button onClick={confirmDelete}>Delete</button>}
/>

Accessibility

  • role="dialog", aria-modal="true" and an accessible label are set automatically from title.
  • Focus moves to the first focusable element on open and Tab cannot escape the panel.
  • Closing — by Escape, scrim click or the close button — always returns focus to whatever had it before the dialog opened.

Performance

The panel and its scrim mount only while open is true; no scroll-lock, listener or trapped-tab handler persists once it's closed.

Composes with

Nothing needs to nest inside a Dialog beyond ordinary form and button content — it doesn't compose with other primitives so much as host them. Don't nest a Dialog inside a Morph panel or vice versa; pick one modal system for a given flow.

Open this skill on its own page