Number Field
Edit a bounded number directly or with increment and decrement controls.
Try it
<hui-number-field id="playground-number-field" name="quantity" min="0" max="10" step="1" value="3">
<hui-number-field-decrement aria-label="Decrease quantity">-</hui-number-field-decrement>
<hui-number-field-input aria-label="Quantity"></hui-number-field-input>
<hui-number-field-increment aria-label="Increase quantity">+</hui-number-field-increment>
</hui-number-field>- data-state
- Waiting for enhancement
No component events yet.
Usage
Use hui-number-field for bounded numeric values that benefit from direct editing and step buttons. Use a slider when users choose from a continuous range visually. Do not use it for formatted currency or arbitrary text without a numeric constraint model.
Anatomy
- Root:
hui-number-field - Input:
hui-number-field-inputwith a nativeinput[type="number"] - Increment/decrement controls:
hui-number-field-increment,hui-number-field-decrement - State hooks:
data-state,data-invalid, anddata-disabled
Examples
<section aria-labelledby="number-field-heading">
<h2 id="number-field-heading">Quantity</h2>
<form id="number-field-form">
<hui-number-field id="quantity-field" name="quantity" min="0" max="10" step="1" value="3" aria-labelledby="number-field-heading">
<hui-number-field-decrement aria-label="Decrease quantity">−</hui-number-field-decrement>
<hui-number-field-input><b>Quantity</b></hui-number-field-input>
<hui-number-field-increment aria-label="Increase quantity">+</hui-number-field-increment>
</hui-number-field>
</form>
</section>API reference
| Attribute | Meaning |
|---|---|
value | Canonical committed numeric value. Values are clamped and rounded to the step grid. |
default-value | Seeds value once when the component is enhanced if value is absent. |
min / max | Numeric bounds; defaults are 0 and 100. A reversed range is repaired. |
step | Positive step used for normalization and increment/decrement; defaults to 1. |
disabled | Disables the input and both buttons and removes the input from the tab order. |
readonly | Keeps the input focusable but prevents editing and increment/decrement actions. |
allow-empty | Allows an empty committed value; defaults to true. Set allow-empty="false" to restore the minimum on blur. |
name / form / required | Native form metadata copied to the inner input. |
placeholder | Native input placeholder shown when the field is empty. |
aria-label / aria-labelledby / aria-describedby | Accessible naming inherited by the inner spinbutton. |
value-text | Optional string with {value} replacement or valueText formatter property. |
The root exposes data-state="empty|valid", data-invalid while the user has an invalid transient edit, and data-disabled when disabled. It emits hui-value-change with { value: number | null } whenever the committed value changes. The value attribute is the source of truth and is serialized as a plain decimal string without unnecessary trailing zeroes.
The inner input owns the successful form value, so no hidden duplicate control is necessary. Its value property and value attribute are both synchronized for SSR and no-JavaScript form submission.
States and styling
| State | Contract and styling guidance |
|---|---|
| Default | Valid numeric value or an allowed empty input with step buttons available. |
| Hover | Use --hui-color-interactive-hover for enabled step-button feedback. |
| Active | Use --hui-color-interactive-active for pressed step-button feedback. |
| Focus | Keep a visible input or button ring with --hui-color-focus-ring. |
| Disabled | Disable the input and buttons and use --hui-color-disabled-background and --hui-color-disabled. |
| Error | Transient invalid edits expose data-invalid; pair --hui-color-danger with an accessible error message. |
Attributes are the source of truth. Style derived state with data-state, ARIA attributes, and the --hui-* token aliases.
Accessibility and keyboard support
ArrowUpincrements bystep;ArrowDowndecrements bystep.Homecommitsmin;Endcommitsmax.Entercommits a valid edit and restores the canonical value for an invalid edit.- Blur commits a valid edit and restores the last committed value for an invalid edit. An empty edit remains empty when
allow-emptyis true (the default); whenallow-empty="false"it becomesmin. - Increment and decrement buttons commit one step while retaining focus on the clicked button.
- Buttons are disabled at the corresponding bound and while the root is
disabledorreadonly. - Modifier-key shortcuts are ignored. Wheel events do not change the value accidentally.
- Disabled controls ignore all keyboard, pointer, and input interaction. Readonly controls remain focusable but do not accept edits or step actions.
SSR and forms
- SSR emits a deterministic, clamped value when
valueordefault-valueis present and emits an empty native input otherwise when empty values are allowed. role="spinbutton",aria-valuemin,aria-valuemax,aria-valuenow(when non-empty),aria-valuetext,aria-disabled,aria-readonly, andaria-requiredare resolved attributes, not only IDL state.- Existing projected nodes and consumer IDs are preserved through adoption. Internal IDs derive from the root ID when present; id-less fields do not create ARIA references.
- SSR never installs input, keyboard, pointer, or blur listeners. No hydro markers or event attributes leak into serialized output.
default-valueanddefault-*metadata are initialization seeds; after enhancement the canonicalvalueattribute is authoritative.
Code
<form id="quantity-form">
<hui-number-field
id="quantity"
name="quantity"
min="0"
max="10"
step="1"
value="3"
>
<hui-number-field-decrement
aria-label="Decrease quantity"
></hui-number-field-decrement>
<hui-number-field-input aria-label="Quantity"></hui-number-field-input>
<hui-number-field-increment
aria-label="Increase quantity"
></hui-number-field-increment>
</hui-number-field>
</form>
The runtime shape is hui-number-field[data-hydro-root] > span > template tree, with projected part hosts retained in slots. The input part contains a native number input and the buttons contain native buttons. SSR writes the resolved spinbutton ARIA attributes, bounds, value, state, and native form attributes. Without JavaScript the native input remains editable and submits through the form, while custom buttons do not increment.
<hui-number-field id="playground-number-field" name="quantity" min="0" max="10" step="1" value="3">
<hui-number-field-decrement aria-label="Decrease quantity">-</hui-number-field-decrement>
<hui-number-field-input aria-label="Quantity"></hui-number-field-input>
<hui-number-field-increment aria-label="Increase quantity">+</hui-number-field-increment>
</hui-number-field>