Components
Number input
A numeric field with −/+ stepper buttons. The buttons share borders with the centered input like an input group; they step by step, clamp to min/max, and disable themselves at the bounds.
Anatomy
A .ds-number-input group joins a .ds-number-input__step minus, a centered .ds-number-input__field, and a plus — collapsing the shared borders so it reads as one unit.
<div class="ds-number-input">
<button class="ds-number-input__step" type="button" aria-label="Decrement" tabindex="-1">−</button>
<input class="ds-number-input__field" type="number" value="3" aria-label="Quantity">
<button class="ds-number-input__step" type="button" aria-label="Increment" tabindex="-1">+</button>
</div>
Interactive
The − / + buttons step the value and clamp it to the range (0–10 here), disabling at the bounds. The small inline script below wires this demo (no dependencies).
<div class="ds-number-input" data-number-input data-min="0" data-max="10" data-step="1">
<button class="ds-number-input__step" type="button" aria-label="Decrement" tabindex="-1" data-number-dec>−</button>
<input class="ds-number-input__field" type="number" value="0" min="0" max="10" step="1" data-number-field>
<button class="ds-number-input__step" type="button" aria-label="Increment" tabindex="-1" data-number-inc>+</button>
</div>
States
A button disables at its bound; the whole control can be disabled too.
<div class="ds-number-input">
<button class="ds-number-input__step" type="button" aria-label="Decrement" tabindex="-1" disabled>−</button>
<input class="ds-number-input__field" type="number" value="0">
<button class="ds-number-input__step" type="button" aria-label="Increment" tabindex="-1">+</button>
</div>
React
The NumberInput steps the value clamped to min/max by step, disabling each button at its bound. Controlled (value) or uncontrolled (defaultValue).
import { NumberInput } from "@diametral/design-system/react";
<NumberInput
defaultValue={1}
min={0}
max={10}
step={1}
onChange={(value) => setQty(value)}
/>