shadimage
roiui
itemalertsource: ok · hit

Alert

A component for displaying important messages to users.

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

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
class-variance-authority

Files2 · source cached 10/9/2026, 5:25:29 PM

registry/brook/ui/alert/alert.tsx
import { cva, type VariantProps } from "class-variance-authority";
import type { ReactNode } from "react";
import { cn } from "../../../lib/utils";
import styles from "./alert.module.css";

const alertVariants = cva(styles.alert, {
  variants: {
    variant: {
      default: styles.default,
      destructive: styles.destructive,
      warning: styles.warning,
      success: styles.success,
      info: styles.info,
    },
  },
  defaultVariants: {
    variant: "default",
  },
});

function DefaultAlertIcon() {
  return (
    <svg
      aria-hidden="true"
      fill="none"
      height="16"
      stroke="currentColor"
      strokeLinecap="round"
      strokeLinejoin="round"
      strokeWidth="2"
      viewBox="0 0 24 24"
      width="16"
      xmlns="http://www.w3.org/2000/svg"
    >
      <circle cx="12" cy="12" r="10" />
      <path d="M12 16V11.5" />
      <path d="M12 8.01172V8.00172" />
    </svg>
  );
}

interface AlertProps extends React.ComponentProps<"div">, VariantProps<typeof alertVariants> {
  icon?: ReactNode;
}

function Alert({ className, variant, icon, children, ...props }: AlertProps) {
  const filteredChildren = Array.isArray(children)
    ? children.filter((child) => !(child && typeof child === "object" && "type" in child && child.type === "svg"))
    : children;

  return (
    <div className={cn(alertVariants({ variant }), className)} data-slot="alert" role="alert" {...props}>
      <div className={styles.shadowRing} />
      <div className={styles.notchBg} />
      <div className={styles.notchBorder} />
      <div className={styles.iconWrap}>{icon ?? <DefaultAlertIcon />}</div>
      {filteredChildren}
    </div>
  );
}

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

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

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

export { Alert, AlertAction, AlertDescription, AlertTitle };
registry/brook/ui/alert/alert.module.css
.alert {
  position: relative;
  margin-top: 1.5rem;
  width: 100%;
  border-radius: 1rem;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  row-gap: 0.125rem;
  padding: 1rem 1.25rem;
  background-color: var(--card);
  font-size: 0.75rem;
  line-height: 1.2;
  border: 1px solid var(--border);
}

.shadowRing {
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: var(--shadow-ring);
}

.notchBg,
.notchBorder {
  position: absolute;
  top: -13px;
  left: 15px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  pointer-events: none;
}

.notchBg {
  background-color: var(--background);
}

.notchBorder {
  box-shadow: 0 0 0 1px var(--border);
  clip-path: inset(50% -3px -3px -3px);
}

.iconWrap {
  position: absolute;
  top: -7px;
  left: 20px;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  color: var(--alert-accent);
}

.iconWrap svg {
  width: 16px;
  height: 16px;
}

.title {
  margin: 0;
  font-weight: 400;
  font-size: 0.875rem;
  line-height: 1.25rem;
  letter-spacing: -0.02em;
  color: var(--secondary-foreground);
  grid-column-start: 1;
}

.description {
  margin: 0;
  font-weight: 300;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
  grid-column-start: 1;
}

.description :global(p) {
  margin: 0;
}

.action {
  grid-column-start: 2;
  grid-row: 1 / span 2;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  gap: 0.25rem;
}

.default {
  --alert-accent: var(--muted-foreground);
}

.destructive {
  --alert-accent: var(--destructive);
}

.warning {
  --alert-accent: var(--warning-foreground);
}

.success {
  --alert-accent: var(--success);
}

.info {
  --alert-accent: var(--info);
}

@media (max-width: 640px) {
  .alert {
    padding: 1rem 0.75rem;
    row-gap: 0.5rem;
  }

  .title {
    font-size: 0.9375rem;
    font-weight: 500;
  }

  .description {
    font-size: 0.9375rem;
    line-height: 1.5;
  }

  .action {
    grid-column: 1 / -1;
    grid-row: auto;
    margin-top: 0.5rem;
    justify-content: stretch;
  }

  .action > * {
    width: 100%;
  }
}

More from roiui

Browse all components in this library →