Quantity Pickerv0.2.4Documentation Under Review
An accessible increment/decrement control built with CVA variants, four size options, danger state, and full keyboard support.
Prefer AI-assisted development?
Catalyst Quantity Picker is a compound component for selecting numeric values — increment, decrement, min/max constraints, danger thresholds, and four size variants included out of the box.
Installation
npm install @pmi/catalyst-quantity-pickerimport {
QuantityPickerRoot,
QuantityPickerTrigger,
QuantityPickerValue,
} from '@pmi/catalyst-quantity-picker';Sizes
Four sizes are available via the size prop on QuantityPickerRoot.
Common Patterns
Danger State
Use danger on QuantityPickerRoot to highlight critical quantities or warning thresholds — the state is propagated to the triggers and value via data-danger.
Color alone does not convey the danger state to screen reader users or users who cannot perceive red (WCAG 2.2 SC 1.4.1, 1.3.1). Always pair it with a visible HelperText message and, while the message is shown, point aria-describedby on QuantityPickerValue at its id. If the danger state can change at runtime, render the message inside an always-present role="status" container so it is announced when it appears.
Disabled State
Use aria-disabled on a trigger when the value has reached its boundary (see ARIA), or pass disabled to both triggers to block all interaction.
API Reference
Quantity Picker is a compound component. The sections below cover the props specific to each part.
QuantityPickerRoot
The flex container that provides the size context to all child parts via data-size.
Prop
Type
QuantityPickerTrigger
The increment or decrement button. Always provide a descriptive aria-label — the trigger renders an icon only.
Prop
Type
QuantityPickerValue
Displays the current numeric value between the two triggers. It renders an <input role="spinbutton"> by default; use asChild to render a custom element.
Prop
Type
Accessibility
Quantity Picker is designed to meet WCAG 2.2 AA standards.
Keyboard Interactions
| Key | Action |
|---|---|
Tab | Moves focus to the value input (the Default example removes the buttons from the Tab order) |
ArrowUp | Increases the value, up to max |
ArrowDown | Decreases the value, down to min |
Home / End | Sets the value to min or max |
Shift + Tab | Moves focus in reverse |
Enter | Activates the focused trigger button |
Space | Activates the focused trigger button |
ARIA
- Provide a visible label identifying what the quantity represents (WCAG 2.2 SC 3.3.2) and associate it with the picker via
role="group"+aria-labelledbyonQuantityPickerRoot. - Each
QuantityPickerTriggeris a<button>that contains an icon only — give it a contextual accessible name that includes the visible label (e.g. "Adults decrease quantity", "Adults increase quantity") (WCAG 2.2 SC 4.1.2). The examples usearia-labelledbycombining the labelidwith a visually hidden (sr-only) action text inside the button, so the name stays in sync with the visible label. - When a single trigger reaches its boundary, set
aria-disabled="true"on it rather thandisabled: the button keeps focus, stays in the Tab order, is announced as unavailable (WCAG 2.2 SC 4.1.2), andQuantityPickerTriggerignores its clicks. Usedisabledonly when both triggers must block all interaction. QuantityPickerValuerenders an<input role="spinbutton">by default, following the APG quantity spinbutton. Passvalue,min,maxandonValueChange: it setsaria-valuenow,aria-valueminandaria-valuemax, and setsaria-invalidwhile the value is outside the range. Associate the visible label with<label htmlFor>(WCAG 2.2 SC 4.1.2, 1.3.1).- Typing keeps digits only, an empty value resets to
minon blur, and Arrow Up/Down and Home/End are handled for you; each change is reported throughonValueChange. Keyboard changes on the focused input are announced natively througharia-valuenow. - When the typed value is outside the range, show a visible
HelperTextmessage inside an always-presentrole="status"container and, only while the message is shown, pointaria-describedbyon the input at it (WCAG 2.2 SC 3.3.1).dangeronQuantityPickerRootonly changes the styling; the visible message is what keeps the error from relying on color alone (SC 1.4.1). - Value changes made with the buttons are not announced by the spinbutton, because focus stays on the button. Mirror the new value into a visually hidden
<output>(an implicit polite status region) and empty it after about 2 seconds (WCAG 2.2 SC 4.1.3). - With
asChild, the value has no spinbutton semantics. Addaria-live="polite"to the child if its changes must be announced. - Trigger borders and the focus indicator meet the WCAG 2.2 AA minimum contrast of 3:1 for non-text elements (SC 1.4.11). Disabled triggers are exempt from this requirement.
- The danger state must not rely on color alone — pair it with a visible message (see Danger State) (SC 1.4.1).
Choosing the Right Component
Use Quantity Picker when users need to select a bounded integer value one step at a time. Reach for a different primitive when:
| If you need… | Use instead |
|---|---|
| A continuous range with a draggable handle | Slider |
| Free-form numeric text entry | Text Field |
| A multi-step workflow progress control | Progress Stepper |
Related
Map
Interactive map component for visualizing geographic data with markers, popovers, and zoom controls. Built on react-simple-maps with customizable geographies and Catalyst design tokens.
Radio Cards
A card-style radio group component for presenting mutually exclusive options as visually rich selectable cards with title, optional label, and size variants.