# Numbers

> Reading numbers as people type and paste them, and showing them as their locale and their work expect.

Numbers are entered by typing, pasting and stepping, and shown back in the form
people read. An interface that handles them well accepts what people naturally
type, and shows values as their language and their work expect.

## Best practices

**Show numbers in the reader's locale.** Digits, the decimal point and the
separators that group thousands follow the language of the interface: 1,234.5 in
English, 1.234,5 in German, ١٬٢٣٤٫٥ in Arabic.

**Read what people type, not only what the field would show.** Accept the
locale's separators where they group thousands; full-width digits, which an input
method produces; any minus sign, and parentheses around a negative amount; and a
separator from another locale where it can only mean one thing — `1,5` in an
English field is one and a half, because a separator before a single digit cannot
be grouping thousands.

**Never guess.** What is typed is read as what the person meant, or it is
invalid. `10 20`, `1,234,56`, a pasted range such as `10–20` and `5²` are not
numbers: joining the digits would keep a value nobody typed.

**Accept arithmetic.** In a field that takes a measurement or a quantity, `12*3`
or `(40-8)/2` is a faster way to say a value than working it out elsewhere. A
percent sign after a number divides it by a hundred, so `200*10%` is 20. Read
pasted data as a number only, without arithmetic: `2026-10-13` in a cell is a
date in the wrong column, not 2003.

**Give a unit, and accept others.** Show the unit people work in after the value,
and convert a value typed in another: `1in` in a millimetre field is 25.4 mm.
Measures written together, from the larger unit down, add up: `5'10"` is 70 in.

**Keep only meaningful precision.** Round a value to the digits that matter for
it — for money, the digits its currency has — with halves away from zero, and
never show the residue of binary arithmetic, such as 0.30000000000000004.

**Constrain rather than refuse.** A value beyond the allowed range becomes the
nearest value within it, and is shown as such, so people see what was kept.

**Let a cycle go round.** Where a range is a cycle — degrees, hours — a value
past one end is the value that far past the other: 370 in 0 to 360 is 10, and −90
is 270. The cycle holds its minimum and not its maximum, which is the minimum
again. This is reading, not limiting: the value kept is the value meant.

**Step predictably.** Up and Down Arrow change a value by one step; with Shift by
ten steps, with Option or Alt by a tenth of one; Page Up and Page Down by ten.
A step lands on a multiple of the step size, counted from the minimum: with a
step of 5, 7 steps up to 10 and down to 5. Typing is never moved to a multiple.
The step size is in what is shown, so a step of 1 moves 50% to 51%. Where a tenth
of a step is finer than the field keeps, it is a whole step.

## Reading

How an English field reads what is typed:

| Typed        | Read as | Because                                         |
| ------------ | ------- | ----------------------------------------------- |
| `1,234.5`    | 1234.5  | the locale's separators                         |
| `1,234`      | 1234    | a separator before three digits groups them     |
| `1,5`        | 1.5     | a separator before one digit is a decimal point |
| `0,125`      | 0.125   | nothing is grouped after a zero                 |
| `1.234,5`    | 1234.5  | with two kinds, the last is the decimal point   |
| `1,234,56`   | invalid | neither grouping nor one decimal point          |
| `１２３．５` | 123.5   | full-width, from an input method                |
| `ー５`       | −5      | what a Japanese keyboard types for a minus      |
| `−3`         | −3      | any minus sign                                  |
| `(1,234.50)` | −1234.5 | wholly in parentheses, as accounts write it     |
| `10–20`      | invalid | a dash between two numbers is a range           |
| `(40-8)/2`   | 16      | arithmetic                                      |
| `=12*3`      | 36      | a leading equals sign is ignored                |
| `200*10%`    | 20      | a percent sign divides by a hundred             |
| `1 1/2`      | 1.5     | a mixed number                                  |
| `10 20`      | invalid | two numbers                                     |
| `1e3`        | 1000    | scientific notation                             |
| `5²`         | invalid | a superscript is not a digit                    |
| `12pt`       | 4.23 mm | converted, in a millimetre field                |
| `5'10"`      | 70 in   | feet and inches, in a length field              |
| `50`         | 50%     | in a percentage field, the sign is optional     |
| `usd 5`      | $5.00   | in a dollar field, its own currency by any name |

## Units

A field that takes a measure holds its value in one unit and accepts any other
of the same kind, each converted by a fixed factor to the kind's base. Each unit
is shown by its symbol after a space that does not break, except where the
symbol is written against the number, as a degree is.

| Kind   | Unit   | Shown  | Is                      | Also typed as                |
| ------ | ------ | ------ | ----------------------- | ---------------------------- |
| Length | pixel  | `px`   | a CSS pixel, the base   | pixel, pixels                |
|        | point  | `pt`   | 1/72 in                 | point, points                |
|        | pica   | `pc`   | 1/6 in                  | pica, picas                  |
|        | inch   | `in`   | 96 px                   | inch, inches, `"`, `″`       |
|        | foot   | `ft`   | 12 in                   | foot, feet, `'`, `′`         |
|        | cm     | `cm`   | 1/2.54 in               | centimeter(s), centimetre(s) |
|        | mm     | `mm`   | 1/25.4 in               | millimeter(s), millimetre(s) |
|        | Q      | `Q`    | ¼ mm, for Japanese type | `級`, `歯`, h                |
| Angle  | degree | `°`    | the base                | deg, degree, degrees         |
|        | radian | `rad`  | 180/π°                  | radian, radians              |
|        | turn   | `turn` | 360°                    | turns                        |
|        | grad   | `grad` | 0.9°                    | gon, grads                   |

