Aller au contenu

Boîte de dialogue d'alerte

Une fenêtre modale qui interrompt le déroulement des actions irréversibles — suppression, déconnexion, annulation —, avec un style néobrutaliste mêlant contours et ombres.

import {
  AlertDialog,
  AlertDialogAction,

La boîte de dialogue d’alerte interrompt l’utilisateur avec une fenêtre modale qui exige un choix explicite — un clic à l’extérieur ne la ferme pas. Construite sur le composant Base UI Alert Dialog et habillée du style néobrutaliste : bordures épaisses, ombres marquées et typographie affirmée.

À privilégier pour :

  • Confirmations destructrices — supprimer un compte, retirer un membre de l’équipe, effacer un projet.
  • Opérations irréversibles — publier, envoyer, soumettre ; tout ce qui ne s’annule pas.
  • Garde-fous des modifications non enregistrées — prévenir avant de quitter un formulaire modifié.

Installation

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
npx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json

Installez les dépendances suivantes :

pnpm add @base-ui/react
npm install @base-ui/react
yarn add @base-ui/react
bun add @base-ui/react

Copiez et collez le code suivant dans votre projet.

components/ui/alert-dialog.tsx
"use client"

import * as React from "react"
import { AlertDialog as AlertDialogPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"

function AlertDialog({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
  return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
}

function AlertDialogTrigger({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
  return (
    <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
  )
}

function AlertDialogPortal({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
  return (
    <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
  )
}

function AlertDialogOverlay({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
  return (
    <AlertDialogPrimitive.Overlay
      data-slot="alert-dialog-overlay"
      className={cn(
        "fixed inset-0 z-50 bg-foreground/20 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogContent({
  className,
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
  size?: "default" | "sm"
}) {
  return (
    <AlertDialogPortal>
      <AlertDialogOverlay />
      <AlertDialogPrimitive.Content
        data-slot="alert-dialog-content"
        data-size={size}
        className={cn(
          "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded border-2 bg-popover p-4 text-popover-foreground shadow-md duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
          className
        )}
        {...props}
      />
    </AlertDialogPortal>
  )
}

function AlertDialogHeader({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-header"
      className={cn(
        "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogFooter({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-footer"
      className={cn(
        "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t-2 bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogMedia({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-media"
      className={cn(
        "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogTitle({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
  return (
    <AlertDialogPrimitive.Title
      data-slot="alert-dialog-title"
      className={cn(
        "font-head text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogDescription({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
  return (
    <AlertDialogPrimitive.Description
      data-slot="alert-dialog-description"
      className={cn(
        "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogAction({
  className,
  variant = "default",
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
  Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
  return (
    <Button variant={variant} size={size} asChild>
      <AlertDialogPrimitive.Action
        data-slot="alert-dialog-action"
        className={cn(className)}
        {...props}
      />
    </Button>
  )
}

function AlertDialogCancel({
  className,
  variant = "outline",
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
  Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
  return (
    <Button variant={variant} size={size} asChild>
      <AlertDialogPrimitive.Cancel
        data-slot="alert-dialog-cancel"
        className={cn(className)}
        {...props}
      />
    </Button>
  )
}

export {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogOverlay,
  AlertDialogPortal,
  AlertDialogTitle,
  AlertDialogTrigger,
}

Adaptez les chemins d’import à la structure de votre projet.

Utilisation

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
<AlertDialog>
  <AlertDialogTrigger render={<Button variant="outline" />}>
    Show Dialog
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
      <AlertDialogDescription>
        This action cannot be undone. This will permanently delete your account
        from our servers.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction>Continue</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

Composition

Utilisez la composition suivante pour construire un AlertDialog :

AlertDialog
├── AlertDialogTrigger
└── AlertDialogContent
    ├── AlertDialogHeader
    │   ├── AlertDialogMedia
    │   ├── AlertDialogTitle
    │   └── AlertDialogDescription
    └── AlertDialogFooter
        ├── AlertDialogCancel
        └── AlertDialogAction

Exemples

Simple

Une boîte de dialogue simple avec un titre, une description et des boutons pour annuler ou continuer.

import {
  AlertDialog,
  AlertDialogAction,

Petit

Utilisez la prop size="sm" pour réduire la taille de la boîte de dialogue.

import {
  AlertDialog,
  AlertDialogAction,

Média

Utilisez le composant AlertDialogMedia pour ajouter un élément média, comme une icône ou une image, à la boîte de dialogue.

import { CircleFadingPlusIcon } from "lucide-react"

import {

Petit avec média

Utilisez la prop size="sm" pour réduire la taille de la boîte de dialogue et le composant AlertDialogMedia pour y ajouter un élément média, comme une icône ou une image.

import { BluetoothIcon } from "lucide-react"

import {

Destructif

Utilisez le composant AlertDialogAction pour ajouter un bouton d’action destructrice à la boîte de dialogue.

import { Trash2Icon } from "lucide-react"

import {

RTL

Pour activer le RTL dans Neobrutalism, consultez le guide de configuration RTL.

"use client"

import { BluetoothIcon } from "lucide-react"

Accessibilité

La boîte de dialogue d’alerte suit le modèle WAI-ARIA Alert Dialog : la fenêtre est rendue avec role="alertdialog" reliée au titre et à la description via aria-labelledby et aria-describedby, et le focus est piégé à l’intérieur tant qu’elle est ouverte.

Interactions clavier :

ToucheAction
Space / EnterOuvre la boîte de dialogue depuis le déclencheur ayant le focus
Tab / Shift + TabFait circuler le focus parmi les éléments focalisables de la boîte de dialogue
EscFerme la boîte de dialogue et rend le focus au déclencheur

Référence API

size

Utilisez la prop size sur le composant AlertDialogContent pour contrôler la taille de la boîte de dialogue. Elle accepte les valeurs suivantes :

PropTypeDefault
size"default" | "sm""default"

Pour en savoir plus sur les autres composants et leurs props, consultez la documentation Base UI.