Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

NumberField

A small field for an exact number, with arrow-key steps, a stepper on hover and scrubbing by dragging its label.

import { NumberField } from "@butteryui/react";

Demo

When to use it

Use it for

  • Exact sizes and positions: width, height, x, y, a crop in pixels
  • Pairs of numbers in an inspector or a menu footer
  • A dense panel, with size="sm"; a field on a white card or in a toolbar, with variant="soft"

Not for

  • A value judged by eye: use Slider
  • Free text or long numbers such as phone numbers: use a text input

Examples

Crop size

A crop's width and height in pixels, side by side, each kept in state with the size it makes.

Crops to 1200 × 630 px

Export size

An export panel: width and height fields with their labels on top, each filling its column, and the button that uses them.

Export the slide

Not exported yet

Scale factor

A scale factor in tenths, from 0.1 to 4, saved once each change is final rather than on every keystroke.

No changes yet

Sizes and soft

A batch's weights in small soft fields for a dense panel, and its price in a large one beside large fields.

17% lost in the roast

Variants and states

Width and height

Type and press Enter, use the arrow keys (Shift for ten), hover for the stepper, or drag the W and H labels sideways.

Label on top

Each field fills its column under its label, as in a menu's width and height fields.

Decimals and disabled

Sizes, outline and soft

28, 34 and 48px tall, 16px digits at lg; soft sits on bg-control and shows its edge only on hover and while it has focus.

Props

NumberField

PropTypeDefaultDescription
label

required

stringWhat the number is, shown before the field. Drag it sideways to scrub the value.
valuenumberThe value. Pair with onValueChange to control it.
defaultValuenumberminThe starting value when uncontrolled. Defaults to min, or 0.
onValueChange((value: number) => void)Called with each committed value: on Enter, on leaving the field, on a step and while scrubbing.
onValueCommit((value: number) => void)Called once a change is final: on Enter, on leaving the field, on a step, and when a scrub ends, for an undo step per change rather than per scrubbed frame.
minnumberThe lowest value.
maxnumberThe highest value.
stepnumber1The smallest change. Arrow keys and the stepper move one step, Shift ten. Defaults to 1.
unitstringA unit shown after the number, such as "px".
labelPosition"side" | "top""side"Where the label sits: "side" (the default), before a 128px field, or "top", above a field that fills its column, as in a pair of width and height fields.
size"sm" | "md" | "lg""md"The field's height: "md" (34px, 30 compact, the default), "sm" (28px) for a dense panel, or "lg" (48px, 40 compact, with 16px digits) beside large fields.
variant"outline" | "soft""outline""outline" (the default), a 1px control-border edge on bg-surface, or "soft", a bg-control fill whose edge shows only on hover and while it has focus.

It also takes Omit<ComponentPropsWithRef<"input">, "value" | "defaultValue" | "onChange" | "type" | "min" | "max" | "step" | "size" | "children">.

Accessibility

A spinbutton named by its label: arrow keys step, Shift and an arrow steps ten, Enter or leaving commits what was typed, Escape puts back the last value. The stepper buttons skip the tab order, since the arrow keys do the same.

KeysWhat they do
Up or Down arrowRaises or lowers the value by one step
Shift and an arrowMoves it ten steps at a time
EnterCommits what was typed
EscapePuts back the last value, dropping what was typed
TabCommits what was typed and moves on, past the stepper's buttons

Motion

It doesn't move.

With reduced motion: Nothing changes.

Layout

With labelPosition="side", the default, the label sits before a 128px field; with "top" it sits above a field that fills its column. The field is 34px tall (--bt-h-control, 30 compact), 28px (--bt-h-sm) at size="sm" or 48px (--bt-h-cta, 40 compact) with 16px digits at "lg", and its number uses tabular figures; a long label truncates with an ellipsis and shows in full as its title. The stepper's 24px buttons show on hover and focus, and a field narrower than 112px drops them, keeping the arrow keys and the label's scrub.