---
title: "Unit systems"
description: "mmol/L against mg/dL, kilograms against stones and pounds, Celsius against Fahrenheit. Conversion is where a wrong answer is a plausible number, not an error."
url: "https://opsinjs.pensievelabs.org/health/unit-systems"
source: "https://opsinjs.pensievelabs.org/health/unit-systems.md"
section: "Health"
kind: "health"
evidence: "opinion"
reviewed: "2026-09-02"
reviewer: "design"
aliases: ["mmol/L", "mg/dL", "imperial", "metric", "stone", "fahrenheit", "unit conversion"]
implements: ["value", "reading-input", "result-card", "range-bar", "metric-tile", "log-sheet"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="health" />

## What this means [#what-this-means]

A unit system is a correctness constraint rather than a presentation one, and
what distinguishes it is that a unit failure is invisible to the reader. A
missing translation shows a raw key and a wrong date format looks wrong, so both
defects are caught by anyone who looks at the screen. A unit failure produces a
number that is well formed and plausible in the system it was computed for, and
wrong for the reader in front of it.

Blood glucose is reported in two systems. Much of the world reports it in
mmol/L, where everyday values are single digits with one decimal, and the United
States reports the same measurement in mg/dL, where everyday values are in the
tens and hundreds. A reader who has learnt one scale and is shown the other
without a label has no cue that separates a change of unit from a change in
their body, because the only thing that has visibly changed is the number.

Body weight fails through notation rather than through scale, because the same
digits carry two readings inside one system. A reader who thinks in stones and
pounds and is shown `11.3` will read it as eleven stone three pounds. The value
is eleven point three stone, which is eleven stone four, so the figure displayed
and the figure read differ by one pound. That discrepancy is small against a
weight trend and decisive wherever weight is an input to a calculation, such
as a paediatric dose, which this page excludes under "What this does not cover".

Temperature differs from the other two because a reader recognises the plausible
range of a body temperature in either scale, so a reading in the wrong scale is
visible as a wrong reading. The residual risk sits in the threshold rather than
in the reading, because a fever threshold expressed in the wrong scale is a
wrong threshold.

## The rule [#the-rule]

**The unit system is a user preference, stored, respected everywhere, and never
inferred silently. A converted value is labelled as converted, and conversion
happens once, at the display boundary.**

### The ten rules [#the-ten-rules]

| #  | Requirement                                                                                                                                                                                                                                             | Where it applies        | Who owns it                            | Failure it prevents                                                                                |
| -- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------- |
| 1  | One canonical unit per metric is used in storage and in every API, and conversion is a rendering concern that never round trips through storage.                                                                                                        | Storage and API         | The product's data model               | A stored figure whose unit depends on the preference in force when it was written                  |
| 2  | The unit preference is explicit, and it is held per metric family wherever readers genuinely differ between families, such as kilograms for weight alongside feet and inches for height.                                                                | Settings and storage    | The reader                             | A single global switch that forces one system onto every metric                                    |
| 3  | Locale supplies the initial default and never the settled answer, because readers move between countries, clinicians and patients differ, and a device may report in a system its owner does not use.                                                   | Onboarding and defaults | The reader, asked once                 | A default that is wrong for the whole relationship and is never corrected                          |
| 4  | A value shown in a system other than the one it arrived in is labelled as converted, in the form "Converted from 98 mg/dL."                                                                                                                             | Display                 | `Value`                                | A converted figure read as the figure the device reported                                          |
| 5  | Ranges, thresholds and statuses convert with the value they are compared against, and the comparison itself runs on canonical units only.                                                                                                               | The display boundary    | The clinical source owns the threshold | A status derived from a value and a range held in different units                                  |
| 6  | Rounding happens after conversion and uses the destination metric's precision, and an original value is never re-derived from a rounded conversion, because the round trip is lossy. See [Numbers, units and precision](./numbers-units-precision.mdx). | Display                 | The destination metric                 | A value that drifts each time the reader switches systems                                          |
| 7  | The unit is part of the entry field, visible at all times, switchable in place and remembered for the next entry. See [Units and numeric entry](../patterns/forms/units-and-numeric-entry.mdx).                                                         | Entry                   | `ReadingInput`                         | A figure typed in one system and stored as another                                                 |
| 8  | Compound imperial quantities are entered compound, so stones and pounds is two inputs and feet and inches is two inputs.                                                                                                                                | Entry                   | `ReadingInput`                         | A compound quantity collapsed into one decimal field, which the reader then has to compose by hand |
| 9  | Entry is plausibility-checked against the entered unit, and a figure far from the reader's own previous readings raises a question rather than a rejection: "That is much higher than your usual readings. Is it in mg/dL?"                             | Entry                   | The reader confirms or corrects        | A mg/dL figure stored as mmol/L                                                                    |
| 10 | Exports, shares and print carry the unit unambiguously, because the recipient may not share the reader's preference. See [Sharing with a clinician](../patterns/sharing-with-a-clinician.mdx).                                                          | Export, share and print |                                        | A recipient reading the figure in their own default system                                         |

### Units that need explicit handling [#units-that-need-explicit-handling]

The measurements below are the ones a consumer health product has to handle
explicitly.

| Measurement    | Common systems    | The specific hazard                                                                                                 |
| -------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| Blood glucose  | mmol/L · mg/dL    | Both plausible; ranges do not overlap but neither looks wrong                                                       |
| Cholesterol    | mmol/L · mg/dL    | As above, with more sub-measures to get wrong                                                                       |
| Body weight    | kg · lb · st + lb | Decimal stone misread as stone-and-pounds                                                                           |
| Height         | cm · ft + in      | Compound entry; decimal feet is meaningless to most readers                                                         |
| Temperature    | °C · °F           | Fever thresholds are scale-specific                                                                                 |
| Blood pressure | mmHg (universal)  | Rarely converted, frequently mis-composed                                                                           |
| Distance       | km · miles        | Conversion error is visible to the reader, so the risk is inconsistency between screens rather than a misread value |
| Energy         | kcal · kJ         | Factor of about four; both called "calories" colloquially                                                           |

## Why (evidence) [#why-evidence]

<ResearchNote evidence="opinion" date="2026-09-02">
  This page states a position and cites no study. The argument it rests on is
  arithmetic rather than empirical: a conversion is either correct or incorrect,
  and what makes an incorrect one dangerous is that the reader has no way to
  detect it.

  The one claim worth stating carefully is the design decision to treat units as
  a **correctness** surface rather than a localisation surface, and to ship them
  now rather than behind an internationalisation layer. The reasoning is
  recorded in the project's decision log: translation can be retrofitted onto
  strings, but a comparison between a value in one unit and a threshold in
  another is a defect that a translation layer would never surface. See
  [No lang segment yet](../project/decisions/0005-no-lang-segment-yet.mdx).

  We do not ship conversion factors as clinical content. Conversions between
  molar and mass concentration depend on the molecular weight of the substance
  being measured, which means the factor is a property of the analyte rather
  than of the units. Publishing a table of factors here would invite someone to
  apply the glucose factor to cholesterol. The factor belongs to the clinical
  source a product works from, and what the interface owns is the labelling of
  the value once it has been converted.

  What would change our mind: we would revise rule 2, which fixes the preference
  per metric family, on a field dataset showing that readers hold different unit
  preferences on different devices, such as a clinic-supplied meter reporting in
  one system while the phone is set to another. The open question is where the
  preference should live, and the candidates are per user, per metric family, or
  per device. We have chosen per metric family on judgement alone.
</ResearchNote>

## Applying it [#applying-it]

<DoDont>
  <DoDont.Do>
    Render "5.4 mmol/L" with a visible, tappable unit control, and carry the
    reader's choice across the whole product.
  </DoDont.Do>

  <DoDont.Dont>
    Render "5.4" with the unit in a legend at the bottom of the screen, or only
    in the chart axis. A unit held there is absent from the value's accessible
    name, so a screen reader announces the figure without it.
  </DoDont.Dont>
</DoDont>

<DoDont>
  <DoDont.Do>
    Take "11 st 4 lb" as two labelled inputs, store it as kilograms, and display
    it back in the form it was entered.
  </DoDont.Do>

  <DoDont.Dont>
    Offer "11.3 st" in a single decimal field. A decimal stone field is read as
    stones and pounds by readers who think in that system, so a figure meant as
    eleven stone three pounds is stored as eleven point three stone, which is
    eleven stone four, and the record is one pound out.
  </DoDont.Dont>
</DoDont>

<DoDont>
  <DoDont.Do>
    State the provenance and the conversion together: "This reading came from
    your meter in mg/dL. Shown here as 5.4 mmol/L."
  </DoDont.Do>

  <DoDont.Dont>
    Convert on import and store the converted value. The original is now
    unrecoverable, and every later precision decision inherits the rounding.
  </DoDont.Dont>
</DoDont>

<DoDont>
  <DoDont.Do>
    Default from locale, then ask once in onboarding: "Which units do you use
    for weight?"
  </DoDont.Do>

  <DoDont.Dont>
    Infer the system from the device region and never ask. A UK phone does not
    indicate a kilogram reader, and a preference set by inference is never
    corrected, because nothing on screen tells the reader that a choice was
    made on their behalf.
  </DoDont.Dont>
</DoDont>

## Components that implement this [#components-that-implement-this]

{/* Generated from `implements`. Do not restate the list by hand. */}

`Value` and `ReadingInput` are specified as the only two places at which a unit
system is resolved, one for display and one for entry. Every other component
passes canonical values, which is what makes rule 5 a structural property of the
system rather than a rule each developer has to remember.

## What this does not cover [#what-this-does-not-cover]

* **Conversion factors and clinical correctness.** opsinjs ships no factors and
  no thresholds. The factor is determined by the analyte and is owned by the
  clinical source a product works from.
* **Translation of the surrounding language** is covered by
  [Internationalisation](../handbook/internationalisation.mdx).
* **Number formatting itself** is canonical at
  [Numbers, units and precision](./numbers-units-precision.mdx).
* **Dose calculation of any kind.** If your product calculates a dose, unit
  handling is a safety-critical function and this page is not sufficient.
* **Non-SI clinical units** are those that are neither metric nor imperial,
  such as international units and unit systems specific to a single assay.
  Handle them as their own metric with a fixed unit.

## Updates to this page [#updates-to-this-page]

<Reviewed />
