Alert
A callout for statuses and announcements: 2 variants × 6 tones, with an optional leading icon that the grid layout indents automatically.
import { Info } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
export default function AlertDemo() {
return (
<Alert tone="info" className="max-w-md">
<Info />
<AlertTitle>Scheduled maintenance</AlertTitle>
<AlertDescription>
The eu-central-1 region will be read-only on Sunday 02:00 UTC.
</AlertDescription>
</Alert>
)
}Installation
Run the following command
npx shadcn@latest add @ai2/alertDependencies, the @ai2/tokens theme and the component file are installed together.
Install dependencies
npm install class-variance-authority@^0.7.1Add the cn util
lib/utils.tsimport { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
/* Adds a source-attribution ref param to a URL (the inspiration exports mark their
outbound links with an ai2.design attribution). An invalid URL is returned as is.
This file is SHOWN TO THE CONSUMER: the docs component pages render the source of
`cn` in a code block, so a Turkish comment here would reach every one of those
pages. Keep it English. */
export function withRef(url: string, ref = "ai2.design"): string {
try {
const u = new URL(url)
u.searchParams.set("ref", ref)
return u.toString()
} catch {
return url
}
}Copy the source code
components/ui/alert.tsximport type * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
// Icon slot works with either a lucide `<svg>` or a remix-icon `<i>` as the
// first child. Both trigger the icon column and are sized/positioned so the
// glyph aligns with the title (description wraps below in column 2).
const alertVariants = cva(
"relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-2 has-[>i]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>i]:gap-x-2 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>i]:block [&>i]:size-4 [&>i]:translate-y-0.5 [&>i]:text-base [&>i]:leading-none",
{
variants: {
variant: {
soft: "border-transparent",
outline: "bg-transparent",
},
tone: {
neutral: "",
brand: "",
success: "",
warning: "",
danger: "",
info: "",
},
},
compoundVariants: [
{ variant: "soft", tone: "neutral", class: "bg-surface-3 text-foreground [&>svg]:text-muted-foreground [&>i]:text-muted-foreground" },
{ variant: "soft", tone: "brand", class: "bg-brand-soft text-brand-soft-foreground [&>svg]:text-brand [&>i]:text-brand" },
{ variant: "soft", tone: "success", class: "bg-success-soft text-success-soft-foreground [&>svg]:text-success [&>i]:text-success" },
{ variant: "soft", tone: "warning", class: "bg-warning-soft text-warning-soft-foreground [&>svg]:text-warning-soft-foreground [&>i]:text-warning-soft-foreground" },
{ variant: "soft", tone: "danger", class: "bg-danger-soft text-danger-soft-foreground [&>svg]:text-danger [&>i]:text-danger" },
{ variant: "soft", tone: "info", class: "bg-info-soft text-info-soft-foreground [&>svg]:text-info [&>i]:text-info" },
{ variant: "outline", tone: "neutral", class: "border-border text-foreground [&>svg]:text-muted-foreground [&>i]:text-muted-foreground" },
{ variant: "outline", tone: "brand", class: "border-brand/30 text-foreground [&>svg]:text-brand [&>i]:text-brand" },
{ variant: "outline", tone: "success", class: "border-success/30 text-foreground [&>svg]:text-success [&>i]:text-success" },
{ variant: "outline", tone: "warning", class: "border-warning/40 text-foreground [&>svg]:text-warning-soft-foreground [&>i]:text-warning-soft-foreground" },
{ variant: "outline", tone: "danger", class: "border-danger/30 text-foreground [&>svg]:text-danger [&>i]:text-danger" },
{ variant: "outline", tone: "info", class: "border-info/30 text-foreground [&>svg]:text-info [&>i]:text-info" },
],
defaultVariants: { variant: "soft", tone: "neutral" },
}
)
interface AlertProps
extends React.ComponentProps<"div">,
VariantProps<typeof alertVariants> {}
function Alert({ className, variant, tone, ...props }: AlertProps) {
return (
<div
data-slot="alert"
data-tone={tone ?? "neutral"}
role="alert"
className={cn(alertVariants({ variant, tone, className }))}
{...props}
/>
)
}
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-title"
className={cn("col-start-2 min-h-4 font-medium tracking-tight", className)}
{...props}
/>
)
}
function AlertDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-description"
// Full width: it starts BELOW the icon (the icon marks the title only).
className={cn("col-span-2 col-start-1 text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
)
}
export { Alert, AlertTitle, AlertDescription, alertVariants, type AlertProps }Manual installs skip the @ai2/tokens theme, so add the token CSS from the theming guide or the tone colors will be missing.
Usage
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Info } from "lucide-react"
<Alert tone="info">
<Info />
<AlertTitle>Scheduled maintenance</AlertTitle>
<AlertDescription>
The eu-central-1 region will be read-only on Sunday 02:00 UTC.
</AlertDescription>
</Alert>Place an icon as the first child. The grid layout detects it and indents the title and description automatically. Both the icon and AlertDescription are optional.
Examples
Tones
The tone carries the semantic meaning. All six tones are shown above: neutral, brand, success, warning, danger and info.
Outline
The outline variant keeps a transparent background with a tone-tinted border, shown here in all six tones. Together with the soft row above, the full 2x6 matrix is on this page.
Title only
Props
Alert, AlertTitle and AlertDescription also accept every native <div> prop.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "soft" | "outline" | "soft" | Soft fills the surface with the tone color; outline keeps a transparent background with a tinted border. |
tone | "neutral" | "brand" | "success" | "warning" | "danger" | "info" | "neutral" | Semantic color channel for the background, border and icon, mapped to theme tokens. |
ai2 Alert: tone-aware callouts with a family-agnostic icon slot
The ai2 Alert is a shadcn-compatible react alert component styled with Tailwind CSS v4. It renders status callouts, banners and inline notices with a 2-variant x 6-tone matrix: soft fills the surface with the tone color, outline keeps a transparent background with a tinted border, and the six tones (neutral, brand, success, warning, danger, info) carry the semantic meaning across background, border and icon at once.
It ships through the shadcn registry format, so you install it with one CLI command, an MCP agent, or a copy-paste, and the TypeScript source lands in your own project. You own the file; there is no runtime dependency on ai2 itself. The live example above is the exact component you get.
What is the ai2 Alert?
It is a three-part composition: Alert, AlertTitle and AlertDescription, matching the shadcn/ui anatomy. The root is a CSS grid with an optional icon slot: drop an icon as the first child and the grid detects it, opens an icon column and tints the glyph with the active tone automatically.
The icon slot is icon-family agnostic: it accepts both a lucide <svg> icon and a remixicon <i> element, sized and aligned identically. The icon marks the title line only: AlertTitle sits next to the icon, and AlertDescription spans the full width, starting under the icon, so multi-line body text never leaves an empty gutter.
Why use it
- Tone does the whole job: One tone prop colors the background, border and icon together from the same semantic tokens Button and Badge use, so a danger alert matches a danger button by construction.
- Works with your icon family: The layout gates on both svg and i children, so lucide and remixicon projects get identical alignment without wrapper divs or manual spacing.
- Full-width descriptions: The description starts under the icon instead of staying indented in a second column, which reads better for multi-line body text and keeps line lengths sane.
- Announced by screen readers: The root carries role="alert", so dynamically inserted alerts are announced assertively by assistive technology without extra wiring.
- Agent-readable metadata: The registry item describes the variant matrix and icon behavior in plain words, so an MCP agent can pick the right tone without guessing.
Features
- 2 variants x 6 tones: 12 combinations generated by cva compound variants: soft surfaces for standard callouts, outline for quieter notices, all resolved from theme tokens.
- Auto-detected icon slot: A has-selector grid opens the icon column only when an icon is present, so icon-less alerts keep clean left alignment with zero props.
- Lucide and remixicon support: Both <svg> and <i> first children are sized to the same 16px box and tinted with the tone color, a standing ai2 compatibility rule.
- Optional parts: Title-only alerts, description-only alerts and icon-less alerts all lay out correctly; every part is optional.
- Data attributes for styling: The root exposes data-slot="alert" and data-tone, and each part has its own data-slot, so specific tones can be restyled from CSS without forking.
- Zero dependencies beyond utilities: The alert is a plain div composition with cva classes; there is no runtime primitive, which keeps the installed file small and framework-agnostic.
Production tips
- Reserve role="alert" semantics for real interruptions: Because the root announces assertively when inserted dynamically, use danger and warning tones for content users must see now; render static informational callouts with the page, not on a timer.
- Match the icon to the tone: CircleCheck for success, TriangleAlert for warning, ShieldAlert or CircleX for danger, Info for info. The tone tints any icon, but the glyph shape is what users recognize first.
- Keep titles to one line: The title shares its row with the icon. Put the sentence-length detail in AlertDescription, which wraps full width under the icon by design.
- Use outline for persistent banners: Soft surfaces attract attention; outline alerts sit quietly in settings pages and changelogs where the notice stays visible for weeks.
- Do not stack many tones: Two or more differently toned alerts in one view compete for attention. Merge related messages or demote the less urgent one to outline neutral.
Works with the rest of ai2
Alerts slot into most product surfaces. Place one above a form built from ai2 Field rows to summarize validation errors, drop an ai2 Button (ghost, same tone) after the description for actions like Retry, or pair it with an ai2 Badge when a count belongs in the title row.
For blocking confirmations, step up to the ai2 Alert Dialog instead; for transient feedback, prefer ai2 Sonner toasts. The alert is the middle ground: persistent, inline and token-consistent with both in light and dark mode.