shadimage
roiui
itemalert-dialogsource: ok · hit

Alert Dialog

A modal dialog that interrupts the user with important content.

npx shadcn add "https://roiui.com/r/alert-dialog.json"
free

Interaction

modal

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
ui-primitives@base-ui/react@1.5.0

Files2 · source cached 10/9/2026, 4:23:03 AM

registry/brook/ui/alert-dialog/alert-dialog.tsx
"use client";

import { AlertDialog } from "@base-ui/react/alert-dialog";
import type React from "react";
import { cn } from "../../../lib/utils";
import styles from "./alert-dialog.module.css";

function AlertDialogRoot({ ...props }: AlertDialog.Root.Props) {
  return <AlertDialog.Root {...props} />;
}

function AlertDialogTrigger({ ...props }: AlertDialog.Trigger.Props) {
  return <AlertDialog.Trigger data-slot="alert-dialog-trigger" nativeButton {...props} />;
}

const AlertDialogPortal = AlertDialog.Portal;

function AlertDialogBackdrop({ className, ...props }: AlertDialog.Backdrop.Props) {
  return (
    <AlertDialog.Backdrop className={cn(styles.overlay, className)} data-slot="alert-dialog-backdrop" {...props} />
  );
}

function AlertDialogPopup({ className, children, ...props }: AlertDialog.Popup.Props) {
  return (
    <AlertDialogPortal>
      <AlertDialog.Popup className={cn(styles.content, className)} data-slot="alert-dialog-popup" {...props}>
        {children}
      </AlertDialog.Popup>
    </AlertDialogPortal>
  );
}

function AlertDialogContent({ className, children, ...props }: AlertDialog.Popup.Props) {
  return (
    <AlertDialogPortal>
      <AlertDialogBackdrop />
      <AlertDialog.Popup className={cn(styles.content, className)} data-slot="alert-dialog-popup" {...props}>
        {children}
      </AlertDialog.Popup>
    </AlertDialogPortal>
  );
}

function AlertDialogTitle({ className, ...props }: AlertDialog.Title.Props) {
  return <AlertDialog.Title className={cn(styles.title, className)} data-slot="alert-dialog-title" {...props} />;
}

function AlertDialogDescription({ className, ...props }: AlertDialog.Description.Props) {
  return (
    <AlertDialog.Description
      className={cn(styles.description, className)}
      data-slot="alert-dialog-description"
      {...props}
    />
  );
}

function AlertDialogClose({ ...props }: AlertDialog.Close.Props) {
  return <AlertDialog.Close data-slot="alert-dialog-close" nativeButton {...props} />;
}

const AlertDialogOverlay = AlertDialogBackdrop;

function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) {
  return <div className={cn(styles.header, className)} data-slot="alert-dialog-header" {...props} />;
}

function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) {
  return <div className={cn(styles.footer, className)} data-slot="alert-dialog-footer" {...props} />;
}

export {
  AlertDialogRoot as AlertDialog,
  AlertDialogClose,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogOverlay,
  AlertDialogPopup,
  AlertDialogPortal,
  AlertDialogRoot,
  AlertDialogTitle,
  AlertDialogTrigger,
};
registry/brook/ui/alert-dialog/alert-dialog.module.css
.overlay {
  position: fixed;
  inset: 0;
  z-index: var(--dialog-z);
  background-color: var(--dialog-overlay);
  transition: opacity 150ms;

  &[data-starting-style],
  &[data-ending-style] {
    opacity: 0;
  }
}

.content {
  position: fixed;
  left: 50%;
  top: 50%;
  z-index: 101;
  display: grid;
  width: 100%;
  max-width: 32rem;
  max-height: 85vh;
  overflow-y: auto;
  gap: 1rem;
  border: none;
  box-shadow:
    0 0 0 1px oklch(from var(--border) l c h / 0.5),
    var(--shadow-border-stack);
  padding: 1.5rem;
  border-radius: var(--radius);
  background-color: var(--mix-card-5-bg);
  transform: translate(-50%, -50%);
  transition: all 150ms;

  &[data-starting-style],
  &[data-ending-style] {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.95);
  }

  @media (max-width: 640px) {
    width: calc(100vw - 2rem);
    max-width: none;
    left: 1rem;
    right: 1rem;
    top: 50%;
    transform: translateY(-50%);

    &[data-starting-style],
    &[data-ending-style] {
      opacity: 0;
      transform: translateY(-50%) scale(0.95);
    }
  }
}

.header {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  text-align: left;

  & h2 {
    margin: 0;
  }

  & p {
    margin: 0;
  }
}

.footer {
  display: flex;
  flex-direction: column-reverse;
  gap: 0.5rem;

  @media (min-width: 640px) {
    flex-direction: row;
    justify-content: flex-end;
  }
}

.title {
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.025em;
  color: var(--foreground);
  margin: 0;
}

.description {
  font-size: 0.875rem;
  color: var(--secondary-foreground);
  margin: 0;
  line-height: 1.5;
}

.close {
  appearance: none;
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0.5rem 1rem;
  border-radius: var(--radius);
  font-size: 0.875rem;
  font-weight: 500;
  transition: all 150ms ease-out;

  &:hover {
    background-color: var(--muted);
  }

  &:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
  }
}

More from roiui

Browse all components in this library →