Names are matched without regard to case. A field can be given units of its
own kind in the same form: each a symbol, a factor to the base, other names, and
whether it is written against the number.

## Platform considerations

**Touch.** Offer a numeric keyboard.

**Assistive technology.** Expose the value as a number, with its unit in the
text read aloud, and the range when there is one.

See also [Number field](https://ui.publictheta.com/components/number-field.md).

## Behaviour

Each statement is verified by a test in Chromium, Chromium (touch), Firefox, WebKit, WebKit (touch), unless it names the environments it applies to or is marked open.

### Reading

- What is shown reads back as the same value, in every locale.
- An amount of money, a percentage and a measure read back as shown, in every locale.
- Full-width digits and signs, as an input method types them, read as numbers.
- Any minus sign makes a number negative.
- A typographic dash is a minus before a number, and not a number's sign between two.
- The long vowel mark and the hyphens a Japanese keyboard types are a minus beside a digit.
- An ideographic full stop or comma between digits is a point or a comma.
- The locale's group separators are read where they group thousands.
- Separators that neither group thousands nor mark one decimal point are not a number.
- A single separator that cannot group thousands is the decimal point, whatever the locale.
- An apostrophe groups thousands only before three digits.
- A space groups thousands only before three digits, in a locale that groups with one.
- Two numbers side by side, or apart by a tab, are not one number.
- A whole number, a space and a proper fraction are a mixed number.
- A number wholly in parentheses is negative, as accounts write it.
- Digits of other numbering systems read as numbers.
- Superscripts, subscripts, fractions and enclosed numerals are not digits.
- Scientific notation reads as the number it writes.
- Where the field has a unit written `e`, a number followed by `e` is in that unit.
- An equals sign before everything else is ignored.
- Arithmetic is worked out.
- Read as a literal only, arithmetic and what merely looks like it are not numbers.
- A value typed in another unit is converted to the field's.
- A converted value too small to write without an exponent is still a number.
- Measures written one after another, from the larger unit to the smaller, add up.
- A unit the field does not know is not a value.
- Where an apostrophe is a unit, it groups thousands only in a locale that groups with one.
- A currency field accepts its currency's code, symbols and name, in either case.
- A currency field does not accept another currency, nor any other field a currency.
- A percent sign after a number divides it by a hundred, in any field, and is never a remainder.
- In a percentage field, what is typed without a percent sign is in percent.
- Nothing typed is empty; anything else that is not a number is invalid.

### Units

- Lengths are in CSS pixels, 96 to the inch, and every length unit is what print and the screen take it to be.
- Angles are in degrees: a turn is 360, a radian 180 over π, a grad nine tenths of one.
- A unit is typed by any of its names, in any case, and by its symbol.
- Each unit is shown by its own symbol: after a space that does not break before most, against a degree.

### Showing

- A value is shown with the locale's digits and separators.
- A unit follows the value, attached where the unit is written so.
- A value shows no more fraction digits than its precision.
- An amount of money keeps the fraction digits its currency has, unless given a precision.

### Showing a percentage

- The precision is of the percentage as shown: one digit keeps 12.3%, none keeps 50%.
- A percent sign written before the number, as some languages write it, is read.

### Options that cannot be meant

- A precision out of range, a currency not named, or a locale that is not one is put right, not failed on.
- A step that is not a size is a step of one, and a range given backwards is the same range.
- Zero is shown as zero, whichever side it was approached from.

### Constraining and stepping

- A value is rounded to its precision, without floating-point residue.
- A half rounds away from zero, as the decimal number it is written as.
- Rounding leaves a value of any magnitude a number.
- A value is held within the range.
- A step adds the step size, as often as asked, and stays in range.
- A step from between two multiples of the step size goes to the nearest one that way.
- The multiples a step goes to are counted from the minimum, when there is one.
- A typed value is kept where it is, on a multiple of the step size or not.
- A tenth of a step moves by tenths of the step size.
- A tenth of a step that the precision cannot keep is a whole step instead.
- In a percentage, the step size is in percent as shown, and the range in the fraction kept.
- Several steps at once land where as many single steps would.

### A range that is a cycle

- A step past either end comes round from the other.
- A typed value beyond the range is read round the cycle, and is the value typed: nothing was limited.
- The maximum is the minimum again, and is shown as the minimum.
- Every value kept lies in the cycle, a whole number of turns from the one given.
- Without both ends there is no cycle: the range is held as any other.

## Reference implementation

React: `npx shadcn@latest add https://ui.publictheta.com/r/number-type.json`
