Skip to content

BbDialog

Presents modal dialogs for focused user interactions.

import { BbDialog } from 'bitboss-ui';

On this page

Props

NameTypeDefaultDescription
adaptiveboolean | undefined`config.adaptive` (`true`)

Adapt the surface to the viewport: render as a BbOffCanvas drawer on mobile instead of a centered dialog. The surface is resolved when the dialog opens and stays fixed for that session: resizing across the mobile breakpoint while open never swaps it. When unset, falls back to the global config.adaptive.

Three-state on purpose: unset falls back to config.adaptive, which ships on, so most projects never write it. The surface is latched when the dialog opens, so a resize mid-session never swaps a modal for a sheet.

See example
descriptionstring | undefined-

One short line under the title, in the header — the shadcn DialogDescription shape: same block as the title, lower hierarchy, muted. It is also what aria-describedby points at. That wiring was dropped in v3 along with the old description prop, on the reasoning that pointing it at arbitrary body content would read whole forms aloud — true, and the reason it aims at THIS element only. Keep it to a sentence; anything longer belongs in the body.

disabledboolean | undefinedfalse

Disable EVERY user-initiated dismissal: backdrop click, Escape AND the header close (×) button (denied attempts play the deny nudge). Use it while an operation is in flight (e.g. a save) so the dialog cannot be dismissed mid-work. Compare persistent, which blocks backdrop/Escape but leaves the × working. Programmatic close (v-model) is still allowed.

It guards dismissal and nothing else — the fields and buttons inside stay live, so disable them yourself while the work is in flight.

See example
eagerboolean | undefinedfalse

Render dialog content immediately instead of waiting for the first open.

Reach for it only when something has to read or measure the body before it has ever been open — a chart, a test, an SSR snapshot. Leaving it off is what keeps a page with many dialogs cheap.

See example
focusTargetstring | undefined-

CSS selector of the element to focus after opening. Defaults to the first tabbable element.

Chooses where focus starts, never whether it is contained: showModal() traps focus and returns it to the trigger either way. Point it at the primary action in a dialog whose real content is its footer.

See example
fullscreenboolean | "mobile" | undefined-

Display the dialog fullscreen. Use 'mobile' to restrict fullscreen to small devices.

Fills the viewport minus the page margin (20px, 14px on mobile) — deliberately not edge to edge. It overrides size rather than combining with it.

See example
hideCloseboolean | undefinedfalse

