data-stepper
Add custom increment and decrement buttons to number inputs. Respects min, max, and step attributes with automatic button disabling at boundaries.
Overview
The data-stepper attribute enhances a native <input type="number"> with visible increment and decrement buttons. Just add the attribute — the script reads min, max, and step from the input and builds the controls automatically.
How It Works
Add data-stepper to any <input type="number">. The init script:
- Wraps the input in a
.number-wrappercontainer - Inserts a decrease button (with
aria-label="Decrease") before the input - Inserts an increase button (with
aria-label="Increase") after the input - Sets both buttons to
tabindex="-1"so the input itself keeps keyboard focus - Reads
min,max, andstepattributes for boundary logic - Disables the decrease button when at
min, the increase button when atmax - Sets
data-stepper-initto prevent double-binding
The underlying input remains a real form control. It submits with the form, supports validation, and native keyboard arrows still increment and decrement as expected.
Attributes
| Attribute | Values | Description |
|---|---|---|
data-stepper |
boolean | Enables the stepper enhancement on a number input. |
data-stepper-init |
boolean | Set automatically to prevent double-binding. Do not set manually. |
min |
number | Standard HTML attribute. The decrease button is disabled at this value. |
max |
number | Standard HTML attribute. The increase button is disabled at this value. |
step |
number | Standard HTML attribute. Controls the increment/decrement amount per click. |
Decimal Steps
When step is a decimal value, the stepper uses toFixed() to maintain precision. No floating-point drift — values stay clean.
Custom Ranges
Combine min, max, and step for any numeric range. Buttons disable automatically at boundaries.
No Min/Max
Without min or max, neither button ever disables. The stepper allows unbounded incrementing and decrementing.
Button State at Boundaries
The stepper automatically manages button states based on the current value:
- When the value equals
min, the decrease button getsdisabled - When the value equals
max, the increase button getsdisabled - Button states update on every step, keyboard change, and manual input
- Disabled buttons use reduced opacity and
cursor: not-allowed
Events
Clicking a stepper button fires both input and change events on the underlying input, matching the behavior of native keyboard arrows.
| Event | Target | Description |
|---|---|---|
input |
The <input> |
Fired immediately when a stepper button is clicked. |
change |
The <input> |
Fired after the value changes, matching native behavior. |
In a Form
Steppers work naturally inside forms. Combine multiple steppers for booking or quantity selection interfaces.
Styling
The .number-wrapper container and its buttons are styled via CSS. All styles are gated on [data-stepper-init] so the input renders normally without JavaScript.
The wrapper uses inline-flex for alignment and rounds the outer corners. Button disabled states reduce opacity for clear visual feedback.
Dynamic Elements
Inputs added to the DOM after page load are automatically enhanced via a MutationObserver. No manual initialization is needed.
Accessibility
- Stepper buttons have
aria-label="Decrease"andaria-label="Increase"for screen readers - Buttons use
tabindex="-1"— the input itself retains keyboard focus, avoiding extra tab stops - Native keyboard arrows (Up/Down) still work for stepping, preserving expected input behavior
- The input remains a real
<input type="number">, so screen readers announce it correctly - Disabled buttons are conveyed to assistive technology via the native
disabledattribute - Without JavaScript, the input renders as a standard number field (progressive enhancement)