Skip to content

Input stepper <l-input-stepper> ​

A stepper control that enhances a native <input type="number"> with decrement/increment buttons and an animated number track.

HTML tag<l-input-stepper>
⏣Native
⬡Progressive
◇Plain
⬢Shadow-DOM
Progressive Custom Element

Options ​

Appearance ​

Pick a visual style via appearance. Each appearance has its own CSS import.

Default ​

Bordered box with inline buttons.

css
@import 'luxen-ui/css/input-stepper/default';
html
<l-input-stepper>
  <input
    type="number"
    value="1"
  />
</l-input-stepper>

Rounded ​

Circular standalone buttons with no container border — Airbnb-style.

css
@import 'luxen-ui/css/input-stepper/rounded';
html
<l-input-stepper appearance="rounded">
  <input
    type="number"
    min="1"
    max="10"
    value="1"
  />
</l-input-stepper>

Pill ​

Circular buttons inset into a filled capsule track — iOS-style quantity picker.

css
@import 'luxen-ui/css/input-stepper/pill';
html
<l-input-stepper appearance="pill">
  <input
    type="number"
    min="1"
    max="10"
    value="5"
  />
</l-input-stepper>

Size ​

Set the size attribute: xs, sm, md (default), lg, xl. All three appearances scale from the same control-height tokens, so their buttons match a button or input of the same size. pill then adds its inset around that row, which makes the capsule itself 8px taller than the other two at every size — give it its own line rather than a shared one when you put it in a row of form controls.

Code
html
<div class="flex flex-wrap gap-6 items-start">
  <div class="flex flex-col gap-4 items-start">
    <l-input-stepper size="xs">
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper size="sm">
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper size="md">
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper size="lg">
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper size="xl">
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>
  </div>

  <div class="flex flex-col gap-4 items-start">
    <l-input-stepper
      appearance="rounded"
      size="xs"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper
      appearance="rounded"
      size="sm"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper
      appearance="rounded"
      size="md"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper
      appearance="rounded"
      size="lg"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper
      appearance="rounded"
      size="xl"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>
  </div>

  <div class="flex flex-col gap-4 items-start">
    <l-input-stepper
      appearance="pill"
      size="xs"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper
      appearance="pill"
      size="sm"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper
      appearance="pill"
      size="md"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper
      appearance="pill"
      size="lg"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>

    <l-input-stepper
      appearance="pill"
      size="xl"
    >
      <input
        type="number"
        value="1"
      />
    </l-input-stepper>
  </div>
</div>

Not defined ​

Before JS loads (:not(:defined)), CSS provides a styled fallback with zero layout shift.

Code
html
<l-input-stepper>
  <input
    type="number"
    value="1"
  />
</l-input-stepper>

The CSS reserves space for the stepper buttons via padding-inline and matches the exact dimensions of the hydrated component. Once defined, the custom element replaces the padding with its own buttons.

Min / Max ​

Constrain the value range via min and max on the <input>.

Code
html
<l-input-stepper>
  <input
    type="number"
    min="0"
    max="10"
    value="5"
  />
</l-input-stepper>

Disabled ​

Native disabled attribute on the <input>.

Code
html
<div class="flex flex-wrap items-center gap-4">
  <l-input-stepper>
    <input
      type="number"
      value="3"
      disabled
    />
  </l-input-stepper>

  <l-input-stepper appearance="rounded">
    <input
      type="number"
      value="3"
      disabled
    />
  </l-input-stepper>

  <l-input-stepper appearance="pill">
    <input
      type="number"
      value="3"
      disabled
    />
  </l-input-stepper>
</div>

Roller ​

Enable the animated number roller overlay with with-roller.

Code
html
<div class="flex flex-wrap items-center gap-4">
  <l-input-stepper with-roller>
    <input
      type="number"
      min="0"
      max="99"
      value="5"
    />
  </l-input-stepper>

  <l-input-stepper
    appearance="rounded"
    with-roller
  >
    <input
      type="number"
      min="0"
      max="99"
      value="5"
    />
  </l-input-stepper>

  <l-input-stepper
    appearance="pill"
    with-roller
  >
    <input
      type="number"
      min="0"
      max="99"
      value="5"
    />
  </l-input-stepper>
</div>

Accessibility ​

Criteria ​

Role

Uses native <input type="number"> and native <button> elements — built-in semantics

WCAG4.1.2
RGAA11.1
Accessible name

The input must have an associated label via <label> or aria-label

WCAG1.3.1
RGAA11.1
Disabled state

Buttons disabled at min/max bounds; entire stepper disabled via disabled on the input

WCAG4.1.2
Form integration

Native <input> participates in form submission and validation directly

WCAG4.1.2

Rules

  • Wrap the stepper with a visible <label> or provide aria-label on the input element

Keyboard interactions ​

ArrowUp
Increments the value (native number input behavior)
ArrowDown
Decrements the value (native number input behavior)
Tab
Moves focus between the decrement button, input, and increment button

API reference ​

Importing ​

js
import 'luxen-ui/input-stepper';
css
@import 'luxen-ui/css/input-stepper/default';
/* or */
@import 'luxen-ui/css/input-stepper/rounded';
/* or */
@import 'luxen-ui/css/input-stepper/pill';

Attributes & Properties ​

minnumber | undefinedProperty
Minimum allowed value. Falls back to the input's min attribute.
maxnumber | undefinedProperty
Maximum allowed value. Falls back to the input's max attribute.
stepnumber | undefinedProperty
Step increment. Falls back to the input's step attribute.
sizeInputStepperSizedefault:'md'Property
Control size.
with-rollerbooleandefault:falseProperty
Enable the animated number roller overlay.
decrement-iconstringdefault:'lucide:minus'Property
Icon name for the decrement button.
increment-iconstringdefault:'lucide:plus'Property
Icon name for the increment button.

Events ​

changeEvent
Fired when the value changes. Bubbles. Properties: value: number.

CSS custom properties ​

--border-colordefault:--l-color-borderCustom property
Edge color of the stepper container (default and pill appearances) and of each button (rounded appearance).
--border-radiusdefault:--radius-mdCustom property
Border radius of the stepper container (default appearance).