Number
A native number input with visible steppers, rendered as
<input type="number" is="root-number">.
Usage
<input type="number" is="root-number" data-label="Quantity" min="0" max="10" step="1" value="1" />
The component wraps the input and injects decrement/increment buttons wired to the native
stepUp/stepDown, honouring min/max/step/disabled/
readonly
and disabling a stepper at its bound. The input stays a native spinbutton - value, keyboard (arrow keys)
and form participation are untouched; the native browser spin buttons are hidden in favour of the
component's.
Both buttons sit in the row's own tab order, after the field: tabbing in reaches the value, then
-, then +. - is still drawn to the left of the value - it is put
back there with order, which is the one place this composition lets the visual order and the
DOM order disagree, against a positive tabindex on all three. Arrow keys still step the value
with the input alone focused - the buttons are there for a keyboard user who reaches for them directly,
not a replacement for it.
The box is Input's box: the same 2px Neutral 400 frame on the page's own ground, the same
xl radius, the same 40px height and the same inline padding, from the same
User Input/Fields/* variables. It used to be its own thing - a 1px Neutral 200 frame around a
Neutral 100 fill at the 2xs radius, 44px tall, with the two steppers on a band of their own -
so a quantity field and a text field in one form read as two different systems. The steppers sit on the
field's ground inside that frame now, which is what makes the box read as one control with two buttons in
it rather than three boxes in a row.
Sizes
data-size
takes small and large; the default is the medium step and has no value to write.
Input's steps, value for value - 36 / 40 / 44px, with the corner stepping up at large - so the two line up
wherever they share a row.
<input type="number" is="root-number" data-size="large" data-label="Large" value="1" />
<input type="number" is="root-number" data-label="Medium" value="1" />
<input type="number" is="root-number" data-size="small" data-label="Small" value="1" />
Bounds
A stepper disables itself once the value reaches its bound, so the pair says what the range is without a message. The value stays reachable by typing and by the arrow keys, which is the native spinbutton doing its own job.
<input type="number" is="root-number" data-label="At the minimum" min="0" max="3" value="0" />
<input type="number" is="root-number" data-label="At the maximum" min="0" max="3" value="3" />
Hint and error
data-hint
holds the description and data-error the error message: both are authored once, and
data-invalid decides which one shows. With no data-error
the browser's own validationMessage stands in. One node carries both, wired to the control
through aria-describedby.
<input type="number" is="root-number" data-label="Guests" data-hint="Up to four per booking" min="1" max="4" value="2" />
<input type="number" is="root-number" data-label="Guests" data-error="Four at most" data-invalid="true" min="1" max="4" value="9" />
Required
required
is the native attribute; the asterisk beside the label is drawn from it and hidden from assistive tech,
which reads the control's own required instead.
<input type="number" is="root-number" required data-label="Guests" min="1" max="4" />
States
Hover moves the border one step deeper and focus takes the Default step, exactly as on Input - the border
moving is the indicator, so focus draws no ring. disabled
repaints the box, takes the not-allowed cursor and stills the steppers; readonly
carries no styling of its own, the value being selectable and the field still focusable.
<input type="number" is="root-number" data-label="Tab to me" value="2" min="1" max="4" />
<input type="number" is="root-number" data-label="Disabled" value="3" disabled />
<input type="number" is="root-number" data-label="Read-only" value="3" readonly />
Inline layout
inline
on the <label> lays the label text and the field on one row instead of stacking them.
It has to sit on the label, which means authoring the label yourself - the generated one takes no
attributes from the control.
<label inline>
<span data-part="label">Guests</span>
<span field-wrapper>
<input type="number" is="root-number" data-size="small" value="2" min="1" max="4" />
</span>
</label>
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
min / max |
number |
- | Bounds; a stepper disables itself at its own |
step |
number |
1 |
Step increment |
data-size |
'small' | 'large' |
medium | Field height, radius and label scale |
data-label |
string |
- | Label text; the component builds the <label> |
required |
boolean |
false |
Marks the field required and draws the asterisk |
data-hint |
string |
- | Description shown under the field |
data-error |
string |
- | Error message; falls back to validationMessage |
data-invalid |
'true' |
- | Shows the error instead of the description |
disabled |
boolean |
false |
Disables the field and both steppers |
readonly |
boolean |
false |
Read-only state; the steppers do nothing |
Methods
| Method | Description |
|---|---|
increment() / decrement() |
Step the value and re-evaluate the stepper bounds |
updateButtons() |
Disable a stepper at its bound |
Events
Native input / change fire from the underlying <input>.
Extends
HTMLInputElement
- use with <input type="number" is="root-number">.