Status Badge
Shows the state of a record, such as new, in progress, resolved or failed, as a soft badge with an icon.
@zenoxai/ui/patterns/status-badge Markdown<>
<StatusBadge tone="neutral" icon={CircleDotIcon} label="New" />
<StatusBadge tone="info" icon={LoaderIcon} label="In progress" />
<StatusBadge tone="warning" icon={ClockIcon} label="Awaiting customer" />
<StatusBadge tone="success" icon={CircleCheckIcon} label="Resolved" />
<StatusBadge tone="danger" icon={TriangleAlertIcon} label="Failed" />
</>Usage
import { CircleCheckIcon } from "lucide-react"
import { StatusBadge } from "@zenoxai/ui/patterns/status-badge"
export function ResolvedBadge() {
return <StatusBadge tone="success" icon={CircleCheckIcon} label="Resolved" />
}
The library does not know your statuses. Keep one map in the app from each domain status to a tone, an icon and a translated label, and pass the result in. Every screen that shows the status then agrees on its color.
import type { StatusTone } from "@zenoxai/ui/patterns/status-badge"
function getStatusTone(status: AlertStatus): StatusTone {
switch (status) {
case "new":
return "neutral"
case "triaging":
return "info"
case "awaiting_customer":
return "warning"
case "resolved":
return "success"
case "failed":
return "danger"
}
}
| Tone | Use for | Icon |
|---|---|---|
neutral |
Not started, paused, archived. | CircleDotIcon |
info |
Running or being worked on. | LoaderIcon |
warning |
Waiting on someone, or close to a deadline. | ClockIcon |
success |
Done, healthy, connected. | CircleCheckIcon |
danger |
Failed, broken, past a deadline. | TriangleAlertIcon |
label is the translated text the user reads, never a raw enum (awaiting_customer) or an i18n
key. For the severity of a finding, use Severity Badge: severity is
a fixed scale, status is a workflow.
Examples
From a domain status
The table reads tone, icon and label from one map, keyed by the status the API returns.
| ID | Alert | Status |
|---|---|---|
| ALR-2291 | Impossible travel sign-in | New |
| ALR-2287 | Malware detected on web-prod-03 | Triaging |
| ALR-2264 | Exposed S3 bucket | Awaiting customer |
| ALR-2250 | Brute force on VPN gateway | Resolved |
function StatusBadgeFromStatus() {
// In the app, this map lives next to the domain type, not in the view.
const statusBadge = {
new: { tone: "neutral", icon: CircleDotIcon, label: "New" },
triaging: { tone: "info", icon: LoaderIcon, label: "Triaging" },
awaiting_customer: {
tone: "warning",
icon: ClockIcon,
label: "Awaiting customer",
},
resolved: { tone: "success", icon: CircleCheckIcon, label: "Resolved" },
} as const
const alerts = [
{ id: "ALR-2291", title: "Impossible travel sign-in", status: "new" },
{
id: "ALR-2287",
title: "Malware detected on web-prod-03",
status: "triaging",
},
{ id: "ALR-2264", title: "Exposed S3 bucket", status: "awaiting_customer" },
{ id: "ALR-2250", title: "Brute force on VPN gateway", status: "resolved" },
] as const
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Alert</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{alerts.map((alert) => (
<TableRow key={alert.id}>
<TableCell className="font-mono">{alert.id}</TableCell>
<TableCell>{alert.title}</TableCell>
<TableCell>
<StatusBadge {...statusBadge[alert.status]} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}Status dot
In dense lists where a badge per row is too heavy, StatusDot draws an 8px dot in the tone.
The dot is decorative: always put the status in text next to it. For inline status text
without a badge, statusToneTextClass[tone] gives the matching text color.
- CrowdStrike FalconConnected
- Microsoft DefenderToken expires soon
- AWS CloudTrailDisconnected
- OktaPaused
<ul className="flex w-80 flex-col gap-2 text-sm">
<li className="flex items-center gap-2">
<StatusDot tone="success" />
<span className="flex-1">CrowdStrike Falcon</span>
<span className="text-text-secondary">Connected</span>
</li>
<li className="flex items-center gap-2">
<StatusDot tone="warning" />
<span className="flex-1">Microsoft Defender</span>
<span className="text-text-secondary">Token expires soon</span>
</li>
<li className="flex items-center gap-2">
<StatusDot tone="danger" />
<span className="flex-1">AWS CloudTrail</span>
<span className="text-text-secondary">Disconnected</span>
</li>
<li className="flex items-center gap-2">
<StatusDot tone="neutral" />
<span className="flex-1">Okta</span>
<span className="text-text-secondary">Paused</span>
</li>
</ul>Accessibility
- Color never carries the status alone. The label always renders; add an icon whenever the status sits among others that differ only by color.
- The icon is decorative (
aria-hidden); the label is what screen readers read. StatusDotis hidden from assistive technology. Without visible text next to it, the status is lost for screen reader users and for users who cannot tell the colors apart.
API reference
Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.
StatusBadge
StatusBadge shows the state of a record (new, in progress, resolved, failed) as a soft badge. State is always told by text, and preferably an icon too, never by color alone.
| Prop | Type | Default |
|---|---|---|
icon | LucideIconLeading glyph. Give one whenever color alone would carry the meaning. | - |
label* | stringAlready-translated label. Never a raw enum or i18n key. | - |
tone* | "info" | "success" | "warning" | "neutral" | "danger"Color of the badge. | - |
StatusDot
StatusDot is a decorative 8px dot in a status tone, for dense lists.
| Prop | Type | Default |
|---|---|---|
tone* | "info" | "success" | "warning" | "neutral" | "danger" | - |