Skip to content

Badge

A tinted, square-cornered plate carrying a state word. Three vocabularies:

jsx
<Badge status="success" dot>Supported</Badge>   // outcome: did it succeed
<Badge state="running">Running</Badge>          // workflow: where work has got to
<Badge state="needs_review">Needs review</Badge>
<Badge decision="denied">Denied</Badge>         // approval decisions

status: neutral · success · warning · danger · info — optional dot. state: queued · running · needs-input · needs-review · done · failed (snake_case accepted). decision: pending · approved · denied · expired.

State/decision badges are hairline-bordered and always marked: queued and expired render a hollow ring (nothing is asked of anyone / nobody decided), the rest a filled dot. Queued and done share stone deliberately — the mark tells them apart. Colour is never the sole carrier.

Preview

Declarations

TypeScript
export type WorkflowState = 'queued' | 'running' | 'needs-input' | 'needs-review' | 'done' | 'failed';
export type Decision = 'pending' | 'approved' | 'denied' | 'expired';

export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
  /** Outcome semantics — did the thing succeed or fail. */
  status?: 'neutral' | 'success' | 'warning' | 'danger' | 'info';
  /** Workflow state — where a piece of work has got to. Overrides `status`.
   *  Always rendered with a mark: hollow ring for queued, filled dot
   *  otherwise (colour is never the sole carrier). Accepts snake_case too. */
  state?: WorkflowState;
  /** Approval decision. `expired` takes the hollow ring — nobody decided. */
  decision?: Decision;
  /** Show a leading dot (status badges only; state/decision always show one). */
  dot?: boolean;
  className?: string;
  children?: React.ReactNode;
}

/**
 * A tinted status plate carrying a state word — SUPPORTED, RUNNING,
 * NEEDS REVIEW, DENIED. Square corners; reads like an engraved indicator.
 */
export function Badge(props: BadgeProps): React.JSX.Element;