Design System

Design System

Quantity

Forms

Open in Storybook: Quantity (opens in a new tab)

Overview

A compact control for a number the user steps through, like the items of a cart. It is one native input (type="number", role="spinbutton", inputmode="numeric") between two buttons that decrease and increase it.

The input holds the role, the name, the value and the range, so the buttons are a convenience and not tab stops.

Live example

0 to 10

Usage

  • label names the input and is always visible. min, max and step set the range, and the range is said before the user errs (0 to 10) and linked with aria-describedby.
  • valueText says the value in words on every change (3 in cart) through aria-valuetext. Never set aria-valuenow, aria-valuemin or aria-valuemax: aria-valuenow overrides the real value, 3 shown and 7 announced.
  • The value stays as typed. Out of range or empty it is invalid: aria-invalid="true" and an error with role="alert", rendered only while invalid, that says the fix ("Must be between 0 and 10"). An empty field is never turned into 0. Only the buttons and Home / End clamp.
  • At min or max the button that would go past it is disabled, dashed and muted. If a button disables itself under a click, focus moves to the input.
  • The buttons are 44 × 44, type="button", tabindex="-1", with aria-controls pointing at the input and a name by action ("Decrease quantity"). The icon is aria-hidden.
  • Focus is a 2px gap and a 3px accent ring on the input. Invalid is a 3px destructive edge plus an icon and text, never colour alone.

Accessibility

Keyboard interaction

  • Tab lands on the input and leaves it: the buttons are not tab stops. Tab commits the value.
  • ArrowUp and ArrowDown change the value by step. They are native, so they work in every browser and with a swipe on mobile.
  • Home and End set the minimum and the maximum, and Cmd + Left and Cmd + Right do the same on Mac keyboards without them. They are not native: the component adds them.
  • Typing 99 with max="10" leaves 99 and ArrowUp does not clamp it. The value is validated on change.

Screen reader

  • The input announces its own value (aria-valuetext) when it has focus. WebKit exposes a plain number input as a textbox, so the explicit role="spinbutton" stays.
  • A button press leaves focus on the button, so the input does not speak. The new value is announced with ariaNotify(), once, from the pressed button: "Increase quantity, 5 in cart". Where ariaNotify is missing nothing is said and nothing breaks.
  • At a limit focus moves to the input, which says its own value, so nothing is announced.
  • The calls are tested, not the speech: nothing here was heard in a screen reader yet.

Related

References

This section is still in progress.

On this page