Modals are dialogs displayed over inert content that allow users to complete a singular, focused task without leaving the page.

Modal

Usage

  • Use Modal for tasks requiring the user's immediate attention: action confirmations, short forms, feature moments, and supplementary workflows.
  • Never nest Modals. Rework the flow, move content inline, or use a Popover instead.
  • Use Modal when users must confirm a task or provide input before continuing. Use Drawer for content with less contextual relation to the main page. Use Popover for small, inline supplementary content.
  • Keep Modal content focused on a single task. Move multi-step workflows or lengthy content to a dedicated page.

Common Patterns

  • Confirmation -- hide the close button (shouldShowCloseButton={false}) and provide primary/secondary footer actions.
  • Destructive confirmation -- hide the close button, require additional user input (e.g., a text field), and disable the primary action (isDisabled) until input requirements are met. Mark the primary action as isDanger.
  • Form -- disable overlay click dismissal (shouldCloseOnOverlayClick={false}) to prevent accidental data loss. Place submit and cancel buttons in the footer.
  • Save/Discard -- hide the close button and provide three footer actions: go back, discard (isDanger), and save (primary).
  • Informational -- display read-only content with a single primary dismiss button in the footer.
  • Feature moment -- use ModalSplit for a branded graphic panel alongside the body content. Hide the close button and include a single call-to-action.

Types

  • Standard -- single-column layout with optional header and footer, and scrollable body content. This is the default.
  • Split -- two-column layout using ModalSplit. Place ModalSplit before ModalBody for a left panel, or after for a right panel.

Appearance

  • Size (size, default "md") -- use "md" for most use cases. Use "sm" for shorter content and smaller viewports.
  • Close button (shouldShowCloseButton, default true) -- hide it only when the Modal requires an explicit user decision. Always provide a cancel action in the footer when the close button is hidden.
  • Inset (ModalInset) -- renders content edge-to-edge, ignoring the Modal's default padding. Use for full-width backgrounds, images, or dividers.

Behaviors

  • Dismissal methods -- close button, action button, overlay click, and the Escape key. All are enabled by default.
  • Non-dismissable shortcut (isDismissable={false}) -- sets shouldShowCloseButton, shouldCloseOnOverlayClick, and shouldCloseOnEscapePress to false in one prop. Individual props still override isDismissable when explicitly set.
  • Overlay click dismissal (shouldCloseOnOverlayClick, default true) -- disable for form Modals to prevent accidental data loss.
  • Escape key dismissal (shouldCloseOnEscapePress, default true) -- disable alongside the close button for Modals that require an explicit choice.
  • Scrolling (shouldScrollInViewport, default false) -- by default the Modal body scrolls while the header and footer remain fixed. Set to true to scroll the entire Modal within the viewport.
  • Animation (animationTransitionDuration) -- fade and downward slide. The duration is customizable.

Content

  • Write titles in sentence case. Use descriptive, action-oriented titles (e.g., "Delete campaign") -- never vague titles like "Are you sure?"
  • Write footer action labels with specific verbs that match the Modal's purpose. Mirror the title's verb when possible.
  • Never use generic labels like "OK" or "Submit" when a specific verb is available.
  • Keep body content concise and focused on the task at hand.

Implementation Notes

  • Compose Modal with subcomponents: ModalBody for scrollable body content, ModalFooter for action buttons, ModalSplit for split-layout panels, and ModalInset for edge-to-edge content.
  • ModalFooter wraps ButtonGroup with groupVariant="spaced" -- pass Button components directly as children.
  • ModalSplit panel side is controlled by DOM order relative to ModalBody, not a prop.
  • ModalInset accepts inheritPadding (default true when used inside ModalBody) to control whether it inherits the Modal's padding.
  • Control visibility with isOpen and onClose. onClose receives (event, reason) where reason is "close-button-press", overlay click, or escape press.
  • Lifecycle callbacks: onOpen fires when opened, onCloseComplete fires after the close animation finishes.
  • initialFocusIndex overrides which focusable element receives focus when the Modal opens (default is the first).
  • state prop accepts all Modal props as a single object for use with a ModalsManager pattern.
  • title prop renders the default header. Omit title to render no header.
  • Ember invocation: <PlumaModal @isOpen={{this.isOpen}} @onClose={{this.handleClose}} @title="Modal title">. Subcomponents: <PlumaModalBody>, <PlumaModalFooter>, <PlumaModalSplit>, <PlumaModalInset>.
  • Ember default block yields a context value: <PlumaModal ... as |ctx|>.
  • Global defaults for animationTransitionDuration can be set via PlumaProvider's componentConfig.PlumaModal.

On this page