Pickers and overlays
Dialog
A modal layer that asks people to finish or dismiss a focused task.
When to use it: Use it for work that should temporarily block the page behind it.
Example
import { Button, Dialog, DialogContent, DialogTrigger } from "@comp0/react";
export function Example() {
return (
<Dialog>
<DialogTrigger className="rounded bg-teal-700 px-3 py-2.5 text-base text-white sm:py-2 sm:text-sm dark:bg-teal-400 dark:text-zinc-950">
Open dialog
</DialogTrigger>
<DialogContent
aria-label="Example dialog"
className="m-auto w-[min(24rem,calc(100vw-2rem))] translate-y-0 rounded-[min(1vw,12px)] bg-white p-4 text-zinc-900 opacity-100 shadow-2xl ring-1 ring-zinc-950/10 transition-[opacity,translate] duration-150 ease-out starting:translate-y-1 starting:opacity-0 motion-reduce:transition-none backdrop:bg-zinc-950/40 dark:bg-zinc-900 dark:text-zinc-50 dark:shadow-none dark:ring-white/10"
>
<div className="flex flex-col gap-2">
<p className="text-base font-medium sm:text-sm">Ready to publish?</p>
<p className="text-base text-zinc-600 sm:text-sm dark:text-zinc-400">
This dialog is scoped to this example.
</p>
<form method="dialog" className="flex pt-2">
<Button
className="rounded bg-teal-700 px-3 py-2.5 text-base text-white sm:py-2 sm:text-sm dark:bg-teal-400 dark:text-zinc-950"
type="submit"
>
Done
</Button>
</form>
</div>
</DialogContent>
</Dialog>
);
}Command palette
Open a modal search from a ⌘K trigger, filter commands with Autocomplete, and run one to close the dialog.
import { useState } from "react";
import {
Autocomplete,
Dialog,
DialogContent,
DialogTrigger,
ListBox,
ListBoxItem,
SearchField,
SearchFieldInput,
} from "@comp0/react";
const commands = [
"Assign to teammate",
"Copy page link",
"Duplicate page",
"Move to project",
"Toggle sidebar",
];
function contains(textValue: string, inputValue: string) {
return textValue.toLocaleLowerCase().includes(inputValue.trim().toLocaleLowerCase());
}
export function Example() {
const [open, setOpen] = useState(false);
const [query, setQuery] = useState("");
const [lastCommand, setLastCommand] = useState("");
const runCommand = (command: string) => {
setLastCommand(command);
setQuery("");
setOpen(false);
};
return (
<div className="flex flex-col items-start gap-1.5">
<Dialog open={open} onToggle={setOpen}>
<DialogTrigger className="flex items-center gap-2 rounded border border-zinc-950/10 px-3 py-2.5 text-base text-zinc-800 sm:py-2 sm:text-sm dark:border-white/10 dark:text-zinc-100">
Search commands
<kbd className="rounded border border-zinc-950/10 px-1.5 font-sans text-xs text-zinc-500 dark:border-white/10 dark:text-zinc-400">
⌘K
</kbd>
</DialogTrigger>
<DialogContent
aria-label="Command palette"
className="m-auto w-[min(26rem,calc(100vw-2rem))] translate-y-0 rounded-[min(1vw,12px)] bg-white p-2 text-zinc-900 opacity-100 shadow-2xl ring-1 ring-zinc-950/10 transition-[opacity,translate] duration-150 ease-out starting:translate-y-1 starting:opacity-0 motion-reduce:transition-none backdrop:bg-zinc-950/40 dark:bg-zinc-900 dark:text-zinc-50 dark:shadow-none dark:ring-white/10"
>
<Autocomplete filter={contains} inputValue={query} onInputChange={setQuery}>
<SearchField as="div" className="flex flex-col gap-2">
<SearchFieldInput
aria-label="Search commands"
autoComplete="off"
className="w-full rounded border border-zinc-950/10 bg-white px-3 py-2.5 text-base text-zinc-950 outline-teal-600 focus-visible:outline-2 sm:py-2 sm:text-sm dark:border-white/10 dark:bg-zinc-950 dark:text-zinc-50 dark:outline-teal-400"
placeholder="Type a command"
/>
<ListBox aria-label="Commands" className="max-h-60 overflow-y-auto">
{commands.map((command) => (
<ListBoxItem
key={command}
className="cursor-pointer rounded px-3 py-2.5 text-base text-zinc-800 data-active:bg-teal-100 data-active:text-teal-950 sm:py-2 sm:text-sm dark:text-zinc-100 dark:data-active:bg-teal-950 dark:data-active:text-teal-50"
onClick={(event) => {
event.preventDefault();
runCommand(command);
}}
value={command}
>
{command}
</ListBoxItem>
))}
</ListBox>
</SearchField>
</Autocomplete>
</DialogContent>
</Dialog>
{lastCommand && (
<p className="text-base text-zinc-600 sm:text-sm dark:text-zinc-400">Ran {lastCommand}</p>
)}
</div>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
Dialog
Modal open-state provider. Does not add a DOM element.
DialogTrigger
Button that opens the dialog. Owns a DOM element.
DialogContent
Modal dialog element. Owns a DOM element.
Step by step
- 1
Add the main part
Start Dialog with DialogTrigger.
- 2
Add the supporting parts
Put a labelled DialogContent after the trigger.
- 3
Make the behavior clear
Keep a clear close or finish action inside the content.
Exampletsx <Dialog> <DialogTrigger>Open details</DialogTrigger> <DialogContent aria-label="Details">Details</DialogContent> </Dialog>;
Keyboard
- Esc
- Closes and restores trigger focus.
- ⇥
- Cycles inside the modal.
Forms and accessibility
No native form behavior; forms may live inside DialogContent.
Accessibility checklist
- Give DialogContent an accessible name.
- Keep focus inside the modal until it closes.
- Include a clear way to finish or dismiss the task.
API reference
import { Dialog, DialogContent, DialogTrigger } from "@comp0/react";Dialog
Context onlyModal open-state provider.
| Prop | Type | Description |
|---|---|---|
open | boolean | Controlled open state. |
defaultOpen | boolean | Initial open state. |
onToggle | (open: boolean) => void | Receives the next open state. |
DialogTrigger
DOM elementButton that opens the dialog.
| Prop | Type | Description |
|---|---|---|
as | ElementType | Fragment | Fragment merges the trigger onto your own element child. |
DialogContent
DOM elementModal dialog element.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Accessible name for the dialog task. |
portal | boolean | Renders into document.body; on by default. |
closedby | "any" | "closerequest" | "none" | Native dismissal policy; any adds light dismiss where supported. |
onClose | (event) => void | Native dialog close event. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
DialogTrigger
| Style hook | Meaning |
|---|---|
[data-open] | The dialog is open. |
DialogContent
| Style hook | Meaning |
|---|---|
[data-open] | The dialog is open. |
:open | Native pseudo-class equivalent. |