Components · Data display
StatusBadge
Typed status registry: label, tone and a distinct icon or dot, so colour is never the only signal.
Service health
import { StatusBadge } from '@rookcom/monarch-react';
const health = ['operational', 'degraded', 'partial', 'down', 'maintenance', 'unknown'] as const;
{health.map((s) => (
<StatusBadge key={s} status={s} />
))}Usage examples
Variants
import { StatusBadge } from '@rookcom/monarch-react';
const health = ['operational', 'degraded', 'partial', 'down', 'maintenance', 'unknown'] as const;
const generic = ['success', 'warning', 'danger', 'info', 'neutral'] as const;
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--mn-space-2)' }}>
{generic.map((s) => (
<StatusBadge key={s} status={s} variant="outline" />
))}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--mn-space-3)' }}>
{health.map((s) => (
<StatusBadge key={s} status={s} variant="dot" />
))}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--mn-space-2)' }}>
<StatusBadge status="down" variant="solid" />
<StatusBadge status="operational" variant="solid" size="sm" />
</div>Your own statuses
import { StatusBadge, StatusRegistryProvider, defineStatuses } from '@rookcom/monarch-react';
// ClockIcon: demo icon from the docs site; use your own.
let requestCache: ReturnType<typeof buildRequestStatuses> | undefined;
const requestStatuses = () => (requestCache ??= buildRequestStatuses());
const buildRequestStatuses = () =>
defineStatuses({
open: { label: 'Open', tone: 'info' },
waiting: { label: 'Waiting on requester', tone: 'warning', icon: <ClockIcon /> },
resolved: { label: 'Resolved', tone: 'success' },
});
<StatusRegistryProvider statuses={requestStatuses()}>
<StatusBadge status="open" />
<StatusBadge status="waiting" />
<StatusBadge status="resolved" />
<StatusBadge status="pending_delete" />
</StatusRegistryProvider>Custom colours, contrast-guarded
import { StatusBadge, StatusRegistryProvider, defineStatuses } from '@rookcom/monarch-react';
const workspaceStatuses = defineStatuses({
triage: { label: 'Triage', tone: 'neutral', color: '#7c3aed' },
review: { label: 'In review', tone: 'neutral', color: '#facc15' },
shipped: { label: 'Shipped', tone: 'neutral', color: '#0d9488' },
});
<StatusRegistryProvider statuses={workspaceStatuses}>
<StatusBadge status="triage" variant="dot" />
<StatusBadge status="review" variant="dot" />
<StatusBadge status="shipped" />
<StatusBadge status="review" variant="outline" />
<StatusBadge status="triage" variant="solid" />
</StatusRegistryProvider>Purpose
Shows the state of a thing (a service, a request, a sync) from a typed registry that maps each status value to a human label, a semantic tone and an icon. Products add their own statuses, including workspace-defined colours, which pass through a contrast guard.
Use when
- A value comes from an enum (
partial,pending_delete) and needs a consistent label, tone and icon everywhere it appears. - Service health: operational, degraded, partial outage, down, maintenance, unknown.
- Users or workspaces define their own statuses with colours.
Avoid when
- A free-form tag or count with no status meaning: use Badge.
- The status must be changed in place: put a Select or DropdownMenu next to it (an editable picker is not part of StatusBadge).
Design principles
- Colour is never the only signal: every badge has text, and the built-in statuses have distinct icon shapes (check, triangle, half-filled circle, cross, wrench, dashed question).
- One registry, one meaning: define a status once with
defineStatusesand every screen shows it the same way. - Never a raw enum. Unknown values render in the neutral look with a readable label ("Pending delete").
- Custom colours mark, text reads: a custom colour tints only the dot, icon and border, and is adjusted per theme until it reaches the non-text contrast target.
- Calm by default:
subtleanddotfor tables and lists;solidonly for the one status that matters most on a screen.
API reference
import { StatusBadge, StatusRegistryProvider, builtInStatuses, checkStatusColor, defineStatuses, formatStatusLabel, resolveStatus } from '@rookcom/monarch-react';| Export | Kind | Rendering | Root class |
|---|---|---|---|
StatusBadge | component | client | .mn-status-light |
StatusRegistryProvider | component | client | .mn-status-light |
builtInStatuses | utility | client | – |
checkStatusColor | utility | client | – |
defineStatuses | utility | client | – |
formatStatusLabel | utility | client | – |
resolveStatus | utility | client | – |
builtInStatuses is the shipped registry. defineStatuses(defs, { extend? }) returns a typed registry and rejects colours that are not #rrggbb.
StatusRegistryProvider makes a registry available below it; nested providers add up. Lookup order: the statuses prop, the nearest provider, then the built-in set.
resolveStatus(status, ...registries) and formatStatusLabel(status) are the same lookup and label formatting, for legends and tooltips.
checkStatusColor(hex) is the contrast guard; it returns the per-scheme colours, the readable text colour for solid fills, and the original ratios.
Generated from the TypeScript declarations of packages/react/src/components/data-display/status-badge.
Properties
status autocompletes the built-in keys and accepts any string from your own registry.
StatusDefinition.description is data for the product’s tooltip or legend; StatusBadge does not render it, because a tooltip on a non-focusable badge is not reachable by keyboard.
StatusBadge
A status shown as text plus a tone and a distinct icon or dot, so colour is never the only signal. Statuses come from a typed registry that products extend with `defineStatuses` and `StatusRegistryProvider`.
| Prop | Type | Description |
|---|---|---|
status required | "danger" | "success" | "warning" | (string & {}) | "neutral" | "info" | "down" | "maintenance" | "operational" | "degraded" | "partial" | "unknown" | null | The status key: a built-in one or one from `statuses` / a StatusRegistryProvider. |
children | ReactNode | |
className | string | |
color | string | Overrides the registry colour (`#rrggbb`), passed through the contrast guard. |
icon | ReactNode | Replaces the registry icon. `false` hides it (the text still carries the meaning). |
id | string | |
label | ReactNode | Overrides the registry label, for example with a translated string. |
size | "sm" | "md" | |
statuses | Readonly<Record<string, StatusDefinition>> | Statuses for this badge only, checked before the provider and the built-in set. |
variant | "subtle" | "solid" | "outline" | "dot" | `subtle` (default) tinted pill; `outline` for dense tables; `solid` for a single prominent status; `dot` for a quiet coloured dot plus text. |
StatusRegistryProvider
Makes a product's statuses available to every StatusBadge below it. Nested providers add up.
| Prop | Type | Description |
|---|---|---|
children required | ReactNode | |
statuses required | Readonly<Record<string, StatusDefinition>> | Statuses added to (and overriding) the built-in set for everything inside. |
Variants
| Prop | Values | Description |
|---|---|---|
variant | 'subtle''outline''solid''dot' | Tinted pill (default), bordered pill, filled pill, or a coloured dot with plain text. Exposed as data-variant. |
size | 'sm''md' | Height and text size. Default md. |
tone (registry) | 'neutral''accent''info''success''warning''danger' | Set per status in the registry, exposed as data-tone. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Known status | data-status, data-tone | Label, tone and icon from the registry. |
| Unknown status | data-known="false" | Neutral tone, dashed question icon, readable label from the raw value. |
| Custom colour | data-custom-color | Guarded --mn-status-light and --mn-status-dark colours mark the dot, icon and border. A custom status without an icon gets a dot, so the colour always shows. |
Not interactive: no hover, focus or disabled states.
Interaction behaviour
Not interactive. Wrap it in a button or link when it should do something.
Keyboard support
Not focusable; the text is read in the normal reading order.
Accessibility
- Role
- none (inline text)
- Accessible name
- Its visible text.
- Icons and dots are
aria-hidden; the label alone carries the meaning. - Distinct icon shapes keep built-in statuses apart for people who cannot tell the colours apart (WCAG 1.4.1).
- Custom colours are guarded to 3:1 against the surface in light and dark, and to the more-contrast target in those themes (WCAG 1.4.11). Solid fills pick a text colour of at least 4.5:1.
- In forced-colours mode the border and dot use system colours.
Responsive behaviour
- Never wraps; long labels truncate with an ellipsis inside a narrow cell (
max-width: 100%). - Same size at every viewport width.
Theme support
- Tones use the Monarch intent tokens (
--mn-color-<tone>-subtle,-strong,-border,-solid), so all themes work. - Custom colours carry a light and a dark value; the dark one applies under
.darkor[data-mn-theme="dark"]. - The
accenttone follows the product accent; the status tones do not.
Do and don't
Do
- Define each product vocabulary once with
defineStatusesand share it through a provider. - Translate with the registry label or the
labelprop, never by showing the enum. - Use
dotin dense tables andsubtleelsewhere.
Don't
- Do not hide the label to save space; colour alone fails people who cannot see it.
- Do not hand-pick raw palette colours for statuses; use a tone, or a guarded custom colour.
- Do not put explanations only in a
titleattribute; show them in text or a focusable tooltip trigger.
Related components
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | @rookcom/monarch-tokensreact |
| Monarch components | None |
Uses the tokens package’s colour maths (contrast, ensureContrast) and theme surfaces for the contrast guard; it is already a peer dependency.
Testing coverage
- Renders label, tone and a decorative icon for a built-in status.
- Every built-in status has a label and an icon.
- Unknown and null values render a readable label in the neutral look.
- Dot variant shows a dot and text without the icon.
- Icon slot replaces or hides the icon;
labeloverrides the registry label. - Products extend the registry through a provider and the
statusesprop;defineStatusesextends the built-in set and rejects malformed colours. - Lookup order and label formatting.
- Contrast guard: keeps passing colours, adjusts failing ones to the target in all four themes, solid text at 4.5:1 or more, rejects non-hex input.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery; Cross-browser axe: /components/status-badge |
Tests: packages/react/src/components/data-display/status-badge