Number stepper
A number stepper sets a small whole number with − and + buttons, such as a quantity, guests, or tickets.
<tk-number-stepper label="Adults" unit="adults" value="2" min="1" max="8"></tk-number-stepper>
Anatomy
| Part | What it is |
|---|---|
tk-number-stepper |
the element; one Tab stop |
field part |
the bordered row |
decrement part |
the − button; press and hold to repeat |
value part |
the number in the middle; it is the spinbutton and takes focus |
increment part |
the + button; press and hold to repeat |
The stepper is only as wide as its content. It shows no label of its own.
Label
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 280px"> <span>Guests</span> <tk-number-stepper label="Guests" unit="guests" value="3" min="1" max="10"></tk-number-stepper> </div>
- Put a visible label beside the stepper and set
labelto the same words.labelis the name screen readers hear; the stepper doesn't draw it. unitadds a word to what screen readers hear, "3 guests"; the visible value stays "3".- Don't wrap a stepper in a
Field . It names itself.
Value
valueis a whole number (default 0) and submits undername.mindefaults to 0. Setmin="1"for "at least one".maxhas no default; set it when there's a real cap, or + never turns off.stepdefaults to 1 and must be a whole number. The value always stays on the step grid and inside the range.- Every press is a change: it fires
inputandchange. Holding a button fires one pair per repeat.
Sizes
<tk-number-stepper label="Medium stepper" value="2"></tk-number-stepper> <tk-number-stepper label="Large stepper" size="large" value="2"></tk-number-stepper>
size |
Height and button width | Value text | Icon |
|---|---|---|---|
medium (default) |
32px | Body Small (14/20) | icon-size-16 |
large |
36px | Body Medium (16/24) | icon-size-16 |
Both sizes meet the 24px minimum target size. There is no small; size="small" renders
as medium and logs a console warning.
States
<tk-number-stepper label="Tickets at minimum" value="1" min="1" max="4"></tk-number-stepper> <tk-number-stepper label="Tickets" value="2" disabled></tk-number-stepper>
The row doesn't change on hover; on focus its border takes the focus color and grows thicker inward. The − and + tint under the pointer.
- At
minormax, the matching button is disabled. disabled: the whole stepper can't be focused or changed, and it's left out of the form.
The stepper never shows an error: the value can't leave the range.
Remove at minimum, or Add first
Two cart patterns are yours to build around the stepper:
- Remove at minimum: set
min="1"and put a trashIcon button beside the stepper. - Add first: show an "Add"
Button until the item is added, then show the stepper.
When to use
| If the value is | Use |
|---|---|
| a small whole number set with − and +: items in a cart, guests, servings | Number stepper |
| a number people type: decimals, money, percent, a large or open range | |
| a value on a scale, set by dragging | |
| a quantity picked from a long list, or "10+" | |
| digits that aren't a quantity: a postcode, a card number | inputmode="numeric" |
People can't type into a stepper. If they need to, use a
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
| Use a stepper for a small whole number | Use it for money, decimals, or a large range |
Set max when there's a real cap |
Leave a capped quantity without max |
Show a visible label and match label to it |
Leave the stepper without a visible label |
| Use a Select for "10+" or long lists | Make people press + twenty times |
Related
Number input : a number people type.Slider : set a value by dragging.Select : pick a quantity from a list.Icon button : the trash button for the remove-at-minimum pattern.
API
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
label |
string | '' |
the accessible name; required, not shown |
unit |
string | — | a word screen readers hear after the value, such as guests |
value |
number | 0 |
the whole number; submits under name |
name |
string | — | the form field name |
min |
number | 0 |
the lowest value |
max |
number | — | the highest value; unset means no upper limit |
step |
number | 1 |
how much − and + change the value; a whole number |
large-step |
number | — | the Page Up / Page Down and Shift step |
disabled |
boolean | false |
not focusable, not changeable, left out of the form |
size |
medium | large |
medium |
height 32 / 36px |
Events
| Event | When |
|---|---|
input |
on every step |
change |
on every step |
Read the value property.
Methods
focus(): moves focus to the value.
CSS
::part(field): the bordered row.::part(decrement)and::part(increment): the − and + buttons.::part(value): the number in the middle.
Tokens
| Role | Rest | Hover / press | Disabled |
|---|---|---|---|
| − / + glyph | icon/secondary |
icon/default |
text/disabled |
| − / + background | none | state/accent/hover |
none |
| value text | text/default |
— | text/disabled |
| row border | border/subtle |
— | border/disabled |
| row background | surface/default |
— | surface/disabled |
Hover and press look the same. Hairlines in border/subtle separate the value from the
buttons. The focus border is border/accent/focus; corners are radius-8. Values per
theme:
Accessibility
Built in:
- The value is a spinbutton and the only Tab stop. Screen readers hear
label, the value (withunit), and the range. - The − and + buttons are hidden from screen readers and skipped by Tab; the arrow keys do the same thing, and pressing a button keeps focus on the value.
Your part:
- Show a visible label beside the stepper and use the same words in
label. - Set
min,max, andstepto the real range.
| Key | Action |
|---|---|
↑ / ↓ |
step up or down by step |
Shift + ↑ / ↓, Page Up / Page Down |
step by large-step |
Home / End |
go to min / max, when set |
Contrast limits
The row's resting border shares the Input's limits: see