Skip to content

Feedback

Statuses and freshness

A state as a word and a colour, with the state itself as the source of the colour — and the age of the data beside it, because in a data product “how old is this?” is part of the number's meaning.

Examples

The vocabulary

Seven presets, one meaning each. The word is the accessible carrier and the colour is the fast one; extending a status means extending the vocabulary, not the palette.

LiveHealthyDegradedPendingStaleDownUnknown
Status presets
StatusToneMeaning
Livepositive + pulseReceiving data right now. The dot pulses; use it rarely.
HealthypositiveFresh and within expectations.
DegradedwarningWorking, but worse than expected. Still usable.
StalewarningData is older than the freshness window.
PendinginfoIn progress; no verdict yet. Not a problem.
DownnegativeFailed or unavailable. Needs attention.
UnknownneutralNo signal. Never render this as an error.

A custom label, an unchanged colour

The preset owns the tone and the dot; `label` overrides the word. A product that needs “Healthy · 3 regions” says so beside the status it already has rather than inventing a colour for it.

Healthy · 3 regionsStreamingDegraded · EU p99 1.4 s

Freshness

Thresholds are explicit props, so “stale” is never a mystery, and the age is computed against an injected clock — which is what makes the server and the browser agree on the same string.

8 seconds ago

Live · 8s ago

3 minutes ago

Fresh · 3m ago

42 minutes ago

Stale · 42m ago

6 hours ago

Outdated · 6h ago

Timestamps

Relative by default, exact on hover, and the machine-readable value is always in the DOM. The absolute form takes a `timeZone`, because a data product's readers are not all in one.

relative

absolute (UTC)

In a data quality panel

How the three combine with row counts in a real surface: the word, the age, and a note about why a table is in the state it is in.

market_candles_1hLive1,284,020provider: binance
market_candles_1dHealthy18,420provider: binance
market_candles_1wHealthy2,640provider: binance
legacy market_candlesStale—compatibility only
retained parquet snapshotPendingretainedreader migration pending

Usage

import { StatusBadge } from "@luc/ui/data";
<StatusBadge status="degraded" />

The preset owns the label, the tone and whether the dot pulses; `label` overrides the word and never the colour.

API

Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.

The table above is `StatusBadge`'s props; `statusPresets` is the vocabulary it resolves against, and a product extends it through `status` plus `label` rather than by adding colours.

Exported symbols
ExportKindSignature
StatusBadge3 documented props({ className, status, label, size, ...props }: StatusBadgeProps) => import("@types/react").JSX.Element
statusPresetsexport{ readonly live: { readonly tone: "positive"; readonly label: "Live"; readonly pulse: true; }; readonly healthy: { readonly tone: "positive"; readonly label: "Healthy"; }; readonly positive: { readon…

StatusBadge props

Type: StatusBadgeProps

Values the compiler reads from the declaration.

StatusBadge props
PropTypeDefaultNotes
label?string | undefined—Override the preset label.
size?"sm" | "md" | undefined—
status"pending" | "positive" | "negative" | "down" | "live" | "healthy" | "degraded" | "stale" …—

FreshnessBadge and Timestamp

Both live in their own module and have their own page, with the compiled tables for every prop — including `now`, `locale` and `timeZone`. Open the Timestamp page.

Guidelines

Do

  • Use a preset wherever the vocabulary already covers the state — the colour is then decided once, in the library.
  • Reserve `live` for a state that really is being measured right now; a pulsing dot on a stale value is a lie.
  • Pair a freshness label with an absolute timestamp somewhere in reach, because "4m" becomes wrong as the page sits open.

Don't

  • Do not let colour carry the status alone — the word is both the accessible and the colour-blind-safe carrier.
  • Do not invent a tone for a status the presets do not have; extend the vocabulary rather than the palette.
  • Do not attach a freshness badge to data that is refreshed on every render.

Accessibility

The badge reads as its label, so the status is announced as a word rather than as a colour. The dot is decorative and `aria-hidden`, and `Live`'s pulse is a slow opacity change that stops under `prefers-reduced-motion` — it never travels. Nothing here is a live region: a status that changes on its own should be announced by the region that owns it, not by every badge on the page.

Server and browser agree

A relative time is computed from a clock, and two clocks are two strings: without an injected `now`, a page rendered at 12:00:59 and hydrated at 12:01:00 disagrees about “1m” and React reports a hydration mismatch. Every specimen above passes one fixed clock, which is what a product should do for anything server-rendered.

Luc UI v1.0StatusBadgedark theme