Modal
Blocking dialog with header, body, and footer actions.
Properties
| Property | Attribute | Description | Type name | Type | Default |
|---|---|---|---|---|---|
open | open | When set, opens the dialog (`showModal`). | boolean | boolean | false |
showModal | show-modal | Alias for `open`. | boolean | boolean | false |
title | title | Header title when the `title` slot is empty. | string | string | "" |
subtitle | subtitle | Secondary line under the title. | string | string | "" |
titleTag | title-tag | Heading element for `title` (default `h5`). | DuoModalTitleTag | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "div" | "h5" |
size | size | `s` | `m` | `l` | `xl` | `fullscreen` (default `m`). | DuoModalSize | "s" | "m" | "l" | "xl" | "fullscreen" | "m" |
showClose | show-close | Renders a header close control. Omit on desktop-only Figma layouts. | boolean | boolean | true |
isMobile | is-mobile | Shows close and stacks footer actions in a column (column-reverse). | boolean | boolean | false |
mobile | mobile | Alias for `is-mobile`. | boolean | boolean | false |
mobileFooter | mobile-footer | Stacks footer actions vertically (column-reverse) without forcing the close button. | boolean | boolean | false |
accessibleLabel | aria-label | Accessible name when no visible title (rare; prefer `title` / `title` slot). | string | undefined | string | undefined | — |
Slots
| Name | Description |
|---|---|
default | Modal body content. |
title | Custom title markup (overrides the `title` attribute). |
header | Full header region (overrides built-in title / subtitle / close). |
footer | Action buttons (e.g. `<duo-button>`). |
Methods
| Method name | Parameters | Description |
|---|---|---|
show() => void | N/A | Programmatically open (sets `open` and `show-modal`). |
hide() => void | N/A | Programmatically close without emitting `close` (use click handlers that call `#emitClose` instead). |
Usage
Modal presents a centered, blocking dialog for confirmations, short forms, or focused tasks. It uses the native <dialog> element and mirrors legacy reactstrap Modal / ModalHeader / ModalBody / ModalFooter from Storybook React.
<duo-modal open title="Delete item?" subtitle="This cannot be undone."> <p>Your data will be removed permanently.</p> <duo-button slot="footer" variant="default" outline>Cancel</duo-button> <duo-button slot="footer" variant="danger">Delete</duo-button></duo-modal>Do
- Control visibility with
open(or legacyshow-modal) and listen forcloseto update app state. - Put primary actions in the
footerslot as real buttons or links. - Provide
title(or thetitleslot) so assistive tech getsaria-labelledby. - Set
show-close="false"on desktop-only layouts that match Figma (footer / Escape / backdrop only).
Don’t
- Don’t use for non-blocking hints — use Banner or inline patterns.
- Don’t nest modals without a strong reason — prefer a single focused step.
- Don’t rely on the modal as the only way to dismiss destructive flows without an explicit footer action.
Sizes
Duo T-shirt sizes (s, m, l, xl) — same naming as Button and Empty state, not Bootstrap sm/md.
| Size | When to use |
|---|---|
s | Compact confirmations; footer stacks (mobile / narrow). |
m | Default (~482px). |
l | Wider content or two-column body. |
xl | Full-width forms or rich previews. |
Set fullscreen for viewport-filling flows (e.g. multi-column order wizards). The body becomes a flex column with nested scroll regions. When fullscreen is active, the host exposes --duo-modal-header-height for sizing side panels.
Migration
See packages/ui/src/components/modal/MIGRATION.md in the repository for a step-by-step guide from the legacy React modal, including a Bootstrap → Duo size mapper (sm → s, md → m, etc.).
Events
The host fires a bubbling, composed close event with detail.reason: escape, backdrop, or close-button.