Klarhimmel UI

Alert dialog

Stable

A confirmation that cannot be dismissed by accident. Built on Dialog.

npx shadcn add @klarhimmel/alert-dialog @klarhimmel/button

When to use

  • Use for

    A decision with consequences that must not be taken by accident: delete, send, publish.

  • Not for

    Information. Use Banner. A task with fields. Use Dialog.

  • Pairs with

    Button, a short sentence.

Preview

Nothing deleted yet.

alert-dialog.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { AlertDialog } from '@/components/klarhimmel/ui/alert-dialog';
import { Button } from '@/components/klarhimmel/ui/button';

export default function Example() {
  return (
    <Theme theme="brand">
      <AlertDialog
        destructive
        trigger={<Button variant="secondary">Delete room</Button>}
        title="Delete the Garden room?"
        description="Its bookings are cancelled. This cannot be undone."
        confirmLabel="Delete room"
        onConfirm={() => console.log('deleted')}
      />
    </Theme>
  );
}

Install

Import

import { AlertDialog } from '@/components/klarhimmel/ui/alert-dialog';
Lands in
components/klarhimmel/ui/alert-dialog.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
Render in
Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.
Limit
Overlays render into the Theme root with position: fixed. A transform on an ancestor re-parents fixed (a dialog centres in the wrapper) and overflow: hidden clips menus. Render <Theme> at the app root, outside any transformed wrapper.

Examples

  • Confirm

    Draft saved, not sent.

    import { useState } from 'react';
    import { AlertDialog } from '@/components/klarhimmel/ui/alert-dialog';
    import { Button } from '@/components/klarhimmel/ui/button';
    
    export default function AlertDialogConfirm() {
      const [sent, setSent] = useState(false);
      return (
        <div className="flex min-h-28 flex-col items-center gap-3">
          <AlertDialog
            trigger={<Button>Send to 212 guests</Button>}
            title="Send the newsletter now?"
            description="It goes to every guest who agreed to hear from us. A sent email cannot be recalled."
            confirmLabel="Send newsletter"
            cancelLabel="Not yet"
            onConfirm={() => setSent(true)}
          />
          <p role="status" className="text-kh-small text-kh-muted">{sent ? 'Sent.' : 'Draft saved, not sent.'}</p>
        </div>
      );
    }
  • Controlled

    Nothing happened yet.

    import { useState } from 'react';
    import { AlertDialog } from '@/components/klarhimmel/ui/alert-dialog';
    import { Button } from '@/components/klarhimmel/ui/button';
    
    export default function AlertDialogControlled() {
      const [open, setOpen] = useState(false);
      const [log, setLog] = useState('Nothing happened yet.');
      return (
        <div className="flex min-h-28 flex-col items-center gap-3">
          <Button variant="secondary" onClick={() => setOpen(true)}>Discard the draft</Button>
          <AlertDialog
            open={open}
            onOpenChange={(o) => { setOpen(o); if (!o) setLog((l) => (l === 'Draft discarded.' ? l : 'Kept the draft.')); }}
            title="Discard this draft?"
            description="Your changes to the room description are lost."
            confirmLabel="Discard draft"
            cancelLabel="Keep editing"
            onConfirm={() => setLog('Draft discarded.')}
          />
          <p role="status" className="text-kh-small text-kh-muted">{log}</p>
        </div>
      );
    }

API reference

PropTypeDefaultDescription
titlestring-Required. The question, written as one.
descriptionstring-Required. What happens if they say yes.
confirmLabelstring-Required. Name the action ("Delete room"), never "OK".
cancelLabelstring'Cancel'The label of the safe button.
onConfirm() => void-Required. Runs when confirmed; the dialog closes after it.
destructivebooleanfalseStyles the confirm button in the danger colour.
trigger / open / onOpenChangeReactNode / boolean-Same as Dialog.

Accessibility

KeyDoes
Tab / Shift+TabCycles inside the dialog
EscapeCancels and returns focus to the trigger
Enter / SpaceActivates the focused button

A click outside does not close it. Write the confirm label as the action, not "OK".

Accessibility