PixelAlertDialog

Modal confirmation dialog for destructive or irreversible actions, with async-aware action handling and Cancel-focused defaults.

  • role="alertdialog" + aria-modal with initial focus pinned to Cancel for safer destructive flows
  • Async onAction with pending state, spinner, and stays open on rejection when onError is provided
  • Destructive tone variant switches the action accent to red
  • Surface-aware styling (pixel chrome vs. linear)
  • Scroll lock, focus trap, and Escape-to-close baked in
Status
stable
Category
overlays
Since
v1.8.0

API

import { PixelAlertDialog } from '@pxlkit/ui-kit';
PropTypeDefaultDescription
open (required)boolean—Whether the dialog is visible; set it from onOpenChange.
onOpenChange (required)(open: boolean) => void—Called with false when the dialog asks to close (Cancel, Escape, the backdrop, a completed action).
title (required)string—Title; it names the dialog.
descriptionstring—Text under the title, wired via aria-describedby.
cancelLabelstring'Cancel'Label of the button that dismisses the dialog.
actionLabelstring'Confirm'Label of the button that confirms.
onAction (required)() => void | Promise<void>—The confirmed action. Its result matters: the dialog closes once it returns, or once the promise it returns resolves — and stays open, busy, until then.
onError(error: unknown) => void—Called when onAction throws / rejects. Receives the thrown value. When set, the dialog stays OPEN on failure so the consumer can show an inline error. When unset, errors are silently swallowed (back-compat).
destructivebooleanfalseRed accent for a destructive action (cyan otherwise).
surface'pixel' | 'linear'—Surface override; defaults to the nearest provider.

ref points to <div>.

Accessibility

WCAG target: 2.1 AA

  • alertdialog

Initial focus is placed on the Cancel button to prevent accidental confirmation of destructive actions.

Keyboard

KeyActionWhen
EscapeCloses the dialognot pending
TabCycles focus inside the dialog—
EnterActivates the focused button—

Usage

import { useState } from 'react';
import { PixelAlertDialog } from '@pxlkit/ui-kit';

export function Default() {
  const [open, setOpen] = useState(false);
  return (
    <div>
      <button type="button" onClick={() => setOpen(true)}>
        Open alert dialog
      </button>
      <PixelAlertDialog
        open={open}
        onOpenChange={setOpen}
        title="Save changes?"
        description="Your edits will be applied to the live document."
        actionLabel="Save"
        onAction={() => setOpen(false)}
      />
    </div>
  );
}

Examples

Default

import { useState } from 'react';
import { PixelAlertDialog } from '@pxlkit/ui-kit';

export function Default() {
  const [open, setOpen] = useState(false);
  return (
    <div>
      <button type="button" onClick={() => setOpen(true)}>
        Open alert dialog
      </button>
      <PixelAlertDialog
        open={open}
        onOpenChange={setOpen}
        title="Save changes?"
        description="Your edits will be applied to the live document."
        actionLabel="Save"
        onAction={() => setOpen(false)}
      />
    </div>
  );
}

Destructive

import { useState } from 'react';
import { PixelAlertDialog } from '@pxlkit/ui-kit';

export function Destructive() {
  const [open, setOpen] = useState(false);
  return (
    <div>
      <button type="button" onClick={() => setOpen(true)}>
        Delete item
      </button>
      <PixelAlertDialog
        open={open}
        onOpenChange={setOpen}
        title="Delete this item?"
        description="This action cannot be undone."
        cancelLabel="Keep"
        actionLabel="Delete"
        destructive
        onAction={() => setOpen(false)}
      />
    </div>
  );
}

Async Action

import { useState } from 'react';
import { PixelAlertDialog } from '@pxlkit/ui-kit';

export function AsyncAction() {
  const [open, setOpen] = useState(false);
  const [error, setError] = useState<string | null>(null);
  return (
    <div>
      <button type="button" onClick={() => { setError(null); setOpen(true); }}>
        Submit
      </button>
      <PixelAlertDialog
        open={open}
        onOpenChange={setOpen}
        title="Submit report?"
        description={error ?? 'The report will be sent for review.'}
        actionLabel="Submit"
        onAction={async () => {
          await new Promise((r) => setTimeout(r, 600));
        }}
        onError={(e) => setError(e instanceof Error ? e.message : 'Failed')}
      />
    </div>
  );
}

Related