Components · Overlays
Dialog
BetaA focused task over the page, such as editing a meal.
- Source
components/ui/dialog.tsx - shadcn/ui Dialog
- Figma: not linked yet (Q13)
When to use
- For a short, focused task that belongs to the current page: edit a meal, rename a program, add a note.
- When the coach must finish or cancel before continuing.
When not to use
- To confirm a destructive action: use an alert dialog, which can't be dismissed by clicking outside.
- For long forms or tasks with several steps: use a page or a sheet.
- For information the coach might want next to the page: use a sheet or a side panel.
- To show a message: use an Alert or a Toast.
Anatomy
Open the dialog above to see its parts: the scrim over the page; the panel with a title, a description, the content, and a footer with actions (primary last); and the close button in the top end corner.
Variants
- Default: fits its content, up to
max-w-lg. - Scrolling content: the header and footer stay visible; only the body scrolls.
States
| State | Behavior | Token |
|---|---|---|
| Opening, closing | Fade and slight zoom; instant with reduced motion | duration-(--duration-base) |
| Open | Scrim over the page, panel on the modal layer | bg-foreground (dark: bg-background), opacity-(--opacity-overlay), z-(--z-modal) |
| Focus | Moves to the first focusable element; returns to the trigger on close | ring-ring/50 |
Usage guidelines
- Title says the task: "Edit meal". The primary button repeats the verb: "Save meal".
- Keep dialogs short. If it needs scrolling on a laptop, it probably wants to be a page.
- Don't open a dialog from a dialog.
Content guidelines
- Title: a verb and an object, sentence case, no punctuation.
- Description: one sentence on the consequence: "Changes apply to Sara's plan from tomorrow."
- Buttons: "Cancel" and a specific verb. Never "OK" or "Yes".
Accessibility
| Key | Does |
|---|---|
| Enter or Space on the trigger | Opens the dialog |
| Tab / Shift+Tab | Cycles through the dialog only; focus is trapped |
| Escape | Closes the dialog and returns focus to the trigger |
- Rendered as
role="dialog"witharia-modal, labelled by the title and described by the description. A screen reader announces "Edit meal, dialog, Changes apply to Sara's plan from tomorrow." - Content behind the dialog is hidden from assistive technology while it's open.
- Always include a
DialogTitle, even if visually hidden.
RTL
The close button sits at the top end corner (left in RTL) and footer actions align to the end. The panel is centered, so nothing else changes.
Tokens used
Read from components/ui/dialog.tsx at build time, so this list changes when the component does. Opacity modifiers such as /90 are shadcn hover and focus tints on top of the token.
| Token | Utilities | SuperCoach | Coach |
|---|---|---|---|
| --accent | bg-accent | brand-50 | orange-50 |
| --background | bg-background | white | white |
| --foreground | bg-foreground | neutral-950 | neutral-950 |
| --muted-foreground | text-muted-foreground | neutral-600 | neutral-600 |
| --ring | ring-ring | brand-600 | orange-500 |
- Layer
z-(--z-modal) - Layer
z-(--z-overlay) - Motion
duration-(--duration-base) - Opacity
opacity-(--opacity-overlay) - Radius
rounded-lg - Radius
rounded-sm - Shadow
shadow-lg - Type
text-lg - Type
leading-none - Type
font-semibold - Type
text-sm
Props
<Dialog>
Also takes every prop of Radix Dialog.Root
No props of its own.
<DialogTrigger>
Also takes every prop of Radix Dialog.Trigger
No props of its own.
<DialogClose>
Also takes every prop of Radix Dialog.Close
No props of its own.
<DialogContent>
Also takes every prop of Radix Dialog.Content
| Prop | Type | Default |
|---|---|---|
| showCloseButton? | boolean | true |
<DialogHeader>
Also takes every prop of <div>
No props of its own.
<DialogFooter>
Also takes every prop of <div>
| Prop | Type | Default |
|---|---|---|
| showCloseButton? | boolean | false |
<DialogTitle>
Also takes every prop of Radix Dialog.Title
No props of its own.
<DialogDescription>
Also takes every prop of Radix Dialog.Description
No props of its own.
Replaces in coach-admin
From the read-only audit of coach-admin: 153 style values, 204 uses.
coach-admin has Bootstrap modals, antd modals and several custom modal components; all of them become this component or an alert dialog.
Classes and selectors
.modal-footer30.modal-content28.modal-title13.modal-body7.modal-header5.modal-body:not(:has(.assignPackage-container)) ul3.modal .modal-header .close2.modal-header .close2.modal-backdrop1.modal-exc-names1.modal-programs-names1.modal-dialog1
Files
src/components/formComponents/formModals/UpgradeWarningModal.scss93src/reusableComponents/modalComponents/GenericPopupModal.scss20src/components/formComponents/formModals/ModalContainer.js2src/components/formComponents/formModals/WarningModal.js1src/reusableComponents/modalComponents/ModalContainer.js1src/reusableComponents/modalComponents/SelectTypeModal.tsx1
The inventory lists the first 12 locations of each value, so file counts are a lower bound.
SCSS variables in these styles
$spacing-lgnot a color: spacing, size or layout$spacing-xlnot a color: spacing, size or layout$paid-experience-muted-text-color→ nearest neutral-500 (no semantic token)$spacing-xsnot a color: spacing, size or layout$spacing-smnot a color: spacing, size or layout$spacing-mdnot a color: spacing, size or layout$spacing-2xlnot a color: spacing, size or layout$paid-experience-radius-mdnot a color: spacing, size or layout$paid-experience-check-bg→ nearest brand-50 (no semantic token)$spacing-2xsnot a color: spacing, size or layout$primary-color→--primary$border-color→--border