Skip to content

Alert

ui

Preview

<div role="alert" class=${alertClass()}>
  <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
  <div data-slot="alert-title" class=${alertTitleClass()}>Heads up!</div>
  <div data-slot="alert-description" class=${alertDescriptionClass()}>You can add components to your app using the cli.</div>
</div>

Installation

webjs ui add alert

Variants

Default

You can add components to your app using the CLI.

Destructive

Your session has expired. Please log in again.

<div class="${alertClass({ variant: 'default' })} max-w-md">
  <h5 class=${alertTitleClass()}>Default</h5>
  <p class=${alertDescriptionClass()}>You can add components to your app using the CLI.</p>
</div>


<div class="${alertClass({ variant: 'destructive' })} max-w-md">
  <h5 class=${alertTitleClass()}>Destructive</h5>
  <p class=${alertDescriptionClass()}>Your session has expired. Please log in again.</p>
</div>

API Reference

Parts

Name Description
alertClass({ variant }) Container styling.
alertTitleClass() Heading inside an alert.
alertDescriptionClass() Body text inside an alert.

Props

Prop Type Default Description
variant "default" | "destructive" "default" Severity of the alert.

Source: components/ui/alert.ts

/**
 * Alert: informational banner. Tier-1 class helpers; compose with a
 * native `<div role="alert">` (or `role="status"` for non-urgent updates).
 *
 * shadcn parity:
 *   Alert (variant: default | destructive)  → alertClass({ variant })
 *   AlertTitle                              → alertTitleClass()
 *   AlertDescription                        → alertDescriptionClass()
 *
 * A11y (required for accessible output): put role="alert" on the container
 * for an urgent, interrupting message, or role="status" for a polite,
 * non-urgent update. The class helper sets no role, so without one the
 * banner is silent to assistive tech.
 *
 * Design tokens used: --card, --card-foreground, --destructive, --muted-foreground.
 *
 * @example
 * ```html
 * <div role="alert" class=${alertClass()}>
 *   <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M12 16v-4M12 8h.01" /></svg>
 *   <div data-slot="alert-title" class=${alertTitleClass()}>Heads up</div>
 *   <div data-slot="alert-description" class=${alertDescriptionClass()}>
 *     Something happened. Probably fine.
 *   </div>
 * </div>
 *
 * <!-- Destructive variant: accent stripe plus colored title and description. -->
 * <div role="alert" class=${alertClass({ variant: 'destructive' })}>
 *   <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" /><path d="M12 9v4M12 17h.01" /></svg>
 *   <div data-slot="alert-title" class=${alertTitleClass()}>Failed</div>
 *   <div data-slot="alert-description" class=${alertDescriptionClass()}>
 *     Couldn't save your changes.
 *   </div>
 * </div>
 * ```
 */
import { cn } from '../lib/utils.ts';

const BASE =
  '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-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current';

const VARIANTS = {
  default: 'bg-card text-card-foreground',
  destructive:
    'bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current',
} as const;

export type AlertVariant = keyof typeof VARIANTS;

export function alertClass(opts: { variant?: AlertVariant } = {}): string {
  return cn(BASE, VARIANTS[opts.variant ?? 'default']);
}

export const alertTitleClass = (): string =>
  'col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight';

export const alertDescriptionClass = (): string =>
  'col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed';