ComponentsDialog

Dialog

component

A focused modal — one screen of content, one primary action in a sunken foot.

Delete project
Open the confirmation dialog.
Best practices
Do
  • One primary action per dialog; the action row is .dialog__foot — secondaries as .btn--ghost, the primary trailing.
  • A destructive confirmation wears .btn--danger and names the object ('Delete 3 invoices'), never a bare 'Are you sure?'.
  • Keep it to one screen of content — a longer flow belongs in a .sheet or its own page.
Don't
  • Don't stack dialogs — replace the content or step within the one you have.
  • Don't use a dialog for passive status; that is a .toast or .banner.

Composes: Card · Button

Best practices

Do
  • One primary action per dialog; the action row is .dialog__foot — secondaries as .btn--ghost, the primary trailing.
  • A destructive confirmation wears .btn--danger and names the object ('Delete 3 invoices'), never a bare 'Are you sure?'.
  • Keep it to one screen of content — a longer flow belongs in a .sheet or its own page.
Don't
  • Don't stack dialogs — replace the content or step within the one you have.
  • Don't use a dialog for passive status; that is a .toast or .banner.

Recipe CSS

This is the exact CSS your kit ships for Dialog — token-driven, so it re-themes with every knob. Get it (and the rest) from the configurator's Use kit panel.

/* === Dialog === */
.dialog {
  border-radius: var(--k-radius-lg);
  border: var(--k-hairline, 1px solid var(--k-border));
  background: var(--k-surface-overlay, var(--k-surface-raised));
  /* Never exceed the viewport on a narrow phone (a content-sized dialog has no
     intrinsic width cap otherwise). The frame demo's 90% rule still wins inside it. */
  max-width: calc(100vw - 2rem);
  /* Box padding floor (shadcn dialog = p-6 = 24); gap on the grid --k-space. */
  padding: var(--k-pad, 24px);
  box-shadow: var(--k-shadow-lg);
  display: flex;
  flex-direction: column;
  gap: var(--k-space, 16px);
  /* Scale-in enter — slower than popover (--k-dur-slow), more presence */
  transform-origin: center;
  animation: k-scale-in var(--k-dur-slow, 320ms) var(--k-ease-out, cubic-bezier(.05,.7,.1,1)) backwards;
}
/* C3 — the dialog's own footer (it had none; actions floated as a bare row).
 * Full-bleed like .card__foot: cancels the dialog's --k-pad so the top divider
 * reaches both edges and CLOSES the box, actions pinned right on the tight gap. */
.dialog__foot {
  margin: var(--k-s-4) calc(-1 * var(--k-pad, 24px)) calc(-1 * var(--k-pad, 24px));
  padding: var(--k-s-16) var(--k-pad, 24px);
  border-top: var(--k-divider);
  border-bottom-left-radius: var(--k-radius-lg);
  border-bottom-right-radius: var(--k-radius-lg);
  display: flex;
  justify-content: flex-end;
  gap: var(--k-gap, var(--k-s-8));
}
/* Tall-dialog scroll — a content-heavy dialog must not push its footer off-screen.
 * .dialog caps its height to the viewport; .dialog__body is the scroll region —
 * min-height:0 lets it shrink in the flex column so the BODY scrolls while the
 * title + .dialog__foot stay put (the .sheet already works this way). Negative
 * inline margin lets the scrollbar reach the edge; content keeps the --k-pad gutter. */
.dialog { max-block-size: calc(100dvh - 2rem); }
.dialog__body {
  min-height: 0; overflow: auto; overscroll-behavior: contain;
  margin-inline: calc(-1 * var(--k-pad, 24px)); padding-inline: var(--k-pad, 24px);
}
/* Frame for modal demo — overlay backdrop, dialog centered inside */
.dialog-frame {
  position: relative;
  min-height: 220px;
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-md);
  background: var(--k-surface-sunken);
  overflow: hidden;
  display: grid;
  place-items: center;
  padding: var(--k-s-16);
}
.dialog-frame__backdrop {
  position: absolute;
  inset: 0;
  background: var(--k-scrim);
  animation: k-fade-in var(--k-dur, 200ms) var(--k-ease-out, cubic-bezier(.05,.7,.1,1)) backwards;
}
.dialog-frame .dialog { position: relative; max-width: 90%; }