Dialog
Overlays & menusBrick 0.1.2

Dialog

Dialog presents a focused modal task or information surface above the current page. Atom owns modal state, focus, dismissal, portals, background isolation, scroll containment, and presence; Brick supplies the finished visual anatomy.

Live example

Built from the published package

Interactive
Choose with confidence

Know when Dialog is the right part

Use it when

Use Dialog for short forms, settings, details, previews, and multi-control tasks that temporarily block interaction with the page.

Choose another path when

Use a future AlertDialog for urgent destructive confirmation, Drawer for side-attached modal content, and Popover or Menu when the page must remain interactive.

Dialog is modal-only. It does not own application workflow, submission, data loading, routing, or generated action copy.

Installation and imports

tsx
import { Dialog } from "@flowstack-ui/brick/dialog";
import "@flowstack-ui/brick/styles.css";

The complete stylesheet above is the recommended default. For a measured route-aware build, replace it with the shared foundation and this component's stylesheet:

tsx
import "@flowstack-ui/brick/styles/core.css"; // once at the application root
import "@flowstack-ui/brick/styles/dialog.css";

Add the modular stylesheet for every other Brick component the route renders. Do not combine modular styles with styles.css or tokens.css.

The namespace is also available from @flowstack-ui/brick. Advanced consumers may import the canonical direct parts from the Dialog subpath.

Quick start

tsx
import { Button } from "@flowstack-ui/brick/button";
import { Dialog } from "@flowstack-ui/brick/dialog";

export function ProfileDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger asChild>
        <Button>Edit profile</Button>
      </Dialog.Trigger>
      <Dialog.Portal>
        <Dialog.Overlay />
        <Dialog.Content>
          <Dialog.Header>
            <Dialog.Title>Edit profile</Dialog.Title>
            <Dialog.Description>
              Update the information visible to your team.
            </Dialog.Description>
          </Dialog.Header>
          <Dialog.Body>{/* form */}</Dialog.Body>
          <Dialog.Footer>
            <Dialog.Close asChild>
              <Button tone="neutral" variant="outline">Cancel</Button>
            </Dialog.Close>
            <Button form="profile-form" type="submit">Save</Button>
          </Dialog.Footer>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}

Overlay and Content must remain siblings. Nesting Content inside Overlay places the dialog inside an accessibility-hidden subtree and is rejected by Atom.

Visual recipes and states

Content supports sm, md, and lg; md is the default. Atom's public data-state, data-positioned, and disabled outputs drive open, closed, positioned, and unavailable styling. Dialog intentionally has no tone, variant, placement, fullscreen, or arbitrary-width prop.

Examples

Portals, scopes, and Branch

A default body portal uses document-level tokens. To retain a scoped theme, portal into an element inside that scope or render inline:

tsx
<Dialog.Portal container={scopedLayerElement}>...</Dialog.Portal>
<Dialog.Portal disabled>...</Dialog.Portal>

Register third-party content that must portal outside Content:

tsx
<ThirdParty.Portal>
  <Dialog.Branch asChild>
    <ThirdParty.Content />
  </Dialog.Branch>
</ThirdParty.Portal>

Prefer placing the third-party portal inside Content when its API supports a container. Branch preserves the third party's keyboard model while keeping it inside the active modal boundary.

Public contract

API

Start with the public parts and root options below. Components with multiple parts separate each area into its own named subsection.

Public exports are the Dialog namespace; named DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter, DialogClose, and DialogBranch parts; and their corresponding prop types plus DialogSize.

ts
DialogRootProps
DialogTriggerProps
DialogPortalProps
DialogOverlayProps
DialogContentProps
DialogHeaderProps
DialogTitleProps
DialogDescriptionProps
DialogBodyProps
DialogFooterProps
DialogCloseProps
DialogBranchProps

Root

Forwards Atom's open, defaultOpen, onOpenChange, closeOnEscape, closeOnBackdropClick, disabled, and keepMounted contract. Root renders no DOM element and has no ref.

Content

PropValuesDefault
sizesm, md, lgmd

Size changes the preferred maximum inline measure only. Content also forwards native ARIA, role, initialFocus, and finalFocus supported by Atom.

Portal and Overlay

Portal forwards container and disabled. A container must be a same-document HTMLElement. Overlay forwards its independent disabled dismissal control.

Trigger, Close, and Branch

These parts forward Atom's asChild and render composition. The composed element must accept the merged props and ref and retain valid semantics.

Title

Title defaults to h2; as accepts h1 through h6. A visible Title supplies the generated accessible name. Consumers that intentionally omit it must add an explicit native aria-label or aria-labelledby to Content.

Shared responsibility

Accessibility

Title supplies the accessible name and Description supplies the optional accessible description. If Title is intentionally omitted, Content requires an explicit native aria-label or aria-labelledby. Consumers keep action labels clear and preserve a logical source and focus order.

Keyboard and focus

InputResult
Enter or Space on TriggerOpens through native/composed control behavior
TabAdvances within the active modal and owned branches
Shift+TabMoves backward within the active modal
EscapeCloses only the top Dialog when enabled

Focus enters according to Atom's interaction-aware policy and restores to an explicit finalFocus, the prior connected target, or the mounted Trigger. Touch opening avoids focusing the first input automatically unless native autoFocus or explicit initialFocus requests it.

Responsive behavior

Content is centered and bounded by safe-area-aware viewport gaps and dynamic viewport height. Body uses contained scrolling so Header and Footer remain reachable with long content. All sizes shrink to available width. Footer wraps without reversing action or focus order, and logical properties support RTL.

Stable visual contract

Styling and tokens

Customization

Prefer the documented size prop and semantic tokens first, then the five component tokens above. Use part-level classes, slots, className, and style only for localized needs that cannot be expressed through those supported contracts.

Tokens and CSS hooks

Stable classes and default slots are:

PartClassSlot
Trigger.brick-dialog-triggerdialog-trigger
Overlay.brick-dialog-overlaydialog-overlay
Content.brick-dialog-contentdialog-content
Header.brick-dialog-headerdialog-header
Title.brick-dialog-titledialog-title
Description.brick-dialog-descriptiondialog-description
Body.brick-dialog-bodydialog-body
Footer.brick-dialog-footerdialog-footer
Close.brick-dialog-closedialog-close
Branch.brick-dialog-branchmodal-branch

Content exposes these component tokens:

  • --brick-dialog-max-inline-size
  • --brick-dialog-max-block-size
  • --brick-dialog-space
  • --brick-dialog-radius
  • --brick-dialog-shadow

Atom owns data-state and data-positioned; Content adds data-size. Brick honors reduced motion and forced colors. Consumers own accessibility and layout verification after arbitrary class, style, or token overrides.

Advanced reference

Open these details only when you need to inspect DOM ownership, native forwarding, or lower-level composition.

Maintainer resources

Tests, playground evidence, source notes, and release history remain available without crowding the plug-and-play guide.