Number Field
Anatomy
Composite number input with decrement (−) and increment (+) buttons flanking a numeric input. Supports min/max constraints and configurable step increments.
maud_ui::primitives::number_field
Props
Generated from public Rust fields. Defaults are evaluated from the implementation.
| Prop | Type | Default | Description |
|---|---|---|---|
name | String | "" | HTML name attribute for form submission |
value | f64 | 0.0 | Current numeric value |
min | Option<f64> | None | Minimum value (optional constraint) |
max | Option<f64> | None | Maximum value (optional constraint) |
step | f64 | 1.0 | Increment/decrement step (default 1.0) |
disabled | bool | false | Whether the field is disabled |
label | String | "" | Accessibility label for the group |
id | String | "" | HTML id attribute for the input |
Examples
Cart — Wireless Headphones
Aurora Wireless Headphones
$149 each
Max 10 per order
Account — Age verification
You must be at least 13 to create an account.
Recipe — Tomato Basil Risotto
Adjust to scale ingredients (1–12 people).
Backordered — quantity locked
Ships when restocked on April 28.
Usage and composition
Import
use maud_ui::primitives::number_field::{self, Props};
Example
use maud::html;
use maud_ui::primitives::number_field;
html! {
div {
label for="cart-qty" { "Quantity" }
(number_field::render(number_field::Props {
name: "quantity".into(),
id: "cart-qty".into(),
value: 2.0,
min: Some(1.0),
max: Some(10.0),
step: 1.0,
disabled: false,
label: "Cart quantity".into(),
}))
}
}
Variants
| Variant | Configuration | Use Case |
|---|---|---|
| Simple counter | min: None, max: None | Unbounded counter (like a quantity setter). |
| Range-bounded | min: Some(X), max: Some(Y) | Constrained input (age, servings, file count). |
| Fixed maximum | min: Some(X), max: Some(X) | Read-only field; user cannot change. |
| Large step | step: 5.0 or 10.0 | Coarse adjustments (e.g., volume in 5% increments). |
| Decimal step | step: 0.1 | Precise adjustments (prices, measurements). |
| Disabled | disabled: true | Read-only or unavailable field. |
Helpers
None. The component renders a composite structure with an input and two buttons.
Design Notes
Button Behavior
The decrement (−) and increment (+) buttons are styled and positioned to flank the input field, creating a compact composite control. Buttons have tabindex="-1" so keyboard users interact with the input directly via arrow keys, not the buttons.
With the Nova profile and library JavaScript, a mouse press focuses the input and steps immediately. Holding the button repeats after a 400 ms delay, at 60 ms intervals. Touch and pen holds wait 50 ms to distinguish a hold from a quick tap or scroll; quick taps step once on release. Releasing, canceling, detaching or reinitializing the control stops the hold. The Standard profile retains its single step on click.
Step Precision
The step prop controls:
- Increment/decrement size for button clicks and arrow keys.
- HTML5 validation (via the numeric input’s
stepattribute). - Nova uses the pinned Base UI arithmetic and locale parser. It adds the step to the current numeric value without HTML step-grid snapping, then clamps to bounds and cleans floating-point arithmetic noise. Standard retains native HTML stepping.
Nova’s formatted display can round a tiny number to 0 or add grouping separators. Focusing and blurring without editing preserves the original numeric value, including digits not visible in the formatted text. Typing, clearing, grouping separators and supported Unicode numerals update the separate numeric form value. Paste inserts at the current selection and uses the pinned parser before changing the text. A paste that cannot be parsed leaves the selection and value untouched; accepted pasted text stays visible until blur normalizes it. The parser and formatter do not require React.
Form Submission
The numeric input retains name and supplies the unformatted value to FormData. Nova’s visible text input is unnamed, so it cannot submit a second formatted value. Programmatic input/change notifications bubble from the named numeric control so native form and htmx consumers receive the numeric value once; direct listeners on the visible editor also receive its editing notifications. Accepted native resets update the editor after the numeric control resets; canceled resets preserve the current value.
Nova keeps the visible editor and its consumer listeners during same-node history reinitialization. Serialized clones rebuild the formatted editor from the numeric value and recorded editing state. Switching profiles or destroying the controller restores the original numeric input and stops held-button timers. The no-JavaScript rendering remains the native numeric field.
Related
Input, Slider, Meter, Label.
Shadcn reference
https://ui.shadcn.com/docs/components/base/input
Accessibility
- Group role: The outer container uses
role="group"andaria-labelfor semantic grouping. - aria-disabled: Set on the group to communicate disabled state to screen readers.
- Button labels: Decrement and increment buttons have explicit
aria-label="Decrement"andaria-label="Increment". - Input constraints: The original
<input type="number">supplies native form values and validation. With Nova JavaScript, an associated text input handles formatted editing; the numeric control is visually hidden and excluded from the accessibility tree. - Keyboard support:
- Arrow keys (Up/Down) increment/decrement the value by
step. Nova also supports Alt for a 0.1 step, Shift for a 10 step, and Home/End for an explicitly supplied minimum/maximum. - Buttons are
tabindex="-1"(not in tab order); focus remains on the input.
- Arrow keys (Up/Down) increment/decrement the value by
- Screen readers: Field label, current value, and min/max constraints are all announced.