Light

Alert

Displays a callout for user attention.

Spec · from metadata

When to use

  • Displaying success, warning, or error messages inline on a page
  • Informational notices that persist (not dismissible)
  • Form-level error summaries above or below a form
  • Status messages after an action completes

When not to use

  • Temporary notifications that auto-dismiss — use Toast instead
  • Confirmation before a destructive action — use AlertDialog
  • Inline field-level errors — use FormMessage within Form
  • Modal alerts — use AlertDialog

Variants

PropValuesDefaultDescription
variantdefaultdestructivedefaultdefault: neutral card-colored background. destructive: red text with destructive styling.

Anti-patterns

Avoid<Alert variant="destructive">
  <p>Are you sure you want to delete?</p>
  <Button variant="destructive">Delete</Button>
</Alert>
Prefer<AlertDialog>...</AlertDialog>

Alert is for displaying messages, not for confirmations. Use AlertDialog for destructive action confirmations.

Avoid<Alert>
  <AlertTitle>Error</AlertTitle>
</Alert>
Prefer<Alert variant="destructive">
  <CircleXIcon />
  <AlertTitle>Error</AlertTitle>
  <AlertDescription>Something went wrong.</AlertDescription>
</Alert>

Use the destructive variant for errors, include an icon, and always provide a description.

Accessibility

  • Screen readerrole='alert' is set by default, which creates a live region. Screen readers will announce the content when it appears. Use sparingly to avoid alert fatigue.
  • ContrastDefault variant uses card-foreground. Destructive uses text-destructive which meets WCAG AA.

Token bindings

TokenCategoryUsage
bg-cardcolorAlert background
text-card-foregroundcolorDefault text color
text-destructivecolorDestructive variant text and icon color
rounded-lgradiusAlert border radius
px-4 py-3spacingInternal padding

[!NOTE] The default variant uses text-primary for the icon (Helix navy blue). The destructive variant uses text-destructive for consistency. The warning variant uses the orange palette (tc-orange-50/200/900), matching the outlineOrange badge. The info variant uses the navy palette (tc-navy-50/200 + text-primary), matching the outlineNavy badge. The success variant uses the sage palette (tc-sage-50/200/600), matching the outlineSage badge. The error variant uses the berry palette (tc-berry-50/200/600), matching the outlineBerry badge.

Import

import { Alert, AlertTitle, AlertDescription, AlertAction } from "@timelycare/helix-ui"

Props

interface AlertProps {
  variant?: "default" | "destructive" | "warning" | "info" | "success" | "error"
  className?: string
  children: React.ReactNode
}

interface AlertTitleProps {
  className?: string
  children: React.ReactNode
}

interface AlertDescriptionProps {
  className?: string
  children: React.ReactNode
}

interface AlertActionProps {
  className?: string
  children: React.ReactNode
}

Variants

VariantBackgroundBorderText ColorDescription ColorIcon Color
defaultbg-carddefault bordertext-card-foregroundtext-muted-foregroundtext-current (inherits card-foreground)
destructivebg-carddefault bordertext-destructivetext-destructive/90text-current (inherits destructive)
warningbg-tc-orange-50border-tc-orange-200text-tc-orange-900text-tc-orange-900/80text-current (inherits orange-900)
infobg-tc-navy-50border-tc-navy-200text-primarytext-primary/80text-current (inherits primary/navy)
successbg-tc-sage-50border-tc-sage-200text-tc-sage-600text-tc-sage-600/80text-current (inherits sage-600)
errorbg-tc-berry-50border-tc-berry-200text-tc-berry-600text-tc-berry-600/80text-current (inherits berry-600)

Choosing a Variant

  • Default: General system status, neutral notices
  • Destructive: Critical errors or destructive actions requiring immediate attention
  • Warning: Cautionary notices where the user should take care (uses orange palette, matching outlineOrange badge)
  • Info: Informational notices, tips, and updates (uses navy palette, matching outlineNavy badge)
  • Success: Positive confirmations, completed actions (uses sage palette, matching outlineSage badge)
  • Error: Non-critical errors and failure states with a softer tone than destructive (uses berry palette, matching outlineBerry badge)

Styling

Typography

ElementFontSizeWeightLine HeightColor (Default)Color (Destructive)
Titlefont-sans (Adelle Sans)text-sm (14px)font-medium (Medium)leading-5 (20px)text-foregroundtext-destructive
Descriptionfont-sans (Adelle Sans)text-sm (14px)font-normal (Regular)leading-5 (20px)text-muted-foregroundtext-destructive

Container

PropertyValue
Layoutgrid (CSS Grid)
Row gapgap-0.5 (2px between title and description)
Column gapgap-x-2 (8px between icon and content)
Columns (with icon)grid-cols-[auto_1fr]
Backgroundbg-card
Borderborder (default border color for both variants)
Border radiusrounded-lg
Paddingpx-2.5 py-2
Texttext-sm text-left
Widthw-full relative
With actionpr-18 (72px right padding when AlertAction present)

Icon Grid Placement

PropertyValue
Row spanrow-span-2 (spans title + description rows)
Vertical offsettranslate-y-0.5 (aligns with title text baseline)
Colortext-current (inherits from container variant)
Default sizesize-4 (16px, applied when no explicit size class)

Title

  • font-medium (600)
  • col-start-2 when icon is present (via group-has-[>svg]/alert:col-start-2)
  • Color inherits from container (text-card-foreground or text-destructive)

Description

  • text-muted-foreground text-sm
  • text-balance md:text-pretty for nicer wrapping
  • Destructive variant: text-destructive/90 (applied via parent slot selector)

