Phi
Dialog
@dicehub/phiv1.0.0-beta.1

Dialog

A window overlaid on the current page, rendering the content underneath inert.

<script setup>
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger>
      <Button>Delete</Button>
    </Dialog.Trigger>

    <Dialog class="dialog-demo__panel">
      <div class="dialog-demo__header">
        <Dialog.Title class="dialog-demo__title">Modal Title</Dialog.Title>
        <Dialog.Close />
      </div>
      <Dialog.Description>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
      </Dialog.Description>
      <div class="dialog-demo__actions">
        <Dialog.Close as-child>
          <Button variant="secondary">Cancel</Button>
        </Dialog.Close>
        <Button variant="destructive">Delete</Button>
      </div>
    </Dialog>
  </Dialog.Root>
</template>

Installation

Barrel

import { Dialog } from "@dicehub/phi";

Granular

import { Dialog } from "@dicehub/phi/components/dialog";

Usage

Dialogs open near the viewport top: 32px on small screens and 64px from 640px upward. This keeps long content visible and gives modal workflows a stable starting point.

<script setup>
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger>
      <Button>Open</Button>
    </Dialog.Trigger>

    <Dialog>
      <Dialog.Title>Dialog Title</Dialog.Title>
      <Dialog.Description>Dialog content goes here.</Dialog.Description>
      <div class="dialog-demo__actions">
        <Dialog.Close as-child>
          <Button variant="secondary">Cancel</Button>
        </Dialog.Close>
      </div>
    </Dialog>
  </Dialog.Root>
</template>

Dialog vs Alert Dialog

Use role="dialog" for ordinary modal content and role="alertdialog" for destructive or confirmation flows.

RoleUse CaseBehavior
dialogForms, content display, short workflowsDismissible by default
alertdialogDestructive actions, critical confirmationsOutside pointer dismissal is disabled by default

Examples

Basic Dialog

<script setup>
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger>
      <Button>Click me</Button>
    </Dialog.Trigger>
    <Dialog class="dialog-demo__panel">
      <div class="dialog-demo__header">
        <Dialog.Title class="dialog-demo__title">Modal Title</Dialog.Title>
        <Dialog.Close />
      </div>
      <Dialog.Description>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
      </Dialog.Description>
    </Dialog>
  </Dialog.Root>
</template>

Alert Dialog (role="alertdialog")

<script setup>
import { PhWarning } from "@phosphor-icons/vue";
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";
</script>

<template>
  <Dialog.Root role="alertdialog">
    <Dialog.Trigger>
      <Button variant="destructive">Delete Account</Button>
    </Dialog.Trigger>
    <Dialog class="dialog-demo__panel dialog-demo__panel--alert">
      <div class="dialog-demo__header dialog-demo__header--icon">
        <span class="dialog-demo__warning"><PhWarning :size="20" weight="fill" /></span>
        <Dialog.Title class="dialog-demo__title dialog-demo__title--compact">Delete Account?</Dialog.Title>
      </div>
      <Dialog.Description>
        This action cannot be undone. All your data will be permanently removed from our servers. Are you sure you want to proceed?
      </Dialog.Description>
      <div class="dialog-demo__actions">
        <Dialog.Close as-child><Button variant="secondary">Cancel</Button></Dialog.Close>
        <Button variant="destructive">Delete Account</Button>
      </div>
    </Dialog>
  </Dialog.Root>
</template>

Confirmation Dialog (with disablePointerDismissal)

<script setup>
import { PhWarning } from "@phosphor-icons/vue";
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";
</script>

<template>
  <Dialog.Root disable-pointer-dismissal>
    <Dialog.Trigger>
      <Button variant="destructive">Delete Project</Button>
    </Dialog.Trigger>
    <Dialog class="dialog-demo__panel">
      <div class="dialog-demo__header dialog-demo__header--icon">
        <span class="dialog-demo__warning"><PhWarning :size="20" /></span>
        <Dialog.Title class="dialog-demo__title dialog-demo__title--compact">Delete Project?</Dialog.Title>
      </div>
      <Dialog.Description>
        This will permanently delete the project and all associated data.
      </Dialog.Description>
      <div class="dialog-demo__actions">
        <Dialog.Close as-child><Button variant="secondary">Cancel</Button></Dialog.Close>
        <Button variant="destructive">Delete</Button>
      </div>
    </Dialog>
  </Dialog.Root>
</template>

With Actions

<script setup>
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger>
      <Button>Delete</Button>
    </Dialog.Trigger>

    <Dialog class="dialog-demo__panel">
      <div class="dialog-demo__header">
        <Dialog.Title class="dialog-demo__title">Modal Title</Dialog.Title>
        <Dialog.Close />
      </div>
      <Dialog.Description>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
      </Dialog.Description>
      <div class="dialog-demo__actions">
        <Dialog.Close as-child>
          <Button variant="secondary">Cancel</Button>
        </Dialog.Close>
        <Button variant="destructive">Delete</Button>
      </div>
    </Dialog>
  </Dialog.Root>
