Skip to content

Number input component

components specs/components/number-input.kmd

A numeric field with increment/decrement steppers, min/max/step bounds, precision, and locale-aware formatting — the base for quantities, counts, durations, and dimensions. The generic sibling that money-input and phone-input specialize. Spectrum (number field / stepper) + Carbon (number input) parity.

When this spec applies

Primary triggers

All triggers

Specification body

Component — Number input

Status: v0.1.0 — Draft. Promoted from the Adobe Spectrum parity scan (meta/docs/stack #094); also the generic Carbon "number input". Source: https://spectrum.adobe.com/page/number-field/

R1 — Value, bounds, step

  • A numeric value with optional min, max, and step (default step 1). The field clamps to [min, max]; out-of-range entry shows an inline message (specs/errors/user-facing-messages.kmd) and clamps on blur — never silently.
  • Precision: integers vs decimals are explicit; the step and precision agree (a step of 0.1 implies one decimal place).

R2 — Steppers (spin buttons)

  • Increment / decrement controls (the +/- spin buttons) change the value by one step, holding at the bounds (disabled at min/max).
  • They are real buttons with accessible labels ("Increase quantity"); a press-and-hold MAY repeat with acceleration.

R3 — Keyboard & wheel

  • ↑/↓ increment/decrement by step; PageUp/PageDown by a larger stride (e.g. 10×); Home/End jump to min/max.
  • Mouse wheel adjusts only while the field is focused (never on incidental scroll-over).
  • Typing is allowed; reject non-numeric characters; inputmode="numeric" for integers, decimal when decimals are allowed.

R4 — Formatting & parsing

  • Display uses locale digit grouping/decimal separators (specs/i18n/contract.kmd); parsing accepts the locale separators + optional sign. Store a canonical number, not the formatted string (specs/data/field-types.kmd).
  • Empty ≠ zero (empty = unset).

R5 — When to use vs slider / money

  • Use a number input for precise/large-range numeric entry.
  • Use a slider (specs/components/sliders.kmd) for approximate values in a small bounded range; pair them when both precision and feel matter.
  • money-input (specs/components/money-input.kmd) specializes this for currency (per-currency precision + minor-unit storage); phone-input for phone numbers. Do not rebuild those here.

R6 — Accessibility & i18n

  • role="spinbutton" (or native) with aria-valuenow/min/max/text; the steppers and field share one accessible label.
  • The current value + bounds are announced; clamping is announced, not silent.
  • All formatting is locale-aware; the stored numeric value is locale-invariant.

Não-escopo

  • Currency (specs/components/money-input.kmd) and phone (specs/components/phone-input.kmd) specializations.
  • Range/approximate selection (specs/components/sliders.kmd).
  • Unit conversion / calculated fields (product logic).

Requirements (testable)

Requirement: Value bounds and clamping {#req-number-input-bounds}

The number input SHALL constrain its value to the [min, max] range: out-of-range entry SHALL show an inline message and clamp on blur, and SHALL NOT silently discard or alter the value without that visible feedback.

Scenario: Entry above max clamps on blur with a message

  • GIVEN a number input with min 0, max 10, step 1
  • WHEN the user types 15 and blurs the field
  • THEN an inline out-of-range message is shown
  • AND the committed value is clamped to 10
  • AND the clamp is not silent (the message is visible to the user)

Scenario: In-range entry is preserved

  • GIVEN a number input with min 0, max 10, step 1
  • WHEN the user types 7 and blurs the field
  • THEN the committed value is 7
  • AND no out-of-range message is shown

Scenario: Step and precision agree

  • GIVEN a number input with step 0.1
  • WHEN the field is rendered as a decimal field
  • THEN its precision allows exactly one decimal place
  • AND a value of 0.1 is accepted as valid

Requirement: Stepper buttons {#req-number-input-steppers}

The number input SHALL provide increment and decrement spin buttons that change the value by exactly one step, hold the value at the bounds, and SHALL expose them as real buttons with accessible labels.

Scenario: Increment changes value by one step

  • GIVEN a number input with value 4 and step 1
  • WHEN the user presses the increment (+) button
  • THEN the value becomes 5

Scenario: Decrement button disabled at min

  • GIVEN a number input with min 0, value 0, step 1
  • WHEN the field is rendered
  • THEN the decrement (-) button is disabled
  • AND pressing it does not change the value below 0

Scenario: Steppers are labelled buttons

  • GIVEN a number input labelled "Quantity"
  • WHEN the increment control is inspected
  • THEN it is a real button element
  • AND it exposes an accessible label such as "Increase quantity"

Requirement: Keyboard and wheel adjustment {#req-number-input-keyboard}

The number input SHALL adjust its value from the keyboard — Arrow Up/Down by one step, PageUp/PageDown by a larger stride, Home/End to min/max — and SHALL adjust on mouse wheel only while the field is focused.

Scenario: Arrow keys step the value

  • GIVEN a focused number input with value 5 and step 1
  • WHEN the user presses ArrowUp
  • THEN the value becomes 6
  • WHEN the user then presses ArrowDown
  • THEN the value returns to 5

Scenario: Home and End jump to bounds

  • GIVEN a focused number input with min 0, max 100, value 50
  • WHEN the user presses End
  • THEN the value becomes 100
  • WHEN the user then presses Home
  • THEN the value becomes 0

Scenario: Wheel adjusts only when focused

  • GIVEN a number input with value 5 that is not focused
  • WHEN the mouse wheel scrolls over the field
  • THEN the value remains 5
  • WHEN the field is focused and the wheel scrolls up
  • THEN the value changes by one step

Requirement: Locale formatting and canonical storage {#req-number-input-formatting}

The number input SHALL display values using locale digit grouping and decimal separators while storing a canonical locale-invariant number, and SHALL treat an empty field as unset rather than zero.

Scenario: Display uses locale separators, storage is canonical

  • GIVEN a number input in a locale that groups thousands and uses comma as decimal separator
  • WHEN the value 1234.5 is set
  • THEN the displayed text uses the locale grouping and decimal separator
  • AND the stored value remains the canonical number 1234.5

Scenario: Empty is unset, not zero

  • GIVEN a number input with no value entered
  • WHEN the field's stored value is read
  • THEN it is unset (empty)
  • AND it is not coerced to 0

Requirement: Accessibility and announcements {#req-number-input-a11y}

The number input SHALL expose spinbutton semantics with aria-valuenow, aria-valuemin, and aria-valuemax, share one accessible label between the field and its steppers, and SHALL announce clamping rather than performing it silently.

Scenario: Spinbutton ARIA reflects value and bounds

  • GIVEN a number input with min 0, max 10, value 3
  • WHEN its accessibility node is inspected
  • THEN role is "spinbutton" (or a native equivalent)
  • AND aria-valuenow is 3, aria-valuemin is 0, and aria-valuemax is 10

Scenario: Clamping is announced

  • GIVEN a number input with max 10 and an assistive technology observing it
  • WHEN the user enters 15 and the value is clamped to 10 on blur
  • THEN the clamp is announced to assistive technology
  • AND the announcement is not suppressed (not silent)

References