AlertAction

  • absolute top-2 right-2
  • Container adds pr-18 when AlertAction is present
  • Action buttons should use size xs (h-7 px-3 text-xs font-medium)

Icons

Size: size-4 (16×16px, default when no explicit size class) Alignment: Grid-based — row-span-2 translate-y-0.5 text-current

IconUse ForVariant
CircleCheckSuccess messagessuccess
AlertCircleErrorserror
TerminalCode/CLI related alertsdefault
InfoInformational noticesinfo
TriangleAlertWarningswarning
AlertCircleCritical errorsdestructive

Icons are placed as direct children of the Alert container. The grid auto-handles positioning — no wrapper div needed.

import { CircleCheck } from "lucide-react"

<Alert>
  <CircleCheck className="size-4" />
  <AlertTitle>Title</AlertTitle>
</Alert>

States

StateImplementation
DefaultStatic display, no interactive states
DestructiveUse variant="destructive"
WarningUse variant="warning"
InfoUse variant="info"
SuccessUse variant="success"
ErrorUse variant="error"

Alert is a static display component. Any action buttons within it have their own states.


Common Patterns

Success Alert (colored)

import { CircleCheck } from "lucide-react"

<Alert variant="success">
  <CircleCheck className="size-4" />
  <AlertTitle>Changes saved</AlertTitle>
  <AlertDescription>
    Your changes have been saved successfully.
  </AlertDescription>
</Alert>

Error Alert (colored)

import { AlertCircle } from "lucide-react"

<Alert variant="error">
  <AlertCircle className="size-4" />
  <AlertTitle>Something went wrong</AlertTitle>
  <AlertDescription>
    We couldn't process your request. Please try again.
  </AlertDescription>
</Alert>

Destructive Alert

import { AlertCircle } from "lucide-react"

<Alert variant="destructive">
  <AlertCircle className="size-4" />
  <AlertTitle>Something went wrong!</AlertTitle>
  <AlertDescription>
    Your session has expired. Please log in again.
  </AlertDescription>
</Alert>

With Action Button

<Alert>
  <CircleCheck className="size-4" />
  <AlertTitle>Dark mode is now available</AlertTitle>
  <AlertDescription>Enable it under your profile settings to get started.</AlertDescription>
  <AlertAction>
    <Button size="xs">Enable</Button>
  </AlertAction>
</Alert>

Error with Retry Action

<Alert variant="destructive">
  <AlertCircle className="size-4" />
  <AlertTitle>Failed to load data</AlertTitle>
  <AlertDescription className="flex items-center justify-between">
    <span>There was a problem fetching your data.</span>
    <Button variant="outline" size="sm" onClick={retry}>
      <RotateCcw className="size-4 mr-2" />
      Retry
    </Button>
  </AlertDescription>
</Alert>

Warning Alert

import { TriangleAlert } from "lucide-react"

<Alert variant="warning">
  <TriangleAlert className="size-4" />
  <AlertTitle>Storage almost full</AlertTitle>
  <AlertDescription>
    You have used 90% of your available storage. Consider upgrading your plan.
  </AlertDescription>
</Alert>

Info Alert

import { Info } from "lucide-react"

<Alert variant="info">
  <Info className="size-4" />
  <AlertTitle>New version available</AlertTitle>
  <AlertDescription>
    Version 2.4.0 includes performance improvements and bug fixes.
  </AlertDescription>
</Alert>

Description Only

<Alert>
  <AlertDescription>
    This one has a description only. No title. No icon.
  </AlertDescription>
</Alert>

Component Structure

// Alert container — CSS Grid layout
// Icon, title, and description are direct children (no wrapper divs)
<div
  role="alert"
  className="grid gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm
    has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2
    *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current
    *:[svg:not([class*='size-'])]:size-4
    has-data-[slot=alert-action]:pr-18
    w-full relative group/alert
    bg-card text-card-foreground"
>
  {/* Icon — direct child, grid handles positioning */}
  <CircleCheck className="size-4" />

  {/* AlertTitle */}
  <div data-slot="alert-title" className="font-medium group-has-[>svg]/alert:col-start-2">
    {title}
  </div>

  {/* AlertDescription */}
  <div data-slot="alert-description" className="text-muted-foreground text-sm text-balance md:text-pretty">
    {description}
  </div>

  {/* AlertAction (optional) — absolute positioned */}
  <div data-slot="alert-action" className="absolute top-2 right-2">
    <Button size="xs">Action</Button>
  </div>
</div>

Destructive variant differences

// Container: text-destructive instead of text-card-foreground
className="... text-destructive bg-card
  *:data-[slot=alert-description]:text-destructive/90
  *:[svg]:text-current"

Accessibility

  • Static container component — not built on a Radix interactive primitive
  • Uses role="alert" for important messages (announced immediately by screen readers)
  • No keyboard interaction needed (static content)
  • Ensure decorative icons have aria-hidden="true"

Gotchas

ProblemSolution
Icon not aligned with titleGrid handles this automatically via row-span-2 translate-y-0.5 on SVG
Destructive text hard to readOnly use for critical errors; keep text concise
Need dismissible alertAdd a close button inside AlertAction; Alert doesn't auto-dismiss
Alert too wideContains to parent width; wrap in constrained container
Action button overlaps textContainer auto-adds pr-18 when AlertAction slot is present
Icon sizing inconsistentDefault size-4 is applied when no explicit size class on the SVG

See Also


Figma Reference

View in Figma


Last updated: February 9, 2026