</template>

Max Width Override

<script setup>
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger>
      <Button>Open capped dialog</Button>
    </Dialog.Trigger>
    <Dialog class="dialog-demo__panel" style="--phi-dialog-width: 32rem; --phi-dialog-max-width: 32rem">
      <div class="dialog-demo__header">
        <Dialog.Title class="dialog-demo__title">Max width override</Dialog.Title>
        <Dialog.Close />
      </div>
      <Dialog.Description>
        Consumer styles can override the width token while the viewport cap remains active.
      </Dialog.Description>
    </Dialog>
  </Dialog.Root>
</template>

With Select

<script setup>
import { ref } from "vue";
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";

const region = ref("us-east");
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger><Button>Open Form</Button></Dialog.Trigger>
    <Dialog class="dialog-demo__panel">
      <div class="dialog-demo__header">
        <Dialog.Title class="dialog-demo__title">Create Resource</Dialog.Title>
        <Dialog.Close />
      </div>
      <Dialog.Description>Select a region for the new resource.</Dialog.Description>
      <div class="dialog-demo__select-wrap">
        <select v-model="region" class="dialog-demo__select">
          <option value="us-east">US East</option>
          <option value="us-west">US West</option>
          <option value="eu-west">EU West</option>
        </select>
      </div>
    </Dialog>
  </Dialog.Root>
</template>

With Combobox

<script setup>
import { ref } from "vue";
import { Button } from "@dicehub/phi/components/button";
import { Combobox, createComboboxCollection } from "@dicehub/phi/components/combobox";
import { Dialog } from "@dicehub/phi/components/dialog";

const value = ref(["eu-west"]);
const regions = [
  { label: "US East", value: "us-east" },
  { label: "US West", value: "us-west" },
  { label: "EU West", value: "eu-west" },
];
const collection = createComboboxCollection({
  items: regions,
  itemToString: (item) => item.label,
  itemToValue: (item) => item.value,
});
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger><Button>Open Form</Button></Dialog.Trigger>
    <Dialog class="dialog-demo__panel">
      <Dialog.Title class="dialog-demo__title">Create Resource</Dialog.Title>
      <Dialog.Description>Search and select a region.</Dialog.Description>
      <Combobox v-model="value" :collection="collection">
        <Combobox.TriggerInput placeholder="Search regions" />
        <Combobox.Content>
          <Combobox.Empty />
          <Combobox.List>
            <template #default="{ item }">
              <Combobox.Item :item="item">{{ item.label }}</Combobox.Item>
            </template>
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
    </Dialog>
  </Dialog.Root>
</template>

With Dropdown

<script setup>
import { Button } from "@dicehub/phi/components/button";
import { Dialog } from "@dicehub/phi/components/dialog";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@dicehub/phi/components/dropdown";
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger><Button>Open Form</Button></Dialog.Trigger>
    <Dialog class="dialog-demo__panel">
      <Dialog.Title class="dialog-demo__title">Resource Actions</Dialog.Title>
      <Dialog.Description>Choose an action for the selected resource.</Dialog.Description>
      <DropdownMenu>
        <DropdownMenuTrigger><Button>Actions</Button></DropdownMenuTrigger>
        <DropdownMenuContent>
        <DropdownMenuItem value="edit">Edit</DropdownMenuItem>
        <DropdownMenuItem value="duplicate">Duplicate</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem value="delete" class="dialog-demo__dropdown-danger">Delete</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
    </Dialog>
  </Dialog.Root>
</template>

Sub-components

ComponentDescription
DialogMain modal surface that renders backdrop, positioner, and content.
Dialog.RootControls open state, modal behavior, role, and dismissal behavior.
Dialog.TriggerAs-child trigger that opens the dialog.
Dialog.TitleAccessible label for the dialog content.
Dialog.DescriptionAccessible supporting description.
Dialog.CloseClose trigger. Use as-child to render a Phi Button.

API Reference

Dialog

PropTypeDefaultDescription
size"sm" | "base" | "lg" | "xl""base"Width token for the modal surface.
teleportTostring | HTMLElement"body"Teleport target for backdrop and content.

Dialog.Root

PropTypeDefaultDescription
role"dialog" | "alertdialog""dialog"ARIA role for general or confirmation flows.
disablePointerDismissalbooleanfalsePrevents closing when the backdrop or outside area is clicked.
openboolean-Controlled open state forwarded to Ark UI.
closeOnEscapebooleantrueWhether Escape closes the dialog.

Dialog Parts

PropTypeDefaultDescription
asChildbooleanfalseAvailable on Dialog.Close to render a custom child control.
Ark propsDialog primitives-Trigger, Title, Description, and Close forward supported Ark attributes.