Units and numeric entry
Accepting a number that has a unit attached. The unit never sits inside the field, the value is never ambiguous, and a surprising reading is never rejected.
When to use
Use this whenever the answer to a question is a quantity: a weight, a height, a temperature, a glucose reading, a dose, a duration.
The distinction from the rest of this group is that a quantity is two facts in
one field, the magnitude and the unit. Almost every serious numeric bug in a
health product comes from the second one going missing. A reader who enters
96 has told you nothing until you know whether they meant kilograms, pounds,
or a fasting glucose in mg/dL.
This page is about entry. Display, rounding, significant figures and the choice of unit for a given measure are owned by Numbers, units and precision and Unit systems, and are not restated here.
When not to use
- The number has no unit. A count of items, a score out of ten, a questionnaire item. Use an ordinary numeric field; the unit machinery adds confusion where there is nothing to confuse.
- The reader is choosing, not typing. A dose selected from a list of the reader's own prescriptions is a selection problem. See Medications.
- You want the formatting rules. Rounding, precision and how to render a value are canonical in Numbers, units and precision.
How it works
flowchart TD
A["A quantity to collect"] --> B{"Is there more than one unit in use?"}
B -->|"no"| C["Show the single unit beside the field, never in it"]
B -->|"yes"| D["Unit is an explicit control, defaulted from the reader's preference"]
D --> E{"Is the unit compound?"}
E -->|"yes"| F["Separate fields with one legend: feet and inches, stones and pounds"]
E -->|"no"| G["One field, one unit control"]
C --> H["Accept the value"]
F --> H
G --> H
H --> I{"Impossible, or merely surprising?"}
I -->|"impossible"| J["Reject with a message naming the range"]
I -->|"surprising"| K["Confirm, do not block; store whichever the reader confirms"]The rules:
- The unit is never typed into the value field. Parsing "72kg", "72 kg", "72,5 kg" and "158lb" is a source of bugs, and the reader should not have to guess what your parser accepts. Display it, or make it a control.
- The unit is always visible, including after entry and in every subsequent display of the value. A number without a unit on screen is an incomplete fact.
- The unit choice is sticky and explicit. It is remembered per measure, not globally: a reader may well think in stones for weight and centimetres for height, and forcing one system on both is a British and American problem in equal measure. See Unit systems.
- Changing the unit converts the entered value, visibly. Do not reinterpret the digits. If the reader typed 12 st and switches to kg, they see 76.2 kg rather than 12 kg.
- Compound units get separate fields under one legend. Feet and inches; stones and pounds; hours and minutes. One field with a slash in it is a parsing problem wearing a costume.
- Decimal separators are accepted in both forms. A reader with a European locale keyboard will type a comma, and rejecting it is a locale bug presented as a validation error.
- Impossible is rejected; surprising is confirmed. A negative height is impossible. A glucose reading of 27 mmol/L is surprising and may be a genuine emergency. Asking is right and blocking is dangerous. See Validation timing.
- The mg/dL versus mmol/L trap deserves an explicit check. A value plausible in one and implausible in the other should prompt "did you mean…?" with both options, rather than either silently accepting or rejecting.
- Store canonically, display in the reader's unit. One unit in the database, the reader's preference at the edge. Storing whatever was typed guarantees a future migration and a mixed-unit trend chart.
States
| State | Behaviour |
|---|---|
| Empty | Placeholder is not the label; the unit is still visible |
| Partial compound | Feet entered, inches empty: treat as incomplete, not as zero inches |
| Out of physical range | Rejected with the accepted range named in the message |
| Surprising | Confirmed inline, entry preserved, never cleared |
| Unit ambiguous | Explicit "did you mean" with both interpretations shown converted |
| Converted | Both the entered value and the stored unit are visible on review |
Content
Do
Label "Weight", a numeric field, and a unit control offering kg, lb and st + lb, defaulted to the reader's last choice. The unit reads as a word in the accessible name: "Weight in kilograms".
Don’t
A single field labelled "Weight (kg/lbs)" that tries to parse whatever arrives, and stores a bare number.
Do
"That's higher than most glucose readings in mmol/L. Did you mean 138 mg/dL (7.7 mmol/L)?" with both options offered and neither pre-selected.
Don’t
Silently converting 138 to 7.7 because it "must" have been mg/dL, so the reader's record now contains a number they never entered.
Accessibility
- The unit is part of the accessible name, spelled out: "Weight in
kilograms", not "Weight kg". Symbols like
µg/LandmmHgare not reliably spoken. - A compound entry is a
fieldsetwith alegend, so each part is announced with the whole: "Height, feet" and "Height, inches". - The unit control is a real control, either a select or a radio group. It is labelled and reachable by keyboard, not a tap target styled as a suffix.
- Changing the unit announces the converted value as a polite status message (WCAG 2.2 SC 4.1.3), because the number on screen has just changed without the reader typing. The component supplies the sentence; the product owns the live region and makes the announcement, because no opsinjs component mounts one on a caller's behalf. See Screen readers.
- Never rely on a placeholder to carry the unit. It disappears on entry, frequently fails contrast, and is not a label. That is a failure of WCAG 2.2 SC 3.3.2.
- Steppers are additive. Typing is always available; see Autocomplete and input types.
- Do not truncate or auto-format while typing. Reformatting a number as the reader types moves the caret and destroys the entry for anyone not watching closely.
Research
Updates to this page
Last read through against the system on 2026-09-20. Due for review every 6 months; expiry is reported by pnpm run check:freshness.
Autocomplete and input types
The attributes that decide which keyboard appears and whether the browser can fill a field, including the health fields for which no autofill token exists.
Ask users for…
Patterns keyed by the data being requested rather than the screen. Every health product asks for the same fifteen things and gets the same eight of them wrong.