Components · Data

Key–value pair

The smallest unit of clinical display: a label, a number, a unit. Almost every rule in this system converges on getting these three inseparable — and on the three different things "no value" can mean.

Stable · v1.0 shadcn/ui WCAG 2.2 AA

Overview

A key–value pair looks too small to have rules. It is where the most consequential display errors happen: a value separated from its unit, a label paired with the wrong number, a blank cell read as a normal result.

Everything on this page follows from one observation — the number alone is never the information. 47 is meaningless; 47 ng/L, reference < 34, drawn 14:41 is a clinical fact.

Inseparable

Label, value and unit are one object. Nothing wraps, truncates or reflows between them, and nothing hides one behind a hover.

Comparable

A value means little without its reference range and its predecessor. Where they exist, they are shown.

Explicit about absence

Not measured, not yet resulted, and genuinely zero are three different states and never share a rendering.

Anatomy

The full form
Peak hs-cTnI
1 240ng/L
ref < 34 ng/L
drawn 15:10 · central lab
Baseline hs-cTnI
14ng/L
ref < 34 ng/L
drawn 14:41 · central lab
ST elevation
3.1mm
V2–V4, at J+60 ms
ECG 14:26:38 · model v4.2
PartRule
Key The full clinical name, never an internal code. Permanently visible above or beside the value — never a placeholder, never a tooltip.
Value Mono, tabular numerals, thin-space thousands separator. Bound to its unit with a non-breaking space.
Unit Always present. Visually lighter than the number but never hidden, never inferred, never only in the column header.
Reference Where one exists. Also carries the measurement conditions where they matter — the lead, the timing point.
Provenance When and from where. The same requirement as Card, at a finer grain.

Three kinds of nothing

The distinction that matters most

A blank cell is the most dangerous rendering in clinical software, because a clinician scanning a column reads absence as unremarkable. Not measured, awaiting result and measured as zero are clinically different and are never rendered the same way. None of them is ever a blank.

Absence, rendered explicitly
hs-cTnI
not measured
no sample requested
hs-cTnI
awaiting result
drawn 15:10 · due ~15:40
ST elevation
0.0mm
measured, no elevation
ECG 14:26:38
Prior ECG
none on file
change from baseline cannot be assessed

Change and reference

A single value is rarely the clinical question; the trajectory usually is. Where a prior exists, the pair shows the change and the interval that produced it.

Serial values with delta
hs-cTnI · 0/1 h
14 → 47ng/L
+33 ng/L over 60 min · ref < 34 ng/L
drawn 14:41 and 15:41 · central lab

Both values, the delta, the interval and the reference. The clinician is not asked to do arithmetic, and can check the arithmetic that was done.

Out of range is not an alarm

A value outside its reference range is stated, not alarmed. It gets no IEC hue, no red text and no icon — those belong to Alert Banner, which fires on a clinical rule rather than on a range boundary. Colouring every abnormal value red produces a screen of red on a sick patient and teaches clinicians to discount it. Where a threshold genuinely warrants action, the alarm says so and the value stays a value.

Layout

Do
Peak hs-cTnI
1 240ng/L
ref < 34 ng/L
drawn 15:10

Label, value, unit, reference, provenance. Readable and checkable without leaving the card.

Don't
TnI
1240

Abbreviated key, no unit, no reference, no provenance, and coloured with an alarm hue for being abnormal. Four separate rules broken in one line.

Do's and don'ts

Do
hs-cTnI
awaiting result
drawn 15:10 · due ~15:40
ST elevation
0.0mm
measured, no elevation

Awaiting and measured-zero rendered differently. Both say what happened.

Don't
hs-cTnI
ST elevation

Two dashes. One sample is in the lab, the other was measured as zero, and the column reads as unremarkable.

Do
Peak hs-cTnI
1 240ng/L
ref < 34 ng/L

Out of range stated in text, in the reference line. Survives greyscale and does not borrow an alarm hue.

Don't
Peak hs-cTnI
1 240ng/L

Abnormal values coloured red. On a sick patient the whole screen turns red and clinicians learn to discount it.

Accessibility

Outcomes of use

What this contributes to, in the terms of Usability & context of use. These are attributes believed to contribute to an outcome; the outcome itself is settled by observing real use in a specified context, not by this page.

Clinical safety notes

Risk controls carried by this component

Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1).

Implementation

$ npx shadcn@latest add https://md.notjustany.tech/r/key-value.json
// Absence is a variant of the value, not the absence of one.
type ClinicalValue =
  | { kind: "measured";  value: number; unit: string }
  | { kind: "pending";   requestedAt: string; dueAt?: string }
  | { kind: "notTaken";  reason?: string }
  | { kind: "unavailable"; reason: string }

<KeyValue
  label="Peak hs-cTnI"
  value={{ kind: "measured", value: 1240, unit: "ng/L" }}
  reference="< 34 ng/L"
  observedAt="2026-08-13T15:10:00Z"
  source="Central lab"
/>

// There is no way to render a value without a unit, and no way to
// render nothing at all.
if (value.kind === "measured" && !value.unit) {
  throw new Error("[KeyValue] a measured value requires a unit.")
}
PropTypeNotes
labelstring Required. Full clinical name.
valueClinicalValue Required. The union above; absence is a member, not a null.
referencestring Range and measurement conditions where they exist.
previousClinicalValue Renders the delta and interval automatically.
observedAt · sourcestring Required for clinical values.
NotJustAnyMed.Tech Design System · Key–value pair · v1.0 · draft for review
Reference applications named in this system are fictional; all patient data and reference ranges shown are fabricated and illustrative.