Skip to content
UI

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
NewIn progressAwaiting customerResolvedFailed
<>
  <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.

IDAlertStatus
ALR-2291Impossible travel sign-inNew
ALR-2287Malware detected on web-prod-03Triaging
ALR-2264Exposed S3 bucketAwaiting customer
ALR-2250Brute force on VPN gatewayResolved
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.
  • StatusDot is 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.

PropTypeDefault
iconLucideIcon

Leading glyph. Give one whenever color alone would carry the meaning.

-
label*string

Already-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.

PropTypeDefault
tone*"info" | "success" | "warning" | "neutral" | "danger"-