Dialog

Focuses attention on a modal task. Title, body, and two equal actions.

Remove this item?

This can’t be undone.

Pull

Remove a sheet from the run. The question sits on paper.

Remove this item?

This can’t be undone.

Use it for

  • A short decision or a form that must finish before the page continues.
  • One title, one sentence of body, two equal actions.

Not for

  • Content the user should keep the page in view for; use Sheet or Popover.
  • Destructive confirmations that must not dismiss on Escape; use AlertDialog.

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

npm install @noorddev/vlak-react

// once, next to your app's root
import "@noorddev/vlak-react/css";

import { Button, Dialog, DialogActions, DialogBody, DialogTitle } from "@noorddev/vlak-react";

Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/dialog.

2. Vendor the source

npx @noorddev/vlak-cli add dialog

The StyleX leaf lands in components/vlak/ with its dependencies, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/dialog.json

The same registry item, installed by shadcn's CLI.

CSS only

<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />

No React. Link vlak.css and use the markup and classes below.

import { useState } from "react";
import { Button, Dialog, DialogActions, DialogBody, DialogTitle } from "@noorddev/vlak-react";

const [open, setOpen] = useState(false);

<Button variant="ghost" onClick={() => setOpen(true)}>Remove</Button>
<Dialog open={open} onClose={() => setOpen(false)} closeLabel="Close">
  <DialogTitle>Remove this item?</DialogTitle>
  <DialogBody>This can't be undone.</DialogBody>
  <DialogActions>
    <Button variant="ghost" size="sm" onClick={() => setOpen(false)}>Cancel</Button>
    <Button size="sm" onClick={remove}>Remove</Button>
  </DialogActions>
</Dialog>

Dialog

A native <dialog>. Focus trapping, Escape, and the backdrop come from the platform; the title names it and the body describes it.

PropTypeDefaultDescription
openrequiredboolean
onClose() => void
dismissablebooleanEscape (and any other platform close request) asks the parent to close. Off, the dialog must be answered; maps to closedby="none".
lightDismissbooleanA click on the backdrop closes too; maps to closedby="any".
closeLabelstringRenders a labelled close button in the top corner.
extraStylesLeavesExtra StyleX leaves merged after the dialog frame (command palette).

Also accepts Omit<DialogHTMLAttributes<HTMLDialogElement>, "open" | "onClose">.

DialogActions

Also accepts HTMLAttributes<HTMLDivElement>.

DialogBody

Also accepts HTMLAttributes<HTMLParagraphElement>.

DialogTitle

PropTypeDefaultDescription
as"h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div""h2"Heading level. The dialog is named by this element either way.

Also accepts HTMLAttributes<HTMLHeadingElement>.

KeysDoes
EscapeCalls onClose (when dismissable, the default)
Tab, Shift + TabCycles focus inside the open dialog
Enter, SpaceActivates the focused action
<div class="rs-dialog" role="dialog" aria-labelledby="remove-title" aria-describedby="remove-body"><h2 class="rs-dialog-title" id="remove-title">Remove this item?</h2><p class="rs-dialog-body" id="remove-body">This can't be undone.</p><div class="rs-dialog-actions"><button class="rs-btn-ghost rs-btn-sm">Cancel</button><button class="rs-btn-primary rs-btn-sm">Remove</button></div></div>
.rs-dialog.rs-dialog-title.rs-dialog-body.rs-dialog-actions.rs-dialog-close