Number input

.number-input-group[data-ui="number-input"]

Number input — a quantity field, optionally with increment and decrement controls.

Examples

A number field

View “A number field” as
<input class="input number-input" type="number" min="1" max="99" step="1" />

font-variant-numeric: tabular-nums always, so a quantity that changes as you step it does not reflow.

A number too long for its box scrolls rather than truncating: an ellipsis on a figure produces something that reads as a different, smaller number.

With real buttons

View “With real buttons” as
<div class="number-input-group" data-ui="number-input">
  <button class="btn btn-icon" type="button" data-step="-1" aria-label="Decrease quantity">
    <svg class="icon" aria-hidden="true"><use href="#icon-minus"></use></svg>
  </button>
  <input class="input number-input" type="number" min="1" max="99" value="1" />
  <button class="btn btn-icon" type="button" data-step="1" aria-label="Increase quantity">
    <svg class="icon" aria-hidden="true"><use href="#icon-plus"></use></svg>
  </button>
</div>

Buttons that change a value have to actually change it, so they ship with their module — the same rule as a role that promises keyboard behaviour. Each has a real name; a bare "+" is not one.

Tokens 2

Level 2, declared on .number-input itself. Set any of them on that selector to restyle this component without touching the skin.

Level 2 tokens declared by number-input
TokenDefault
--number-input-step-min-size2.5rem
--number-input-text-aligncenter

Variants and states

Variants

None. It is one shape, and the page does the rest.

Inside it

  • .number-input-group
  • .btn

State it reads

Read from the platform, never mirrored into a class that could disagree with it.

  • :focus

Before you ship it

This one needs a script. Opt the markup in with data-ui="number-input" and import import 'mostlycss/js/components/number-input' — that specifier and not the package root, which is a bundle carrying a second copy of the registry, so a page holding both initialises every element twice. What you lose without the script is listed below — for some components that is only polish, for others it is the whole interaction.

What you have to do 4 requirements

  1. Give the input a real <label>, as always.
  2. The buttons ship with the module or not at all. A + button that does not add one is worse than no button: it is a control that announces itself and does nothing. So .number-input-group carries data-ui="number-input" and src/js/components/number-input.ts is loaded, or the markup is the bare input above with no buttons and no claim made. This is the same rule menu.css states for roles that promise keyboard behaviour, applied to a control that promises to act.
  3. Name each button on the button — "Increase quantity", not "+". The glyph is decorative and aria-hidden. Nothing in CSS can supply the name.
  4. Keep data-step on each button: -1 decrements, 1 increments, and any other integer multiplies the input's own step.

src/css/components/number-input.css · npx mostlycss add number-input