
Vertical orientation, touch gestures, responsive props, imperative API, and 9 bug fixes — all in one major release.
Introduction
This release started with a one-line CSS fix from a community contributor: wrap="nowrap". That PR from @kruschid — just a single prop addition — exposed a deeper truth: SelectStepper v1 wasn't ready for the real world. Narrow containers broke it. Touch devices couldn't use it. Vertical layouts weren't possible. And the animation engine had a race condition hiding in plain sight. So instead of patching, we rebuilt — keeping the same API surface but rearchitecting the internals to handle every scenario we'd been ignoring. The result is v2.0.0: the same component, but now it actually works everywhere.
✨ What's New
Vertical Orientation
Flip the stepper on its side. Navigation buttons switch to up/down arrows, keyboard keys remap automatically, and swipe gestures respond to vertical movement.
<SelectStepperdata={['XS', 'S', 'M', 'L', 'XL']}orientation="vertical"label="Size"/>Enter fullscreen mode Exit fullscreen mode
The orientation prop is also responsive — vertical on mobile, horizontal on desktop:
<SelectStepperorientation={{ base: 'vertical', sm: 'horizontal' }}data={['XS', 'S', 'M', 'L', 'XL']}/>Enter fullscreen mode Exit fullscreen mode
Swipe & Touch Gestures
Native Pointer Events — no external dependencies. Swipe left/right in horizontal mode, up/down in vertical. Enabled by default with a configurable threshold.
<SelectStepperdata={['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']}swipeable // true by defaultswipeThreshold={30} // pixelsloop/>Enter fullscreen mode Exit fullscreen mode
Imperative API
Control the stepper programmatically via controlRef. Navigate, reset, or jump to any value from parent code.
const controlRef = useRef<SelectStepperRef | null>(null);
<SelectSteppercontrolRef={controlRef}data={['React', 'Vue', 'Angular', 'Svelte']}/>
// From anywhere in the parent:controlRef.current?.next();controlRef.current?.prev();controlRef.current?.reset();controlRef.current?.navigateTo('Svelte');Enter fullscreen mode Exit fullscreen mode
Responsive Props (CSS-Native)
viewWidth, viewHeight, and size accept Mantine breakpoint objects. Under the hood, responsive values are resolved via InlineStyles with real CSS @media queries — the same pattern Mantine core uses in SimpleGrid and Grid. Zero React re-renders on resize.
<SelectStepperdata={['React', 'Vue', 'Angular']}viewWidth={{ base: 120, sm: 180, md: 260 }}size={{ base: 'xs', sm: 'sm', md: 'md' }}/>Enter fullscreen mode Exit fullscreen mode
Size Prop
Control the ActionIcon button size independently from xs to xl. Supports responsive breakpoint objects.
<SelectStepper data={data} size="lg" /><SelectStepper data={data} size={{ base: 'xs', md: 'md' }} />Enter fullscreen mode Exit fullscreen mode
Animation Callbacks
Synchronize external logic with navigation transitions using onStepStart and onStepEnd.
<SelectStepperdata={data}onStepStart={() => setAnimating(true)}onStepEnd={() => setAnimating(false)}/>Enter fullscreen mode Exit fullscreen mode
Gradient Variant
Full gradient support for navigation buttons, consistent with Mantine's variant system.
<SelectStepperdata={['Low', 'Medium', 'High', 'Ultra']}variant="gradient"gradient={{ from: 'indigo', to: 'cyan', deg: 45 }}/>Enter fullscreen mode Exit fullscreen mode
Accessible Button Labels
Localize navigation button labels for screen readers.
<SelectStepperpreviousLabel="Vorheriges Element"nextLabel="Nächstes Element"/>Enter fullscreen mode Exit fullscreen mode
New Props Summary
Prop | Type | Default | Description |
|---|---|---|---|
| `StyleProp<'horizontal' \ | 'vertical'>` |
|
|
|
| Viewport height in vertical mode |
|
|
| ActionIcon button size |
|
|
| Enable touch/swipe navigation |
|
|
| Min swipe distance in pixels |
|
| — | Fired when animation starts |
|
| — | Fired when animation ends |
| `RefObject<SelectStepperRef \ | null>` | — |
|
|
| Aria label for prev button |
|
|
| Aria label for next button |
|
| — | Gradient config for |
💥 Breaking Changes
defaultValue={null} no longer auto-selects
Before (v1): Passing defaultValue={null} auto-selected the first non-disabled item.
After (v2): defaultValue={null} means "no selection" — the component starts empty.
// If you relied on null to auto-select, just omit the prop:<SelectStepper data={data} /> // ← auto-selects first itemEnter fullscreen mode Exit fullscreen mode
viewWidth type changed to StyleProp
The prop now accepts responsive objects. TypeScript users who explicitly type viewWidth as CSSProperties['width'] will need to update to StyleProp<CSSProperties['width']>. Runtime behavior is backward compatible.
onChange timing changed
The callback is now properly wired through Mantine's useUncontrolled hook. If you had workarounds for stale-state issues in v1, they may no longer be needed.
🐛 Bug Fixes
Timeout race condition — Rapid clicks no longer orphan timers or cause setState on unmounted components
Controlled mode sync — External
valuechanges during animation no longer cause visual glitchesLoop animation — Infinite scroll now consistently moves forward instead of reversing at the boundary
Narrow container wrapping — Navigation buttons no longer wrap to a new line (thanks @kruschid)
View shrinking misalignment — Resizing below
viewWidthno longer misaligns the scroll offsetStale data handling — Changing
datawhilevaluereferences a removed item now auto-resetsCallback consistency —
onStepStartonly fires when navigation actually occurs;onLeftIconClick/onRightIconClickrespectcanGoPrev/canGoNextDemo import typo — Styles API demo no longer references the wrong package
🔧 Improvements
Full ARIA Accessibility
The component now implements role="spinbutton" with complete attributes: aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext, aria-disabled, aria-label, and aria-labelledby. A hidden aria-live="polite" region announces value changes to screen readers.
Orientation-Aware Keyboard Navigation
Arrow keys are now direction-specific: ArrowLeft/ArrowRight in horizontal, ArrowUp/ArrowDown in vertical. No more confusing cross-axis navigation.
66 Tests (up from 2)
Comprehensive coverage across: navigation, keyboard, controlled/uncontrolled mode, loop, disabled items, empty data, swipe gestures (with PointerEvent polyfill), imperative API, responsive props, ARIA attributes, and animation callbacks.
Documentation Reorganized
Docs follow the Mantine standard: Installation → Usage → Core → Behavior → Visual → Advanced → Reference. Eight new interactive demos added.
📦 Installation
yarn add @gfazioli/mantine-select-stepper
Enter fullscreen mode Exit fullscreen mode
Import styles at your app root:
import '@gfazioli/mantine-select-stepper/styles.css';Enter fullscreen mode Exit fullscreen mode
Or with CSS layers:
import '@gfazioli/mantine-select-stepper/styles.layer.css';Enter fullscreen mode Exit fullscreen mode
Compatibility: Mantine 8.x · React 18/19 · TypeScript 5.x · MIT License
Comments (0)
Login to post a comment.