Skip to content

FeedbackAlert

Alert

A persistent, in-flow message. The page-level way to say something is or isn't right.

4 tonesDismissible variantInline placement

Tones

Info (default) is for general notes. Success is for completed actions. Warning is for non-fatal issues. Destructive is for blocking errors.

Tones

Note

A general note about the section above.

Saved

Your changes have been saved.

Be careful

This action can be undone, but only for 24 hours.

Could not save

A network error interrupted the request. Try again.

Dismissible

The close button appears in the top-right. The alert animates out on dismiss.

Dismissible

Note

You can dismiss this alert by clicking the ×.

Anatomy

  • 3 px left border in the tone color; 1 px --border on the other three sides
  • --surface-elevated background, 4 px radius
  • Icon, title, body, optional close button — all four on a single row, vertically aligned to the top
  • Default: 0.875 rem title, 0.875 rem body, 0.5 rem gap between icon and content

Implementation

alert.tsx
import { type ReactNode } from "react"
import { Info, AlertTriangle, CheckCircle2, X } from "lucide-react"
import SiteIcon from "@/components/site-icon"

type Tone = "info" | "warning" | "success" | "destructive"

interface AlertProps {
  tone?: Tone
  title: string
  children: ReactNode
  dismissible?: boolean
  onDismiss?: () => void
}

const iconMap = {
  info: Info,
  warning: AlertTriangle,
  success: CheckCircle2,
  destructive: X,
}

export function Alert({ tone = "info", title, children, dismissible, onDismiss }: AlertProps) {
  const Icon = iconMap[tone]
  return (
    <div
      className={`ds-alert ${tone === "destructive" ? "ds-alert--destructive" : ""}`}
      role={tone === "destructive" ? "alert" : "status"}
    >
      <span className="ds-alert__icon">
        <SiteIcon icon={Icon} size="md" tone={tone === "destructive" ? "emphasis" : "muted"} />
      </span>
      <div style={{ flex: 1 }}>
        <p className="ds-alert__title">{title}</p>
        <p className="ds-alert__body">{children}</p>
      </div>
      {dismissible ? (
        <button className="ds-alert__close" onClick={onDismiss} aria-label="Dismiss">
          <SiteIcon icon={X} size="sm" tone="muted" />
        </button>
      ) : null}
    </div>
  )
}

Props

PropTypeDefaultDescription
tone"info" | "warning" | "success" | "destructive""info"The visual style
titlestringNoneThe bolded title
dismissiblebooleanfalseAdds a close button
onDismiss() => voidNoneRequired if dismissible