# Badge

A short label that tags a record with an attribute, a category or a count.

```tsx
import { Badge } from "@zenoxai/ui/components/badge"

function BadgeDemo() {
  return (
    <>
      <Badge variant="soft" tone="neutral">
        Production
      </Badge>
      <Badge variant="soft" tone="primary">
        Internet-facing
      </Badge>
      <Badge variant="outline">Linux</Badge>
    </>
  )
}
```

## Usage

```tsx
import { Badge } from "@zenoxai/ui/components/badge"

export function ExposureTag() {
  return (
    <Badge variant="soft" tone="primary">
      Internet-facing
    </Badge>
  )
}
```

Use a badge for attributes of a record that the user scans for: an environment, an asset type, a
tag, a count. Keep the label to one or two words, in sentence case.

Use something else when the badge would show:

- The state of a record (new, in progress, resolved, failed). Use
  [StatusBadge](/patterns/status-badge). It takes a semantic tone and a label, and keeps the
  status-to-tone mapping in one place in the app.
- A severity level (critical, high, medium, low). Use
  [SeverityBadge](/patterns/severity-badge), which uses the severity scale colors.

## Variant and tone

The Zenox variants, `soft`, `solid` and `outline`, take a `tone` that sets the color. Without a
`tone` they are `neutral`.

```tsx
import { Badge } from "@zenoxai/ui/components/badge"

function BadgeMatrix() {
  const variants = ["soft", "solid", "outline"] as const
  const tones = [
    "neutral",
    "primary",
    "success",
    "warning",
    "danger",
    "info",
  ] as const

  return (
    <div className="grid gap-3">
      {variants.map((variant) => (
        <div key={variant} className="flex flex-wrap items-center gap-2">
          <span className="w-14 font-mono text-xs text-text-muted">
            {variant}
          </span>
          {tones.map((tone) => (
            <Badge key={tone} variant={variant} tone={tone}>
              {tone}
            </Badge>
          ))}
        </div>
      ))}
    </div>
  )
}
```

| Variant   | Use for                                                                               |
| --------- | ------------------------------------------------------------------------------------- |
| `soft`    | The default for tags and attributes. Tinted background, low contrast.                 |
| `solid`   | One badge that must stand out in a dense row, such as "Beta" or "New". Use sparingly. |
| `outline` | Secondary tags next to soft ones, and badges rendered as links.                       |

| Tone      | Means                                           |
| --------- | ----------------------------------------------- |
| `neutral` | No judgment: an environment, a type, a tag.     |
| `primary` | Highlighted by the product: a feature, a scope. |
| `success` | Positive or protected: enforced, verified.      |
| `warning` | Needs attention: exposed, expiring.             |
| `danger`  | Harmful or broken: compromised, blocked.        |
| `info`    | Informational: preview, monitored.              |

The shadcn variants `default`, `secondary`, `destructive`, `ghost` and `link` still exist and
ignore `tone`. Prefer the Zenox variants in new code.

## Pill

`pill` rounds the badge fully. Use it for counts next to a label, and add `tabular-nums` so the
width holds as the number changes.

```tsx
import { Badge } from "@zenoxai/ui/components/badge"

function BadgePill() {
  return (
    <div className="flex items-center gap-2 text-sm">
      Open alerts
      <Badge variant="soft" tone="danger" pill className="tabular-nums">
        24
      </Badge>
    </div>
  )
}
```

## With an icon

Mark the icon with `data-icon="inline-start"` or `data-icon="inline-end"`; the badge sizes it to
12px and tightens the padding on that side.

```tsx
import { GlobeIcon, ShieldCheckIcon } from "lucide-react"
import { Badge } from "@zenoxai/ui/components/badge"

function BadgeWithIcon() {
  return (
    <>
      <Badge variant="outline" tone="success">
        <ShieldCheckIcon data-icon="inline-start" />
        MFA enforced
      </Badge>
      <Badge variant="soft" tone="warning">
        <GlobeIcon data-icon="inline-start" />
        Exposed port 3389
      </Badge>
    </>
  )
}
```

## As a link

A badge that filters a list or opens a record renders an `<a>` through the `render` prop, with
the label inside the link. The `outline` variant then gets a hover background.

```tsx
import { Badge } from "@zenoxai/ui/components/badge"

function BadgeAsLink() {
  return <Badge variant="outline" render={<a href="#usage">tag:pci-scope</a>} />
}
```

## Accessibility

- A badge is plain text. When its meaning depends on the column or the context ("24" next to
  "Open alerts"), keep that context visible or put it in the label.
- The tone is a visual hint. The label alone must say what the badge means.

## API reference

`import { ... } from "@zenoxai/ui/components/badge"`

### Badge

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| pill | `boolean \| null` |  | Fully rounded ends instead of the default small radius. |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| tone | `"info" \| "success" \| "warning" \| "neutral" \| "primary" \| "danger" \| null` |  | Color for the `soft`, `solid` and `outline` variants (`neutral` has no effect on `outline`). The other variants ignore it. |
| variant | `"link" \| "default" \| "destructive" \| "outline" \| "secondary" \| "ghost" \| "soft" \| "solid" \| null` |  | Style. `default`, `secondary`, `destructive`, `outline`, `ghost` and `link` are complete on their own. `soft` (tinted fill) and `solid` (strong fill) take their color from `tone` and render uncolored without one. |

