<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Dialog } from "@dicehub/kappa/components/dialog";
</script>
<template>
<Dialog.Root>
<Dialog.Trigger as-child>
<Button variant="outline">Edit run details</Button>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Edit run details</Dialog.Title>
<Dialog.Description>Change the label used in reports and result archives.</Dialog.Description>
</Dialog.Header>
<label>Run label <input value="PIMPLE baseline 07" /></label>
<Dialog.Footer>
<Dialog.Close as-child><Button variant="secondary">Cancel</Button></Dialog.Close>
<Dialog.Close as-child><Button variant="primary">Save changes</Button></Dialog.Close>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
</template>Installation
Barrel
import {
Dialog,
DialogRoot,
DialogContent,
DialogTitle,
DialogDescription,
DialogClose,
type DialogProps,
type DialogSize,
} from "@dicehub/kappa";Granular
import {
Dialog,
DialogRoot,
DialogContent,
DialogTitle,
DialogDescription,
DialogClose,
type DialogProps,
type DialogSize,
} from "@dicehub/kappa/components/dialog";Usage
Put Trigger and Content inside Dialog.Root.Content supplies the portal layers and close control. UseHeader and Footer for stable title and action regions.
<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Dialog } from "@dicehub/kappa/components/dialog";
</script>
<template>
<Dialog.Root>
<Dialog.Trigger as-child><Button>Open dialog</Button></Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Simulation summary</Dialog.Title>
<Dialog.Description>Run 4189 completed successfully.</Dialog.Description>
</Dialog.Header>
<p>Review the generated fields and reports.</p>
<Dialog.Footer>
<Dialog.Close as-child><Button variant="primary">Done</Button></Dialog.Close>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
</template>Composition
Dialog.Root
├── Dialog.Trigger
└── Dialog.Content
├── Dialog.Header
│ ├── Dialog.Title
│ └── Dialog.Description
├── Content
├── Dialog.Footer
└── Dialog.CloseArk UI Dialog owns open state, modality, focus trapping, dismissal, aria relationships, presence, and nested layers. Kappa owns the compound API, composed portal surface, close control, layout, sizing, semantic tokens, and motion.
DialogandDialog.Rootare the same root component, matching other Kappa compound APIs.Contentteleports tobody; pass:teleport="false"for an in-place test or constrained integration.BackdropandPositionerremain public for selectors and advanced styling, whileContentcomposes them.- Use
as-childon Trigger and Close when a Kappa Button owns the visual element.
<script setup>
import {
DialogRoot,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose,
} from "@dicehub/kappa/components/dialog";
</script>
<template>
<DialogRoot>
<DialogTrigger>Open</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Title</DialogTitle>
<DialogDescription>Description</DialogDescription>
</DialogHeader>
<DialogFooter><DialogClose>Close</DialogClose></DialogFooter>
</DialogContent>
</DialogRoot>
</template>Reference Differences
The main Dialog export is the root, not the content surface. It supports header, footer, custom-close, no-close, fixed-action, scrolling, and RTL composition patterns. The Kappa close label is configurable for localization.
Behavior and Focus
- The first focusable element receives focus. Use
initialFocusElwhen the safest target is not first. - Escape and allowed outside interactions close a normal dialog. Focus returns to the opening trigger.
role="alertdialog"disables outside dismissal by default. Put focus on the least destructive action.disablePointerDismissalprotects a confirmation without changing its role.- Keep
Dialog.Rootmounted. Use controlled state plus the default lazy mount and exit unmount behavior. - For a child popup that teleports to
body, use its lazy mount and exit unmount options. This keeps the popup in the active modal accessibility tree. - For a non-modal surface, set
:modal="false",:trap-focus="false", and:show-backdrop="false".
Examples
Basic
Content supplies the portal, backdrop, centered positioner, surface, and default close control.
<Dialog.Root>
<Dialog.Trigger as-child><Button>Open dialog</Button></Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Simulation summary</Dialog.Title>
<Dialog.Description>Run 4189 completed in 02:14:38.</Dialog.Description>
</Dialog.Header>
</Dialog.Content>
</Dialog.Root>Alert Dialog
Use alertdialog for a destructive decision. Kappa disables outside dismissal and the example removes the corner close control.
<Dialog.Root role="alertdialog">
<Dialog.Trigger as-child><Button variant="destructive">Delete run</Button></Dialog.Trigger>
<Dialog.Content :show-close-button="false">
<Dialog.Header>
<Dialog.Title>Delete run 4189?</Dialog.Title>
<Dialog.Description>This action cannot be undone.</Dialog.Description>
</Dialog.Header>
<Dialog.Footer>
<Dialog.Close as-child><Button variant="secondary">Keep run</Button></Dialog.Close>
<Dialog.Close as-child><Button variant="destructive">Delete permanently</Button></Dialog.Close>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>Confirmation
disablePointerDismissal keeps an ordinary dialog open after backdrop clicks. Escape still works unless closeOnEscape is false.
<Dialog.Root disable-pointer-dismissal>
<Dialog.Trigger as-child><Button variant="warning">Stop solver</Button></Dialog.Trigger>
<Dialog.Content :show-close-button="false">
<Dialog.Title>Stop the active solver?</Dialog.Title>
<Dialog.Footer>
<Dialog.Close as-child><Button>Continue run</Button></Dialog.Close>
<Dialog.Close as-child><Button variant="warning">Stop solver</Button></Dialog.Close>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>Controlled
Bind v-model:open when application state must own visibility. Do not conditionally remove Dialog.Root.
<script setup>
import { ref } from "vue";
import { Dialog } from "@dicehub/kappa/components/dialog";
const open = ref(false);
</script>
<template>
<Button @click="open = true">Open controlled dialog</Button>
<Dialog.Root v-model:open="open">
<Dialog.Content>
<Dialog.Title>Controlled state</Dialog.Title>
<Button @click="open = false">Done</Button>
</Dialog.Content>
</Dialog.Root>
</template>Sizes and Custom Width
Use sm, base, lg, or xl. Override --kappa-dialog-inline-size for a one-off cap; the viewport limit still applies.
<Dialog.Content size="sm">...</Dialog.Content>
<Dialog.Content size="base">...</Dialog.Content>
<Dialog.Content size="lg">...</Dialog.Content>
<Dialog.Content size="xl">...</Dialog.Content>
<Dialog.Content style="--kappa-dialog-inline-size: 40rem">...</Dialog.Content>Custom Close Button
The close slot replaces the built-in icon. Keep Dialog.Close so Ark UI still owns dismissal and focus restoration.
<Dialog.Content>
<template #close>
<Dialog.Close as-child>
<Button size="xs" variant="ghost">Dismiss</Button>
</Dialog.Close>
</template>
<Dialog.Title>Custom close control</Dialog.Title>
</Dialog.Content>No Close Button
Disable the corner control when a visible action row gives the user a clearer next step.
<Dialog.Content :show-close-button="false">
<Dialog.Title>Review required</Dialog.Title>
<Dialog.Footer>
<Dialog.Close as-child><Button>Acknowledge</Button></Dialog.Close>
</Dialog.Footer>
</Dialog.Content>Native Form Control
Native inputs and selects keep their normal keyboard behavior inside the focus trap.
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Configure resource</Dialog.Title>
<Dialog.Description>Choose the target region.</Dialog.Description>
</Dialog.Header>
<label>
Region
<select v-model="region">
<option value="eu-central">EU Central · Frankfurt</option>
<option value="us-east">US East · Virginia</option>
</select>
</label>
</Dialog.Content>With Combobox
Lazy mount a teleported Kappa Combobox so its popup stays above the dialog and inside the active modal accessibility tree.
<Dialog.Content size="lg">
<Dialog.Header>
<Dialog.Title>Move simulation</Dialog.Title>
</Dialog.Header>
<Combobox
v-model="region"
:items="regions"
label="Destination region"
lazy-mount
placeholder="Search regions"
unmount-on-exit
/>
</Dialog.Content>Sticky Footer
Give the content a block size and let a min-block-size: 0 body scroll. Header and Footer remain fixed flex children.
Scrollable Content
Keep long copy in a named scroll region. The surface stays inside the dynamic viewport limit.
<Dialog.Content>
<Dialog.Header><Dialog.Title>Solver notes</Dialog.Title></Dialog.Header>
<div class="scroll-region" tabindex="0">Long content...</div>
</Dialog.Content>
<style scoped>
.scroll-region { max-block-size: 16rem; overflow-y: auto; }
</style>Nested Dialog
Place another Root inside the parent Content. Ark UI coordinates focus, dismissal, aria hiding, and layer order.
<Dialog.Root>
<Dialog.Trigger>Open parent</Dialog.Trigger>
<Dialog.Content>
<Dialog.Title>Run settings</Dialog.Title>
<Dialog.Root>
<Dialog.Trigger>Edit advanced settings</Dialog.Trigger>
<Dialog.Content size="sm">
<Dialog.Title>Advanced settings</Dialog.Title>
</Dialog.Content>
</Dialog.Root>
</Dialog.Content>
</Dialog.Root>Right-to-left
Logical spacing keeps the close control and action layout correct in RTL content.
<Dialog.Content dir="rtl">
<Dialog.Header>
<Dialog.Title>نتائج المحاكاة</Dialog.Title>
<Dialog.Description>النتائج جاهزة للمراجعة.</Dialog.Description>
</Dialog.Header>
<Dialog.Footer><Dialog.Close>تم</Dialog.Close></Dialog.Footer>
</Dialog.Content>Accessibility
- Render a visible
Titleand normally aDescription. UseariaLabelonly when no title is visible. - Every icon-only close control needs an accessible label.
closeLabellocalizes the built-in control. - Tab and Shift+Tab stay inside a modal dialog. Escape closes it when enabled.
- Do not use an alert dialog for ordinary information. Reserve it for an urgent decision or acknowledgement.
- Give long scroll regions
tabindex="0"when keyboard users must scroll them independently. - All focus indicators remain visible. Open and close motion stops under reduced-motion preferences.
API Reference
Dialog.Root
| Prop | Type | Default | Description |
|---|---|---|---|
open / defaultOpen | boolean | false | Controlled or initial visibility. Pair open with update:open. |
role | "dialog" | "alertdialog" | "dialog" | Sets the accessible role. Alert dialogs disable outside dismissal by default. |
ariaLabel | string | - | Accessible name when no visible Title is rendered. |
closeOnEscape | boolean | true | Closes the top dialog when Escape is pressed. |
closeOnInteractOutside | boolean | true | Closes after an allowed outside interaction. |
disablePointerDismissal | boolean | false | Forces outside dismissal off without changing the dialog role. |
modal | boolean | true | Blocks pointer and assistive technology access outside the dialog. |
trapFocus | boolean | true | Keeps keyboard focus inside while open. |
preventScroll | boolean | true | Prevents the page behind the modal from scrolling. |
restoreFocus | boolean | true | Returns focus to the opening trigger or finalFocusEl. |
initialFocusEl / finalFocusEl | () => HTMLElement | null | - | Overrides initial and restored focus targets. |
lazyMount / unmountOnExit | boolean | true | Defers dialog DOM and removes it after the exit motion. |
triggerValue / defaultTriggerValue | string | null | null | Tracks which value-bearing Trigger opened the dialog. |
persistentElements | (() => Element | null)[] | - | Outside elements that stay interactive and do not dismiss the layer. |
id / ids | string / object | generated | Overrides machine and individual part identifiers. |
Dialog.Content
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "base" | "lg" | "xl" | "base" | Sets the preferred inline size while keeping the viewport cap. |
showCloseButton | boolean | true | Renders the built-in corner close control. |
closeLabel | string | "Close dialog" | Localizes the built-in close control's accessible name. |
showBackdrop | boolean | true | Renders the dimmed backdrop. Disable it with modal=false for a non-modal surface. |
teleport | boolean | true | Moves the layers out of clipping and stacking contexts. |
teleportTo | Teleport target | "body" | Sets the Vue Teleport destination. |
Parts
| Part | Element | Description |
|---|---|---|
Dialog.Root | renderless | Owns open state, focus, modality, dismissal, and trigger state. |
Dialog.RootProvider | renderless | Connects parts to an external useDialog machine. |
Dialog.Trigger | button | Opens the dialog; asChild merges behavior onto a Kappa Button. |
Dialog.Content | div | Composes Teleport, Backdrop, Positioner, surface, and optional close control. |
Dialog.Backdrop | div | Public dimmed layer used by the composed Content. |
Dialog.Positioner | div | Public viewport layer used by the composed Content. |
Dialog.Header / Footer | div | Kappa layout parts for stable title and action regions. |
Dialog.Title | h2 | Visible accessible name connected to Content. |
Dialog.Description | p | Visible accessible description connected to Content. |
Dialog.Close / CloseTrigger | button | Dismisses the layer; renders a localized icon when no slot is provided. |
Dialog.Context | renderless | Exposes the unwrapped dialog API to its slot. |
Events
| Event | Payload | Description |
|---|---|---|
update:open | boolean | Drives v-model:open. |
openChange | { open: boolean } | Reports every visibility change. |
update:triggerValue | string | null | Drives v-model:triggerValue. |
triggerValueChange | { triggerValue: string | null } | Reports the active value-bearing trigger. |
escapeKeyDown | KeyboardEvent | Fires when Escape reaches the layer. |
focusOutside / interactOutside / pointerDownOutside | Ark outside event | Lets consumers inspect or prevent outside behavior. |
requestDismiss | DialogRequestDismissEvent | Fires when a parent layer requests nested dismissal. |
exitComplete | void | Fires after the closed-state motion completes. |
Exports
| Export | Description |
|---|---|
Dialog | Compound root API exposing every named part. |
DialogRoot / DialogContent / DialogClose … | Named unaugmented component exports. |
DialogProps / DialogContentProps / DialogSize | Public prop and variant contracts. |
DialogOpenChangeDetails and outside-event types | Typed Ark UI event payloads. |
DIALOG_SIZES / DIALOG_ROLES | Readonly supported-value lists. |
useDialog / useDialogContext | Ark UI hooks for external state and descendant access. |
dialogAnatomy | Ark UI part anatomy metadata. |