ComponentsDialog
Dialog
componentA 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.
Delete project?
This permanently removes:
- 14 source files
- 3 environments
- all deploy history
- 2 connected domains
- the staging database
- every API token
- 8 team invitations
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%; }