Status Indicator

A richer companion to Status Dot. Where Status Dot is a tiny inline glyph for use mid-sentence, Status Indicator pairs a colored dot with a prominent label sized for dashboards, user lists, and system-status panels. Six operational statuses, three sizes, and an optional pulse halo for live signals.

Online
Offline
Idle
Busy
Degraded
Maintenance
Customize
Status & size
online
md
Pulse & label
pulse

Installation

npx shadcn@latest add https://craftbits.dev/r/status-indicator.json

Usage

import { StatusIndicator } from "@craft-bits/core";
 
<StatusIndicator status="online" label="Online" pulse="pulse" />

A live, pulsing indicator on one line; a calm "offline" row on the next:

<StatusIndicator status="offline" label="Offline" />
<StatusIndicator status="maintenance" label="Scheduled maintenance" size="lg" />

The label is optional — drop it and you get just a sized dot:

<StatusIndicator status="busy" aria-label="Busy" />

Understanding the component

  1. Status-driven palette. status selects six operational colors: online (success), offline (muted), idle (warning), busy (error), degraded (warning), and maintenance (accent). The label inherits the dot color so the eye reads dot and word as one signal.
  2. Three sizes. size="sm" for inline rows, "md" (default) for table cells and lists, "lg" for hero status displays. The dot diameter, gap, and label font scale together so the proportions stay consistent.
  3. Pulse via CSS, not JS. The @keyframes cb-status-indicator-pulse rule is injected once into <head> the first time a pulsing indicator mounts. It's wrapped in @media (prefers-reduced-motion: no-preference) so reduced-motion users see a static dot automatically — no JS branch.
  4. Pulse modes. pulse="static" (default) paints a solid dot. pulse="pulse" layers an outward-fading halo on top — reserve it for genuinely live signals. pulse="none" hides the dot entirely so a consumer can rebuild the row with its own glyph while keeping the typography.
  5. Sized for chip use. align-middle keeps the indicator optically centered against surrounding text; select-none prevents accidental highlighting when users drag-select rows in a dashboard.

Props

PropTypeDefaultDescription
status'online' | 'offline' | 'idle' | 'busy' | 'degraded' | 'maintenance'requiredOperational status — drives dot and label color.
labelReactNodeOptional text rendered after the dot. When omitted, only the dot renders.
pulse'none' | 'static' | 'pulse''static'Dot mode. pulse adds a halo that respects prefers-reduced-motion.
size'sm' | 'md' | 'lg''md'Indicator size — drives dot diameter, gap, and label scale.
classNamestringMerged onto the rendered root <div>.
...restHTMLAttributes<HTMLDivElement>Any other div prop (aria-*, data-*, onClick, …).

Accessibility

  • The indicator renders a single <div> containing the visible label — screen readers announce the label as-is. The dot is marked aria-hidden="true" so assistive tech reads only the label.
  • For dynamic status updates, wrap the indicator in an element with role="status" or aria-live="polite" so changes are announced.
  • When used without a label, supply aria-label so the indicator has an accessible name — otherwise it's invisible to assistive tech.
  • The pulse animation is automatically disabled when prefers-reduced-motion: reduce is set — the rule is scoped under @media (prefers-reduced-motion: no-preference). Reduced-motion users see a static dot identical to pulse="static".

Credits

  • Extracted from: terminal-dreams (src/components/principles/demo-primitives/StatusDot.tsx). Generalized into a richer "indicator" variant distinct from the existing <StatusDot> primitive — operational status names, prominent label typography, and three sizes designed for dashboards rather than inline-with-text use.