opsinjs
ComponentsHealth data display

StatusPill

A short label that says which of four levels of attention something needs. It is shown as a word, an icon and a colour together, never as a colour alone.

Preview

Theme
Density
Text
Status
status-pill · base base · style base-lyraOpen under the product theme

Installation

pnpm dlx shadcn@latest add @opsinjs/status-pill

The @opsinjs namespace is declared in your components.json. Everything it installs is code you then own. There is no runtime package to keep in step.

Usage

import { StatusPill } from "@/components/ui/status-pill"
<StatusPill status="attention" describes="Second example measurement" />

When to use it

Use it when

  • Labelling one result, reading or item with the level of attention the product has assigned to it.
  • Making a status scannable in a list, a table or a grid, where reading each row in full is not realistic.
  • Summarising a surface whose status the reader would otherwise have to infer from its contents.

Do not use it when

  • The status needs explaining, or the reader needs to know what to do about it.Use care-card instead.
  • The status must interrupt what the reader is doing rather than wait to be noticed.Use alert-banner instead.
  • The label is a category, a source, a tag, a count or a release phase rather than a level of attention.Use badge instead.
  • You want to show where a value sits relative to a range, which is a position rather than a level.Use range-bar instead.
  • The message is informational and carries no clinical level at all.Use callout instead.

Clinical meaning

Asserts. That the product has assigned one of exactly four levels of attention to the thing the pill labels. The levels are steady, watch, attention and urgent, defined in Clinical status semantics. It does not say what is wrong, why, or what happens next.

Never read as. A diagnosis, a severity score or a triage category. The levels describe what the interface asks of the reader, not the state of their body. urgent on a screen is not urgent in a clinical handover.

Colour axis. Status only, and never alone: every pill renders a word and a glyph beside its colour. A pill never carries a category colour. That is a Badge under Category identity, and The two colour axes says why the two never share an element.

Thresholds. None. The consuming product assigns the level from rules or a clinician, and the pill derives nothing.

Anatomy

  1. StatusPillThe root. Carries data-status and the level's surface, line and ink. Not focusable and not interactive.Controlled by status
  2. StatusPill.IconOne distinct glyph per level, from CLINICAL_STATUS_META[level].icon. aria-hidden and sized in em.
  3. StatusPill.LabelThe level in words. Not removable; an empty label raises OPSIN-0002 and falls back to the level's word.Controlled by label

Examples

Scanning a column

Four rows, each graded by status, and exactly one urgent. A pill earns its place when the alternative is reading every row.

Theme
Density
Text
Status
status-pill-in-a-list · base base · style base-lyraOpen under the product theme

Every size keeps the word

size sets the type step and padding and nothing else. sm and md label a status in prose and lists; lg matches a heading. No size drops the word.

Theme
Density
Text
Status
status-pill-sizes · base base · style base-lyraOpen under the product theme

Content guidelines

Use the level's own word, and use the same word on every screen. Never write a level as a sentence about the person: "Needs attention" labels a result, not a human being. A banned word passed as label raises OPSIN-0006 in development; Reference ranges and normal holds the list.

Do

"Steady" for a result that needs nothing right now.

Don’t

"Normal", "Healthy", "Good", "All clear" are claims about the person.

Accessibility

Audited against WCAG 2.2 AA in a source pass and a rendered pass. The audit is author-run, it is not an independent review, and clinical review is still pending. The rendered pass reported no axe violations, no target-size hits, no focus problems and no reflow break at 320px, and found nothing to change.

Checked by pnpm run check:a11y on every commit.

  • No file paints a status colour without reading CLINICAL_STATUS_META and a glyph, and every status surface carries data-status.
  • The four levels name four distinct glyphs: circle, ringed dot, diamond and octagon. A11Y017 fails the build when two levels share one.
  • The status words are read from CLINICAL_STATUS_META[level].word, never written as a literal.

Argued from the source.

  • Status is a visible word plus one of four distinct glyph shapes plus colour, never colour alone. The icon is aria-hidden and sized in em, so the word carries the meaning and the glyph grows with it.
  • With describes, the pill announces as Needs attention, Second example measurement. Omit it when the subject is visible text in the same reading unit. When the pill floats free of its subject, in a table cell reached by column navigation or a card corner, the consuming product must pass describes, because the component cannot know the reading context. That is by design, and the prop JSDoc records it.
  • The pill is a span, not focusable and not in the tab order. A pill made into a filter is a Button and owes a Button's target and name.
  • No live region is mounted, on purpose. The pill is a static read-only label rather than a message, so a role="status" announcement would be wrong here.
  • At 200% text the pill grows and wraps; the word is never truncated and never replaced by the icon alone.

