NumberField
A numeric field pairing a native <input type="number"> control with a decrement and an increment button, all three sharing one bordered, rounded frame.
Hold a step button to run through the range.
Steps by 0.50, as the input's own step says.
View code
let seats = "3";
<div mix={[vstack({ gap: 5, align: "stretch" })]}>
<NumberField>
<Label htmlFor="seats">Seats</Label>
<NumberField.Group mix={[stepper()]}>
<NumberField.DecrementButton
aria-label="One seat fewer"
command={NUMBER_FIELD_STEP_DOWN_COMMAND}
commandfor="seats"
/>
<NumberField.Input
id="seats"
name="seats"
min={1}
max={50}
step={1}
value={seats}
aria-describedby="seats-hint"
mix={[
on<HTMLInputElement, "input">("input", (event) => {
seats = event.currentTarget.value;
void handle.update();
}),
]}
/>
<NumberField.IncrementButton
aria-label="One seat more"
command={NUMBER_FIELD_STEP_UP_COMMAND}
commandfor="seats"
/>
</NumberField.Group>
<Description id="seats-hint">Hold a step button to run through the range.</Description>
</NumberField>
<NumberField>
<Label htmlFor="budget">Monthly budget</Label>
<NumberField.Group mix={[stepper({ holdIntervalMs: 40 })]}>
<NumberField.DecrementButton
aria-label="Lower the budget"
command={NUMBER_FIELD_STEP_DOWN_COMMAND}
commandfor="budget"
/>
<NumberField.Input
id="budget"
name="budget"
min={0}
max={10000}
step={0.5}
defaultValue={250}
/>
<NumberField.IncrementButton
aria-label="Raise the budget"
command={NUMBER_FIELD_STEP_UP_COMMAND}
commandfor="budget"
/>
</NumberField.Group>
<Description id="budget-hint">Steps by 0.50, as the input's own step says.</Description>
</NumberField>
<div mix={[hstack({ gap: 2, justify: "between" })]}>
<span mix={[text("sm"), weight("medium")]}>Subtotal</span>
<span mix={[text("sm")]}>{subtotal}</span>
</div>
</div>Installation
An ordinary dependency: install it, import the theme once, and import the component where it is used.
npm add @sdxc/uiimport "@sdxc/ui/theme.css";Usage
import { NumberField } from "@sdxc/ui";Renders a plain host stacking this field's compound parts in a single column with a small gap between them: typically a caption, the NumberField.Group row, and any supporting or validation copy.
Composition
The parts the component publishes, each a static property of the host.
NumberFieldNumberField.GroupRenders NumberField's frame: arole="group"<div>drawing a bordered, rounded, tinted-background box that reads as one seamless control, ringing the whole frame via:focus-withinwhen a child gains focus.NumberField.InputRenders NumberField's control: a native<input type="number">built on Input.NumberField.DecrementButtonRenders a native<button>decrementing NumberField.Input's value; its glyph isaria-hidden, so callers must supply anaria-label.NumberField.IncrementButtonRenders a native<button>incrementing NumberField.Input's value; its glyph isaria-hidden, so callers must supply anaria-label.
Props
Read from the component's own types, so every prop, every default and every allowed value is listed.
Also accepts everything in TagProps<"div">.
NumberField.Group
Also accepts everything in TagProps<"div">.
NumberField.Input
Also accepts everything in Omit<Input.Props, "type" | "role" | "color">.
NumberField.DecrementButton
Also accepts everything in Omit<TagProps<"button">, "children">.
NumberField.IncrementButton
Also accepts everything in Omit<TagProps<"button">, "children">.