Skip to content
SuperCoach

Components · Overlays

Dialog

Beta

A focused task over the page, such as editing a meal.

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

StateBehaviorToken
Opening, closingFade and slight zoom; instant with reduced motionduration-(--duration-base)
OpenScrim over the page, panel on the modal layerbg-foreground (dark: bg-background), opacity-(--opacity-overlay), z-(--z-modal)
FocusMoves to the first focusable element; returns to the trigger on closering-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.
Do.Cancel first, the primary action last, at the end of the footer.
Don't.Two primary buttons with vague labels, in the wrong order.

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

KeyDoes
Enter or Space on the triggerOpens the dialog
Tab / Shift+TabCycles through the dialog only; focus is trapped
EscapeCloses the dialog and returns focus to the trigger
  • Rendered as role="dialog" with aria-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.

Semantic color tokens used by this component
TokenUtilitiesSuperCoachCoach
--accentbg-accentbrand-50orange-50
--backgroundbg-backgroundwhitewhite
--foregroundbg-foregroundneutral-950neutral-950
--muted-foregroundtext-muted-foregroundneutral-600neutral-600
--ringring-ringbrand-600orange-500
  • Layerz-(--z-modal)
  • Layerz-(--z-overlay)
  • Motionduration-(--duration-base)
  • Opacityopacity-(--opacity-overlay)
  • Radiusrounded-lg
  • Radiusrounded-sm
  • Shadowshadow-lg
  • Typetext-lg
  • Typeleading-none
  • Typefont-semibold
  • Typetext-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

DialogContent props
PropTypeDefault
showCloseButton?booleantrue

<DialogHeader>

Also takes every prop of <div>

No props of its own.

<DialogFooter>

Also takes every prop of <div>

DialogFooter props
PropTypeDefault
showCloseButton?booleanfalse

<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.scss93
  • src/reusableComponents/modalComponents/GenericPopupModal.scss20
  • src/components/formComponents/formModals/ModalContainer.js2
  • src/components/formComponents/formModals/WarningModal.js1
  • src/reusableComponents/modalComponents/ModalContainer.js1
  • src/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

Button, Toast, Elevation.