One badge, two ways to mean state

A small mono, uppercase pill under a record's title signals its state at a glance. The same primitive carries state two ways — a fixed lifecycle vocabulary, and an urgency gradient that a pill computes for itself from time-to-deadline.

Flavour A · the lifecycle vocabulary

Five semantic variants, driven from one JSON state-vocabulary so you add a state once — not per row. A confirmation wears the green (service shares resolved's tone; the 🔧 sets it apart), and the loud orange is reserved for the warning tier.

In progress Delivered · not received Refund issued · $401.74 Service complete Recall · stop use

Shelf: assets.gf.cx/status-pill · icons come from each variant's CSS ::before — never typed into the label.

Flavour B · the urgency gradient

Instead of a fixed word, the band is computed from time until due. One pill walks green → amber → red as a deadline approaches, then flips to red OVERDUE N once it passes — so a recurring chore signals its own urgency with no operator math. This flavour is emitted by the maintenance-timer primitive. Hover any pill for the weeks·days countdown (the data-tip tooltip standard).

The bare pills, side by side

DUE 6mo DUE 3w DUE 3d OVERDUE 12d NEVER LOGGED
BandTriggerPillColour
mt-ok> 30 days outDUE 6mogreen
mt-soon8–30 daysDUE 3wamber
mt-due0–7 days / todayDUE 3d · DUE NOWred
mt-overduepast dueOVERDUE 12d (carries N)red
mt-neverno data-lastNEVER LOGGEDdark red

Live — real timers rendering the pill in context

These are actual maintenance-timer cards rendered by the shipped script from a fixed data-last — the whole card (label, meta, progress bar, hover countdown), not just the badge. One reads green (months out), one reads overdue.

Wiring

<link rel="stylesheet" href="https://assets.gf.cx/maintenance-timer/maintenance-timer.css">
<link rel="stylesheet" href="https://assets.gf.cx/tooltip/tooltip.css">
<script src="https://assets.gf.cx/maintenance-timer/maintenance-timer.js" defer></script>

<div data-maintenance
     data-label="Descale"
     data-detail="every 6 months"
     data-last="2026-08-15"
     data-interval="6mo"></div>

The committed data-last is the source of truth. Add data-store="key" to show a "✓ Mark done today" button that stamps localStorage and then prints the exact data-last="…" line to bake back into the page — the browser never becomes the record.

Why one primitive, two flavours

Lifecycle state is a word a human sets; urgency is a number time computes. Both want the same at-a-glance badge, so they share the pill's shape, type, and colour semantics — a reader learns "green good, amber soon, red now" once and it holds across both. First shipped on the DeLonghi EC9255M household record — a 6-month descale cadence that counts itself down.