Hide the default close (×) button. The button is shown by default; set this to remove it (e.g. for a persistent dialog you don't want dismissible — the × otherwise still closes a persistent dialog).

Inverted from v2's show-close, which defaulted to true. A leftover :show-close="false" type-checks, falls through to $attrs, and the × comes back. Removing it also removes the last one-click way out, so the footer then owes the user a visible Cancel.

See example
modelValueboolean | undefinedfalse

Used by v-model to open / close the dialog. A dialog with no v-model starts closed.

offCanvasPropsPartial<BbOffCanvasProps> | undefined-

Props for the phone sheet. A pt inside it, object or colon keys, styles only that sheet. Overrides merged over the offcanvas (mobile) surface's props. The drawer defaults to a bottom, draggable, content-sized sheet (side: 'bottom', draggable: true, size: 'auto'); use this to override those, set other drawer-only options (stackGap), or override shared props whose meaning differs on the drawer (stack). Ignored on the desktop dialog surface. A pt inside it styles ONLY the sheet, in BbOffCanvas's part words (root, header, title, description, content, footer, close; state open), object ({ pt: { header: '…' } }) or colon keys ({ 'pt:header': '…' }) alike. It merges after what this component forwards to its sheet, so it wins a conflict and drops nothing — see the passthrough guide § Inside the sheet.

Merged last, so it wins over both the forwarded dialog props and the sheet's own defaults. The drawer's edge prop is sidedirection was its v2 name. Spelled canvas-props in v2.

See example
persistentboolean | undefinedfalse

Prevent closing via outside clicks or Escape key. The header close (×) button STILL closes a persistent dialog — that is the documented escape hatch; combine with hideClose to remove it, or use disabled to block every dismissal. Programmatic close is still allowed.

Blocks the backdrop and Escape only. The × still closes, by design, so persistent alone never makes a dialog unresolvable — pair it with hideClose when the choice must be made explicitly.

See example
ptPtMap<BbDialogPtPart, BbDialogPtState> | undefined-

Passthrough, object form: the same keys as the pt:* attributes without the prefix. See the pt:<part> row.

pt:closePtValue | undefined-
pt:close:fullscreenPtValue | undefined-
pt:close:openPtValue | undefined-
pt:contentPtValue | undefined-
pt:content:fullscreenPtValue | undefined-
pt:content:openPtValue | undefined-
pt:descriptionPtValue | undefined-
pt:description:fullscreenPtValue | undefined-
pt:description:openPtValue | undefined-
pt:footerPtValue | undefined-
pt:footer:fullscreenPtValue | undefined-
pt:footer:openPtValue | undefined-
pt:headerPtValue | undefined-
pt:header:fullscreenPtValue | undefined-
pt:header:openPtValue | undefined-
pt:rootPtValue | undefined-
pt:root:fullscreenPtValue | undefined-
pt:root:openPtValue | undefined-
pt:sheetPtValue | undefined-
pt:sheet:fullscreenPtValue | undefined-
pt:sheet:openPtValue | undefined-
pt:titlePtValue | undefined-
pt:title:fullscreenPtValue | undefined-
pt:title:openPtValue | undefined-
sizeResponsive<NonNullable<number | ({ size?: any; } & string) | keyof BbDialogSizes | undefined>> | undefined"sm"

Controls the maximum width of the dialog. Accepts named presets from 'xs' to '2xl', a custom CSS width string, or a numeric pixel value — or a per-breakpoint map (e.g. { default: 'sm', lg: 'lg' }) to switch width responsively.

A maximum width; the height always hugs the content. The v3 presets are narrower than v2's { sm: 384, md: 652, lg: 896 } and nothing warns — pin the old numbers with dialogDefaultSizes if a layout assumed them. Only the per-breakpoint map form re-resolves on a resize.

See example
stackstring | undefined-

Name of the stack this dialog belongs to. Dialogs sharing the same stack value replace one another: only the most recently opened stays visible while the others in the stack are hidden beneath it. Dialogs in different stacks — or with no stack — never affect each other and remain fully visible.

Members of a dialog stack replace one another. The identically named prop on BbOffCanvas does the opposite — parents step back and peek — which matters because an adaptive dialog forwards this value to its mobile sheet.

See example
titlestring | undefined-

Title text announced to assistive technologies. If omitted, no header is rendered (unless a header slot is supplied) and accessibility must be handled manually.

The accessible name, not just a heading: it wires aria-labelledby. Drop it and the header goes too, and naming the dialog becomes your job.

See example
transitionDurationnumber | undefined250

Controls the duration of the open and close animations.

Times both the animation and when hidden fires. Reopening inside that window keeps the dialog open rather than snapping it shut. Default is 250ms; v2 used 300.

See example

v-model

Values kept in sync through v-model.

NameTypeDescription
modelValueboolean | undefined

The only way to open a dialog. There is no exposed open(), and toggling the model back from @hide to veto a close is not the way to keep it open — that is what persistent is for.

See example

Events

NameTypeDescription
hidden(event: "hidden"): void

Fires after the close transition, transitionDuration later than hide. The place to tear down expensive content, not to cancel a close.

See example
hide(event: "hide"): void
show(event: "show"): void

The hook for seeding a draft: it fires as the model turns true, before the open transition, so the fields render already populated.

See example
shown(event: "shown"): void
update:modelValue(event: "update:modelValue", value: boolean): void

Slots

The listed properties are the ones exposed to the slot scope.

NameTypeDescription
defaultBbDialogDefaultSlotProps

Primary body content of the dialog, rendered in the scrollable body area.

The only region that scrolls, and it renders lazily — nothing inside exists until the first open unless you set eager.

See example
descriptionBbDialogDescriptionSlotProps

Replaces the description text under the title. Renders the header even when title and description are unset — a slotted description is a description, the same rule #title follows.

footerobject

Content rendered in the dialog footer, below the body area. Typically used for action buttons.

Stays pinned while the body scrolls. One primary action, aligned to the end, and a Cancel that is never gated on validity.

See example
headerBbDialogHeaderSlotProps

Replaces the entire dialog header section (title + close button) on both surfaces — the desktop <dialog> and the mobile off-canvas. Because it replaces the default close (✕), the slot owns closing: render your own control wired to the close prop, and wire titleId onto your title element to keep aria-labelledby intact. Providing this slot renders a header even when title is unset. Layout: the header container is a flex row (justify-content: space-between), so your root element is a flex item and shrinks to its content by default — give it flex: auto (Tailwind flex-1) to fill the width, or it renders squeezed against the left edge.

Replaces the header on both surfaces, so it also replaces the ×: render your own control wired to close, and put titleId on your heading or aria-labelledby points at nothing. The header is a flex row, so give your root flex-1 or it hugs the left edge. This is where a v2 #close or #description goes.

See example
titleBbDialogTitleSlotProps

Replaces the default title text inside the header. Remains linked to the dialog through aria-labelledby. Providing this slot renders the header even when title is unset — a slotted title is a title.

Changes from v2

Props removed or renamed in 3.0. If you are coming from v2, the answer is here.

  • canvasPropsoffCanvasPropsfails silently
  • compactremovedfails silently
  • hideHeaderremovedfails silently
  • overlayClassesremovedfails silently
  • panelClassesremovedfails silently
  • showClosehideClosefails silently