[!NOTE] The
defaultvariant usestext-primaryfor the icon (Helix navy blue). Thedestructivevariant usestext-destructivefor consistency. Thewarningvariant uses the orange palette (tc-orange-50/200/900), matching theoutlineOrangebadge. Theinfovariant uses the navy palette (tc-navy-50/200+text-primary), matching theoutlineNavybadge. Thesuccessvariant uses the sage palette (tc-sage-50/200/600), matching theoutlineSagebadge. Theerrorvariant uses the berry palette (tc-berry-50/200/600), matching theoutlineBerrybadge.
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
| Variant | Background | Border | Text Color | Description Color | Icon Color |
|---|---|---|---|---|---|
default | bg-card | default border | text-card-foreground | text-muted-foreground | text-current (inherits card-foreground) |
destructive | bg-card | default border | text-destructive | text-destructive/90 | text-current (inherits destructive) |
warning | bg-tc-orange-50 | border-tc-orange-200 | text-tc-orange-900 | text-tc-orange-900/80 | text-current (inherits orange-900) |
info | bg-tc-navy-50 | border-tc-navy-200 | text-primary | text-primary/80 | text-current (inherits primary/navy) |
success | bg-tc-sage-50 | border-tc-sage-200 | text-tc-sage-600 | text-tc-sage-600/80 | text-current (inherits sage-600) |
error | bg-tc-berry-50 | border-tc-berry-200 | text-tc-berry-600 | text-tc-berry-600/80 | text-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
outlineOrangebadge) - Info: Informational notices, tips, and updates (uses navy palette, matching
outlineNavybadge) - Success: Positive confirmations, completed actions (uses sage palette, matching
outlineSagebadge) - Error: Non-critical errors and failure states with a softer tone than destructive (uses berry palette, matching
outlineBerrybadge)
Styling
Typography
| Element | Font | Size | Weight | Line Height | Color (Default) | Color (Destructive) |
|---|---|---|---|---|---|---|
| Title | font-sans (Adelle Sans) | text-sm (14px) | font-medium (Medium) | leading-5 (20px) | text-foreground | text-destructive |
| Description | font-sans (Adelle Sans) | text-sm (14px) | font-normal (Regular) | leading-5 (20px) | text-muted-foreground | text-destructive |
Container
| Property | Value |
|---|---|
| Layout | grid (CSS Grid) |
| Row gap | gap-0.5 (2px between title and description) |
| Column gap | gap-x-2 (8px between icon and content) |
| Columns (with icon) | grid-cols-[auto_1fr] |
| Background | bg-card |
| Border | border (default border color for both variants) |
| Border radius | rounded-lg |
| Padding | px-2.5 py-2 |
| Text | text-sm text-left |
| Width | w-full relative |
| With action | pr-18 (72px right padding when AlertAction present) |
Icon Grid Placement
| Property | Value |
|---|---|
| Row span | row-span-2 (spans title + description rows) |
| Vertical offset | translate-y-0.5 (aligns with title text baseline) |
| Color | text-current (inherits from container variant) |
| Default size | size-4 (16px, applied when no explicit size class) |
Title
font-medium(600)col-start-2when icon is present (viagroup-has-[>svg]/alert:col-start-2)- Color inherits from container (
text-card-foregroundortext-destructive)
Description
text-muted-foreground text-smtext-balance md:text-prettyfor nicer wrapping- Destructive variant:
text-destructive/90(applied via parent slot selector)
AlertAction
absolute top-2 right-2- Container adds
pr-18when 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
| Icon | Use For | Variant |
|---|---|---|
CircleCheck | Success messages | success |
AlertCircle | Errors | error |
Terminal | Code/CLI related alerts | default |
Info | Informational notices | info |
TriangleAlert | Warnings | warning |
AlertCircle | Critical errors | destructive |
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
| State | Implementation |
|---|---|
| Default | Static display, no interactive states |
| Destructive | Use variant="destructive" |
| Warning | Use variant="warning" |
| Info | Use variant="info" |
| Success | Use variant="success" |
| Error | Use 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
| Problem | Solution |
|---|---|
| Icon not aligned with title | Grid handles this automatically via row-span-2 translate-y-0.5 on SVG |
| Destructive text hard to read | Only use for critical errors; keep text concise |
| Need dismissible alert | Add a close button inside AlertAction; Alert doesn't auto-dismiss |
| Alert too wide | Contains to parent width; wrap in constrained container |
| Action button overlaps text | Container auto-adds pr-18 when AlertAction slot is present |
| Icon sizing inconsistent | Default size-4 is applied when no explicit size class on the SVG |
See Also
- Related Components: Alert Dialog (blocking confirmation), Sonner (toast notifications)
- Patterns: Component Match
Figma Reference
Last updated: February 9, 2026