---
id: "packages/active-ui/status-badge"
title: "StatusBadge"
description: "A status in one of five generic tones, as a pill or as a dot, with the word always visible."
url: "/packages/active-ui/status-badge"
locale: "en"
revision: "0e2e38312a87a593"
---

# StatusBadge

`StatusBadge` shows a status: draft, sent, failed. Your application maps its own statuses to one of
five generic tones; the badge shows the word in that tone, as a tinted pill or, in dense lists, as a
dot beside the word. It is a [Badge](/packages/active-ui/badge) with a fixed tone axis.

**Example: Five tones in two appearances**

Client: Runs entirely in your browser. Source: `examples/ui/status-badge.tsx`.

```tsx
// apps/docs/examples/ui/status-badge.tsx
import {
  StatusBadge,
  type StatusTone,
} from '@workspace/active-ui/components/composed/status-badge';

// A domain status mapped to a generic tone: the mapping is the application's.
const STATUSES: {label: string; tone: StatusTone}[] = [
  {label: 'Draft', tone: 'neutral'},
  {label: 'Scheduled', tone: 'info'},
  {label: 'Sent', tone: 'success'},
  {label: 'Waiting for approval', tone: 'warning'},
  {label: 'Failed', tone: 'danger'},
];

/**
 * The five tones, as a pill to scan and as a dot for dense lists. The word
 * carries the meaning; the colour supports it.
 */
export default function StatusBadgeExample() {
  return (
    <div className="grid gap-6 sm:grid-cols-2">
      <ul className="grid justify-items-start gap-2" aria-label="Soft">
        {STATUSES.map((status) => (
          <li key={status.tone}>
            <StatusBadge tone={status.tone}>{status.label}</StatusBadge>
          </li>
        ))}
      </ul>
      <ul className="grid gap-2" aria-label="Dot">
        {STATUSES.map((status) => (
          <li key={status.tone}>
            <StatusBadge tone={status.tone} appearance="dot">
              {status.label}
            </StatusBadge>
          </li>
        ))}
      </ul>
    </div>
  );
}
```

## What the composition adds

- **Five tones**: `neutral`, `info`, `success`, `warning` and `danger`. There is no `pending`:
  a wait without action needed is `neutral`, a wait that needs attention is `warning`.
- **Readable text in every tone**: the text uses the deeper `--tone-*` colours, which reach AA on
  the tinted fill in both modes; the dot uses the full colour, which meets the 3:1 for shapes.
- **Two appearances**: `soft` (default), a pill to scan; `dot`, plain text with a coloured dot for
  rows that all carry the same status axis.

## Usage

```tsx
import {StatusBadge} from '@workspace/active-ui/components/composed/status-badge';

<StatusBadge tone="success">Sent</StatusBadge>
<StatusBadge tone="danger" appearance="dot">Failed</StatusBadge>
```

## What your application provides

- The mapping from its statuses to tones, and the translated word: the badge never shows colour
  without a word.
- A live region around the badge if its value changes after an action and should be announced.

## Accessibility

The badge has no role: it is text, read in place. The dot is hidden from assistive technology,
since the word beside it carries the meaning.

## API

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | yes | The translated label. Required: colour must never be the only carrier of meaning (WCAG 1.4.1), and this component does not translate itself. |
| `tone` | `StatusTone` | yes | The generic UI tone. Mapping from a domain status is the caller's job. |
| `appearance` | `"dot" \| "soft"` | no | `"soft"` (default) is a pill with a tinted fill: the shape for a status that stands on its own and needs to be scanned.  `"dot"` is a coloured dot with plain text next to it, without a pill. Intended for dense lists in which every row carries the same status axis; there pills together would take too much visual weight. |

**StatusTone**

```ts
type StatusTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
```

The five generic status tones.

This is **UI semantics, not domain semantics**. `active-ui` does not know
what `LAPSED` or `PENDING_APPROVAL` means; the caller decides which domain
status maps to which tone, just as active-grid does for labels with
`meta.filterOptions`.

There is deliberately no `pending` tone. "Pending" is a state in time and not
a judgement about the outcome, and the name would clash with domain values
such as `PENDING_APPROVAL`. The convention for pending statuses:

- waiting without attention or action needed → `neutral`
- waiting with attention or action needed → `warning`

**StatusBadgeProps**

```ts
interface StatusBadgeProps
  extends React.ComponentProps<'span'>, VariantProps<typeof softToneClasses> {
  tone: StatusTone;
  appearance?: 'soft' | 'dot';
  children: React.ReactNode;
}
```