Not yet measured. The rendered pass ran in the light theme and reported no contrast violation, so light-mode text contrast passes. Dark-mode text contrast, forced colours, and the non-text contrast of the boundary and glyph against the surface were not measured. Those pairs arrive through role tokens and live in the token layer and the report below, not in this source.

KeyActionNotes
TabSkips the pillNot focusable. A label with a tab stop is a stop every row pays for.
Shift+TabSkips the pillSame in reverse. Never in the focus order in either direction.
PairThemeAPCA LcWCAG 2.2Floor
attention ink on attention surfacelight89.410.35:1Pass
attention ink on attention surfacedark-81.112.54:1Pass
attention line on attention surfacelight70.04.90:1Pass
attention line on attention surfacedark-65.59.54:1Pass
attention accent on attention surfacelight65.14.18:1Pass
attention accent on attention surfacedark-49.46.80:1Pass
attention ink on the attention bandlight22.42.48:1Below floor
attention ink on the attention banddark-31.01.84:1Below floor
attention accent on the pagelight67.84.35:1Pass
attention accent on the pagedark-50.57.47:1Pass
attention ink on the pagelight92.210.77:1Pass
attention ink on the pagedark-82.313.76:1Pass
steady ink on steady surfacelight88.39.62:1Pass
steady ink on steady surfacedark-82.912.56:1Pass
steady line on steady surfacelight67.54.45:1Pass
steady line on steady surfacedark-68.99.91:1Pass
steady accent on steady surfacelight56.43.21:1Pass
steady accent on steady surfacedark-53.27.25:1Pass
steady ink on the steady bandlight30.03.00:1Below floor
steady ink on the steady banddark-28.81.73:1Below floor
steady accent on the pagelight58.63.31:1Pass
steady accent on the pagedark-54.58.19:1Pass
steady ink on the pagelight90.69.94:1Pass
steady ink on the pagedark-84.214.20:1Pass
unknown ink on unknown surfacelight89.59.98:1Pass
unknown ink on unknown surfacedark-82.212.56:1Pass
unknown line on unknown surfacelight69.24.64:1Pass
unknown line on unknown surfacedark-67.29.72:1Pass
unknown accent on unknown surfacelight43.12.29:1Below floor
unknown accent on unknown surfacedark-51.37.05:1Pass
unknown accent on the pagelight45.62.37:1Below floor
unknown accent on the pagedark-52.37.84:1Pass
unknown ink on the pagelight92.010.34:1Pass
unknown ink on the pagedark-83.113.97:1Pass
urgent ink on urgent surfacelight88.210.58:1Pass
urgent ink on urgent surfacedark-80.312.52:1Pass
urgent line on urgent surfacelight70.05.09:1Pass
urgent line on urgent surfacedark-64.89.52:1Pass
urgent accent on urgent surfacelight72.35.58:1Pass
urgent accent on urgent surfacedark-48.06.63:1Pass
urgent ink on the urgent bandlight13.91.90:1Below floor
urgent ink on the urgent banddark-31.51.89:1Below floor
urgent accent on the pagelight75.35.84:1Pass
urgent accent on the pagedark-49.57.24:1Pass
urgent ink on the pagelight91.211.06:1Pass
urgent ink on the pagedark-81.813.66:1Pass
watch ink on watch surfacelight89.410.02:1Pass
watch ink on watch surfacedark-82.112.58:1Pass
watch line on watch surfacelight69.54.72:1Pass
watch line on watch surfacedark-66.89.68:1Pass
watch accent on watch surfacelight61.73.72:1Pass
watch accent on watch surfacedark-50.76.95:1Pass
watch ink on the watch bandlight25.72.70:1Below floor
watch ink on the watch banddark-30.61.81:1Below floor
watch accent on the pagelight64.23.85:1Pass
watch accent on the pagedark-51.77.71:1Pass
watch ink on the pagelight91.910.38:1Pass
watch ink on the pagedark-83.113.95:1Pass
body text on the pagelight101.617.18:1Pass
body text on the pagedark-100.518.00:1Pass
body text on a cardlight104.717.96:1Pass
body text on a carddark-99.616.32:1Pass
body text on the muted groundlight98.116.32:1Pass
body text on the muted grounddark-97.513.77:1Pass
secondary text on the pagelight83.97.42:1Pass
secondary text on the pagedark-80.713.44:1Pass
secondary text on a cardlight87.07.76:1Pass
secondary text on a carddark-79.812.19:1Pass
secondary text on the muted groundlight80.47.05:1Pass
secondary text on the muted grounddark-77.810.28:1Pass
a hairline boundary on the pagelight19.31.41:1Below floor
a hairline boundary on the pagedark-8.21.90:1Below floor
a hairline boundary on a cardlight22.41.47:1Below floor
a hairline boundary on a carddark-7.31.72:1Below floor
a hairline boundary on the muted groundlight15.81.34:1Below floor
a hairline boundary on the muted grounddark0.01.45:1Below floor
a placeholder boundary on the pagelight69.84.52:1Pass
a placeholder boundary on the pagedark-51.07.62:1Pass
a placeholder boundary on a cardlight72.84.72:1Pass
a placeholder boundary on a carddark-50.16.91:1Pass
a placeholder boundary on the muted groundlight66.24.29:1Pass
a placeholder boundary on the muted grounddark-48.15.83:1Pass
a placeholder fill on the pagelight8.31.18:1Below floor
a placeholder fill on the pagedark-8.21.90:1Below floor
a placeholder fill on a cardlight11.41.23:1Below floor
a placeholder fill on a carddark-7.31.72:1Below floor
a placeholder fill on the muted groundlight0.01.12:1Below floor
a placeholder fill on the muted grounddark0.01.45:1Below floor
a placeholder boundary on its own filllight58.93.84:1Pass
a placeholder boundary on its own filldark-41.54.01:1Below floor
the card hairline on the pagelight69.84.52:1Pass
the card hairline on the pagedark-28.74.19:1Below floor
the card hairline on a cardlight72.84.72:1Pass
the card hairline on a carddark-27.93.80:1Below floor
attention surface on the card materiallight0.01.09:1Below floor
attention surface on the card materialdark0.01.00:1Below floor
steady surface on the card materiallight0.01.08:1Below floor
steady surface on the card materialdark0.01.03:1Below floor
urgent surface on the card materiallight0.01.09:1Below floor
urgent surface on the card materialdark0.01.01:1Below floor
watch surface on the card materiallight0.01.08:1Below floor
watch surface on the card materialdark0.01.01:1Below floor
the focus ring on the attention surfacelight67.34.44:1Pass
the focus ring on the attention surfacedark-45.96.30:1Pass
attention ink on the neutral hover filllight88.610.23:1Pass
attention ink on the neutral hover filldark-79.310.53:1Pass
the focus ring on the steady surfacelight67.84.47:1Pass
the focus ring on the steady surfacedark-45.86.12:1Pass
steady ink on the neutral hover filllight87.09.43:1Pass
steady ink on the neutral hover filldark-81.310.86:1Pass
the focus ring on the urgent surfacelight67.04.42:1Pass
the focus ring on the urgent surfacedark-45.56.34:1Pass
urgent ink on the neutral hover filllight87.710.50:1Pass
urgent ink on the neutral hover filldark-78.910.46:1Pass
the focus ring on the watch surfacelight67.64.46:1Pass
the focus ring on the watch surfacedark-46.06.24:1Pass
watch ink on the neutral hover filllight88.39.86:1Pass
watch ink on the neutral hover filldark-80.110.67:1Pass

These are the measured token pairs this component draws colour from, not a measurement of the component itself.

Data attributes

AttributeConditionValue
data-slotOn the root and both inner parts, alwaysstatus-pill, status-pill-icon, status-pill-label
data-statusOn the root, always, because status is requiredsteady | watch | attention | urgent

API reference

Prop

Type

Generated from StatusPillProps in registry/bases/base/status-pill.tsx.

status has no default. A value outside the four, including unknown, renders no pill and raises a development warning. className is merged last and can strip the fill or the line, so change size instead. It cannot recolour the word; a rule on [data-slot="status-pill"] can.

  • Badge is a label, not a judgement. Categories, sources, counts and release phases are badges.
  • AlertBanner shares the vocabulary but interrupts. A pill waits to be noticed.
  • CareCard is the status with an instruction attached, when the reader has to act.
  • Callout sits outside the status axis, for information with no clinical level.

On this page