Number Field
Number fields collect numeric input with increment/decrement steppers, locale formatting, and min/max constraints.
Try it
Usage
Set minValue, maxValue, and step to constrain input. Compose with RuiNumberFieldGroup and stepper buttons for the full pattern.
import {
RuiNumberField,
RuiNumberFieldGroup,
RuiNumberFieldInput,
RuiNumberFieldDecrementButton,
RuiNumberFieldIncrementButton,
RuiNumberFieldSteppers,
} from '@ecopages/radiant-ui/number-field';
<RuiNumberField value={3} minValue={0} maxValue={10} step={1}>
<RuiNumberFieldGroup>
<RuiNumberFieldInput />
<RuiNumberFieldSteppers>
<RuiNumberFieldDecrementButton />
<RuiNumberFieldIncrementButton />
</RuiNumberFieldSteppers>
</RuiNumberFieldGroup>
</RuiNumberField>Custom markup
<rui-number-field> coordinates any light-DOM tree that matches its query contract. The RuiNumberField helpers stamp these targets; they are not required.
import '@ecopages/radiant-ui/number-field';
<rui-number-field name="quantity" value={3} min-value={0} max-value={10}>
<div class="rui-number-field__group" data-number-field-group>
<input type="text" data-number-field-input data-rui-control data-rui-control-type="number" class="rui-number-field__input" />
<button type="button" data-number-field-action="decrement" class="rui-number-field__stepper">−</button>
<button type="button" data-number-field-action="increment" class="rui-number-field__stepper">+</button>
</div>
</rui-number-field>BEM classes are presentation-only. The host owns formatted input value and spinbutton aria-*. Set name on the host to submit the raw number with a native form (not the formatted display string).
Step and commit behavior
commitBehavior controls whether out-of-range typed values snap to bounds or reject on blur.
Disable scroll wheel
Set wheelDisabled to prevent accidental value changes when users scroll over a focused number field.
Theming
Number field surfaces map to semantic surface roles, never Tailwind palette steps:
| Part | CSS roles |
|---|---|
Group (.rui-number-field__group) | border, background, rounded-control |
Input (.rui-number-field__input) | on-background text |
Steppers (.rui-number-field__steppers) | border divider |
Stepper (.rui-number-field__stepper) | on-surface, surface hover, surface-container-low active |
| Focus / invalid | focus-ring / error |
Geometry shares control tokens (--size-control-sm, --space-control-*, --radius-control, --text-control) with RuiInput and RuiButton so form rows align. Override at the theme layer, not in component CSS.
API
RuiNumberField is a custom element (<rui-number-field>). Standalone it renders the default input + stepper row; compose the view helpers for full control.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
value | number | Current value. | |
default-value | number | Initial value when uncontrolled. | |
min-value | number | -Infinity | Lower bound; steppers and arrow keys snap to it. |
max-value | number | Infinity | Upper bound; steppers and arrow keys snap to it. |
step | number | 1 | Increment/decrement and snap interval. |
disabled | boolean | false | Disables input and steppers. |
read-only | boolean | false | Blocks editing; the value still displays formatted. |
label | string | '' | Accessible name when there is no visible RuiLabel. |
name | string | '' | Form field name on this host. Submits the raw number, not the formatted display. |
locale | string | '' | BCP 47 locale tag, or comma-separated fallback list. |
format-options | string | '' | JSON-serialized Intl.NumberFormatOptions. |
commit-behavior | snap · validate | snap | Blur behavior after editing. |
increment-aria-label | string | Increment | Accessible name for the increment stepper. |
decrement-aria-label | string | Decrement | Accessible name for the decrement stepper. |
wheel-disabled | boolean | false | Disables scroll-wheel value changes. |
Light-DOM contract
| Target | Required | Host writes | Author owns |
|---|---|---|---|
[data-number-field-input] | yes | id, role, inputmode, data-disabled, data-readonly, aria-valuemin, aria-valuemax, aria-valuenow, formatted value | text input; stamp data-rui-control for field wiring |
[data-number-field-action="decrement"] | no | disabled, aria-label | decrement button |
[data-number-field-action="increment"] | no | disabled, aria-label | increment button |
[data-number-field-group] | no | — | presentation wrapper only |
Nested hosts: none.
Events
| Event | Detail | Description |
|---|---|---|
rui-change | { value: number } | Emitted when a value is committed (blur, stepper, or keyboard). |
View helpers
| Component | Target stamped | Notes |
|---|---|---|
RuiNumberField | default group when children omitted | Includes input and steppers. |
RuiNumberFieldGroup | [data-number-field-group] | Presentation only; not queried. |
RuiNumberFieldInput | [data-number-field-input] | Always stamps data-rui-control. |
RuiNumberFieldIncrementButton | [data-number-field-action="increment"] | Default label "Increment". |
RuiNumberFieldDecrementButton | [data-number-field-action="decrement"] | Default label "Decrement". |
RuiNumberFieldSteppers | stepper column wrapper | Presentation only. |
CSS classes
Public BEM classes (documented via @cssclass):
| Class | Description |
|---|---|
.rui-number-field | Root field wrapper. |
.rui-number-field__group | Control-height bordered row. |
.rui-number-field__input | Borderless text input. |
.rui-number-field__steppers | Trailing stepper column. |
.rui-number-field__stepper | Icon button cell (+ / −). |
Theme roles
| Part | CSS variables consumed |
|---|---|
| Group | --border, --background, --radius-control, --duration-normal |
| Input | --on-background, --space-control-x, --space-control-y, --text-control |
| Steppers | --size-control-sm |
| Stepper states | --on-surface, --surface, --surface-container-low |
| Focus / invalid | --focus-ring / --error |
Accessibility
- Stepper buttons need accessible names; "Increment" and "Decrement" are provided by default.
- Announce min and max constraints in helper text when the valid range is not obvious.
- Disabled fields should explain why editing is unavailable.