Stepper
Numeric quantity control with increment and decrement buttons. Two layout variants (pill and floating), five sizes, full intent palette, min/max/step, disabled/readOnly states, and LTR/RTL support.
Installation
pnpm add @tessinaui/uiUsage
import { Stepper } from "@tessinaui/ui";{/* Basic uncontrolled */}
<Stepper />
{/* Controlled */}
<Stepper value={quantity} onChange={setQuantity} min={1} max={99} />
{/* Floating variant */}
<Stepper variant="floating" />
{/* With label + validation */}
<Stepper
label="Quantity"
intent="error"
supportingText="Maximum quantity reached"
value={10}
min={1}
max={10}
/>
{/* Custom step */}
<Stepper step={5} defaultValue={0} />
{/* RTL */}
<Stepper dir="rtl" label="الكمية" />Playground
Preview
Showcase
Preview
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "pill" | "floating" | "pill" | pill — decrement, value and increment share one rounded-full container; floating — each button is a separate filled circle |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Size token |
intent | "none" | "primary" | "error" | "warning" | "success" | "info" | "none" | Color intent — affects button/pill background and text color |
value | number | — | Controlled value |
defaultValue | number | 0 | Uncontrolled initial value |
min | number | -Infinity | Minimum value — decrement button is disabled at this value |
max | number | Infinity | Maximum value — increment button is disabled at this value |
step | number | 1 | Amount to change per click |
disabled | boolean | false | Disable all interaction; container gets opacity-60 |
readOnly | boolean | false | Buttons are non-interactive (opacity-40, pointer-events-none) — value is still visible |
onChange | (value: number) => void | — | Called with the new value after each change |
label | string | — | Visible label rendered above the stepper |
supportingText | string | — | Helper / validation text below the stepper (colored by intent) |
groupLabel | string | "Quantity" | aria-label for the stepper group — used when no visible label is provided |
decrementLabel | string | "Decrease" | aria-label for the − button |
incrementLabel | string | "Increase" | aria-label for the + button |
dir | "ltr" | "rtl" | "ltr" | Text direction. In RTL the + button is on the leading (right) side and − is on the trailing (left) side |
rounded | "none" | "sm" | "md" | "lg" | "full" | "full" | Border radius of the pill container and pill buttons. Floating buttons always stay rounded-full. |
className | string | — | Additional class on the root wrapper |
Variants
| Variant | Description |
|---|---|
pill | All three elements (−, value, +) are inside one rounded-full container. Buttons are transparent; the pill provides the background. |
floating | Each button is a standalone filled circle. The value floats between them without a container background. |
Intents
| Intent | Pill bg | Floating button bg | Text |
|---|---|---|---|
none | bg-secondary | bg-secondary | text-foreground |
primary | bg-primary | bg-primary | text-on-primary |
error | bg-error-light | bg-error-light | text-error |
warning | bg-warning-light | bg-warning-light | text-warning |
success | bg-success-light | bg-success-light | text-success |
info | bg-info-light | bg-info-light | text-info |
Notes
- Controlled vs uncontrolled: When
valueis provided, the component is fully controlled. UsedefaultValuefor uncontrolled usage. - Min/Max clamping: Values are always clamped between
minandmax. The respective button dims toopacity-30and becomes non-interactive at the boundary. - RTL:
dir="rtl"swaps the + and − button sides. The + button moves to the inline-start (right) side, matching standard RTL number-input conventions (e.g. Carbon, Fluent). - Accessibility: The control renders as
role="group"witharia-labeloraria-labelledby. The value uses<output aria-live="polite">so screen readers announce changes. - readOnly vs disabled:
disabledprevents all interaction and dims the whole control;readOnlydims only the buttons (the value remains clearly readable).
Pagination
Navigate between pages of content. Five sizes, three active-page variants (default/primary/outline), five border-radius options (none/sm/md/lg/full), three nav styles (icon/label/none), smart ellipsis truncation, controlled/uncontrolled, and LTR/RTL support.
SegmentedControl
A radio-group style control where one segment is selected at a time, with outline, filled, and pill variants, horizontal and vertical orientations, and full keyboard navigation