Skip to content
Browse docs

Dialog

Beta

A modal window that traps focus until it closes.

---
import Dialog from "@/components/ui/dialog/Dialog.astro";
import DialogTrigger from "@/components/ui/dialog/DialogTrigger.astro";
import DialogContent from "@/components/ui/dialog/DialogContent.astro";
import DialogTitle from "@/components/ui/dialog/DialogTitle.astro";
import DialogDescription from "@/components/ui/dialog/DialogDescription.astro";
import DialogClose from "@/components/ui/dialog/DialogClose.astro";
---

<Dialog id="create-project">
  <DialogTrigger variant="primary">Create project</DialogTrigger>
  <DialogContent>
    <div class="flex flex-col gap-1.5">
      <DialogTitle>Create project</DialogTitle>
      <DialogDescription>Deploy your new project in one click.</DialogDescription>
    </div>
    <label class="flex flex-col gap-2 text-[15px] font-medium">
      Name
      <input
        name="name"
        placeholder="Name of your project"
        class="eris-curve h-11 rounded-field border border-border bg-field px-3.5 font-normal text-foreground placeholder:text-placeholder focus-visible:border-ring focus-visible:outline-none"
      />
    </label>
    <div class="flex justify-end gap-2">
      <DialogClose>Cancel</DialogClose>
      <DialogClose variant="primary">Deploy</DialogClose>
    </div>
  </DialogContent>
</Dialog>

Installation

npx eris-ui@latest add dialog
pnpm dlx eris-ui@latest add dialog
bunx eris-ui@latest add dialog
yarn dlx eris-ui@latest add dialog

Installs @zag-js/dialog, @zag-js/vanilla. Also adds button.

Confirmations

For a decision the user must make before going on, use role="alertdialog" and turn off closing on outside click. Escape still closes it.

---
import Dialog from "@/components/ui/dialog/Dialog.astro";
import DialogTrigger from "@/components/ui/dialog/DialogTrigger.astro";
import DialogContent from "@/components/ui/dialog/DialogContent.astro";
import DialogTitle from "@/components/ui/dialog/DialogTitle.astro";
import DialogDescription from "@/components/ui/dialog/DialogDescription.astro";
import DialogClose from "@/components/ui/dialog/DialogClose.astro";
---

<Dialog id="delete-project" role="alertdialog" closeOnInteractOutside={false}>
  <DialogTrigger variant="danger">Delete project</DialogTrigger>
  <DialogContent>
    <div class="flex flex-col gap-1.5">
      <DialogTitle>Delete this project?</DialogTitle>
      <DialogDescription>This removes every deployment. It cannot be undone.</DialogDescription>
    </div>
    <div class="flex justify-end gap-2">
      <DialogClose variant="secondary">Keep it</DialogClose>
      <DialogClose variant="danger">Delete</DialogClose>
    </div>
  </DialogContent>
</Dialog>

Focus on close

Closing the dialog returns focus to the trigger. To send it somewhere else, pass a CSS selector as finalFocus. Use this when the trigger will not be there afterwards, like a delete button in a row that the dialog removes.

<h2 id="projects" tabindex="-1">
  Projects
</h2>

<Dialog finalFocus="#projects">
  <DialogTrigger variant="danger">Delete project</DialogTrigger>
  ...
</Dialog>

The target must be focusable, so give a heading or container tabindex="-1".

If the trigger is removed, hidden or disabled when the dialog closes and finalFocus is not set, focus moves to the nearest focusable ancestor of the dialog, or to the page’s <main>. It does not fall to <body>. This only covers changes made before the dialog closes. If you remove the trigger later, after a request finishes for example, set finalFocus.

Keyboard

Key Does
Enter or Space on the trigger Opens the dialog and moves focus inside
Tab / Shift Tab Moves between focusable elements, staying inside the dialog
Esc Closes the dialog and returns focus to the trigger

While the dialog is open, the page behind it does not scroll and screen readers only see the dialog.

Parts

Component Renders
Dialog The root. Props: id, defaultOpen, role, closeOnInteractOutside, finalFocus
DialogTrigger The button that opens it. Takes Button’s variant and size
DialogContent The backdrop and the dialog panel
DialogTitle The heading; becomes the dialog’s accessible name
DialogDescription Supporting text; becomes its accessible description
DialogClose A button that closes it. Takes Button’s variant and size