bx

Alert Dialog

A modal dialog that interrupts the user with important content and expects a response.

tsx
<AlertDialog>
  <AlertDialogTrigger for="delete">
    <Button variant="outline">Show Dialog</Button>
  </AlertDialogTrigger>
  <AlertDialogContent id="delete">
    <AlertDialogHeader>
      <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
      <AlertDialogDescription>
        This action cannot be undone.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction>Continue</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

AlertDialog est un Dialog pour les décisions irréversibles — supprimer un compte, discard des changements non sauvés, annuler une opération en cours. Différence clé avec Dialog : on ne peut pas fermer sans choix explicite.

  • Clic sur le backdrop → ignoré (pas de fermeture).
  • Escape → ignoré (contrairement à Dialog).
  • Pas de bouton × dans le coin.
  • Seuls AlertDialogAction et AlertDialogCancel ferment le dialog.

Le rôle ARIA est alertdialog (pas dialog) — les lecteurs d'écran annoncent différemment et attendent une réponse. Utiliser uniquement pour de vraies décisions destructives ; les formulaires ou éditions vont dans un Dialog classique.

Composition idiomatique :

  • AlertDialogTitle — pose la question ("Are you absolutely sure ?"). Format interrogatif recommandé.
  • AlertDialogDescription — explique les conséquences ("This action cannot be undone. This will permanently delete…"). Toujours mentionner l'irréversibilité.
  • AlertDialogCancel — bouton neutre (outline), typiquement à gauche. Ferme sans action.
  • AlertDialogAction — bouton primary (ou destructive), à droite. Confirme et ferme.

Installation

Terminal
$ bext ui add alert-dialog

Usage

tsx
import { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from "@bext-stack/ui/primitives"
import { Button } from "@bext-stack/ui/primitives"

<AlertDialog>
  <AlertDialogTrigger for="delete-account">
    <Button variant="destructive">Delete account</Button>
  </AlertDialogTrigger>
  <AlertDialogContent id="delete-account">
    <AlertDialogHeader>
      <AlertDialogTitle>Are you absolutely sure ?</AlertDialogTitle>
      <AlertDialogDescription>
        This action cannot be undone. This will permanently delete your
        account and remove your data from our servers.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction>Delete</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

Anatomy

<AlertDialog>
  ├── <AlertDialogTrigger for="alert-id">      // data-ui="toggle-pop"
  │   └── <Button>Delete account</Button>
  └── <AlertDialogContent id="alert-id">        // role="alertdialog"
      ├── <AlertDialogHeader>
      │   ├── <AlertDialogTitle>...</AlertDialogTitle>
      │   └── <AlertDialogDescription>...</AlertDialogDescription>
      └── <AlertDialogFooter>
          ├── <AlertDialogCancel>Cancel</AlertDialogCancel>
          └── <AlertDialogAction>Delete</AlertDialogAction>

API Reference

AlertDialog

Prop Type Default Description
className string Wrapper (inline-block). Contient trigger + content.

AlertDialogTrigger

Prop Type Default Description
for string Match l'id d'AlertDialogContent à ouvrir.
children ReactNode Bouton qui déclenche l'ouverture.

AlertDialogContent

Prop Type Default Description
id string Match AlertDialogTrigger.for.
className string Modal centrée avec role="alertdialog". Backdrop non-cliquable, Escape ignoré.

AlertDialogHeader

Prop Type Default Description
className string Container flex-col gap-2 pour titre + description.

AlertDialogTitle

Prop Type Default Description
className string

text-lg font-semibold. Format interrogatif recommandé.

AlertDialogDescription

Prop Type Default Description
className string

text-sm muted-foreground. Toujours expliciter l'irréversibilité.

AlertDialogFooter

Prop Type Default Description
className string flex-col-reverse mobile → flex-row sm+, justify-end. Cancel gauche, Action droite.

AlertDialogCancel

Prop Type Default Description
children ReactNode Texte du bouton neutre (typiquement "Cancel", "Annuler", "Nevermind"). Rendu comme Button variant="outline".

AlertDialogAction

Prop Type Default Description
children ReactNode Texte du bouton confirmation ("Continue", "Delete account"). Rendu comme Button variant="default" (utiliser className="destructive" pour actions dangereuses).

Data Attributes

Attributs qu'on peut cibler en CSS custom pour override le style par état.

Attribute On Values Description
role="alertdialog" AlertDialogContent Rôle ARIA (pas "dialog"). Les AT annoncent différemment.
data-ui="toggle-pop" AlertDialogTrigger data-target="#id" Cliquer ouvre l'overlay.
data-ui="close" Cancel + Action Cliquer ferme. Le seul chemin de fermeture.

Keyboard

Key Action
Tab / Shift+Tab Focus trap sur Cancel ↔ Action.
Enter Active le bouton focus (par défaut : Action).
Escape IGNORÉ. Le dialog ne se ferme pas — c'est intentionnel, il faut un choix explicite.

Accessibility

Le role="alertdialog" signale aux AT que c'est une décision bloquante — ils annoncent immédiatement le contenu (pas de "vous pouvez ignorer").

aria-labelledby vers l'id du Title et aria-describedby vers l'id de la Description — nécessaire pour que le screen reader lise le titre + description à l'ouverture.

Focus par défaut : sur AlertDialogCancel, PAS sur Action. C'est la convention accessibilité : ne pas préfocus une action destructive au cas où le user appuie Enter par réflexe.

Utiliser avec parcimonie — chaque AlertDialog interrompt le flow. Réserver aux vraies décisions destructives (suppression permanente, annulation d'un batch, exit sans save). Pour la confirmation courante, un Dialog classique + toast de confirmation post-action suffit.

Examples

Action destructive (rouge)

tsx
<AlertDialogFooter>
  <AlertDialogCancel>Cancel</AlertDialogCancel>
  <button data-ui="close" className="… bg-destructive text-destructive-foreground …">
    Delete
  </button>
</AlertDialogFooter>

Sans description

tsx
<AlertDialogContent id="discard">
  <AlertDialogHeader>
    <AlertDialogTitle>Discard unsaved changes ?</AlertDialogTitle>
  </AlertDialogHeader>
  <AlertDialogFooter>
    <AlertDialogCancel>Keep editing</AlertDialogCancel>
    <AlertDialogAction>Discard</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialogContent>
Try it live
Open in play

Edit this component live in the bext playground. The PRISM + signals version is the bext-native idiom — fine-grained reactivity, no virtual DOM.

src/app/page.tsx 2 files · signals · runs in your browser
Pure PRISM + real bext signals — fine-grained, no re-render.