Drawn Capsule Stepper
An SVG stroke draws a capsule outline, then step pills inside light up in sequence — for setup wizards, task progress and onboarding flows.
A multi-step flow packed into a single capsule. On entry a stroke starts at the middle of the left edge and draws the capsule outline clockwise; once it is done, the step pills inside fade in one by one. As the flow advances, completed steps fill with the accent colour and draw a white check, the active step carries a subtle breathing halo, and a second accent stroke travels along the outline to the completed ratio — the outline itself is a progress bar. In compact mode only the active step expands to show its label; the rest keep just an icon or number. The step index supports controlled and uncontrolled modes, and can advance on its own.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add drawn-capsule-stepper.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<DrawnCapsuleStepper/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/drawn-capsule-stepper.en.jsonOr, once registries are configured in components.json, install it as @webberui/drawn-capsule-stepper.
Usage
import { Package, Rocket, Store } from "lucide-react";
import { DrawnCapsuleStepper } from "@/components/ui/drawn-capsule-stepper";
const steps = [
{ id: "create", label: "Create shop", icon: <Store /> },
{ id: "upload", label: "Add products", icon: <Package /> },
{ id: "launch", label: "Open for business", icon: <Rocket /> },
];
// Uncontrolled + auto-advance
<DrawnCapsuleStepper steps={steps} autoPlay />
// Controlled: drive the current step from outside buttons
const [current, setCurrent] = React.useState(0);
<DrawnCapsuleStepper
steps={steps}
current={current}
onCurrentChange={setCurrent}
compact={false}
clickable
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
steps | DrawnCapsuleStep[] | — | Step list, 3–6 steps recommended |
current | number | — | Controlled active step index (0-based); steps.length means all steps are complete |
defaultCurrent | number | 0 | Initial step index in uncontrolled mode |
onCurrentChange | (current: number) => void | — | Called when the step changes, with the new step index |
autoPlay | boolean | false | Advance through the steps automatically, looping back to the first step after finishing |
interval | number | 1400 | Auto-advance interval (ms) |
trigger | "mount" | "inView" | "inView" | When the outline draws in: right after mount, or when scrolled into view |
drawDuration | number | 900 | Outline draw duration (ms), written to --wb-drawn-capsule-draw |
compact | boolean | true | Compact mode: only the active step expands to show its label; the rest show an icon or number |
clickable | boolean | false | Allow clicking a completed step to jump back to it |
accent | string | "#3b82f6" | Accent colour (completed fill, progress stroke, active outline), written to --wb-drawn-capsule-accent |
className | string | — | Passed through to the outermost container |
DrawnCapsuleStep
| Field | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique step id, used as the React key |
label | string | — | Step name; in compact mode only the active step shows its text |
icon | React.ReactNode | — | Custom icon; falls back to the step number |
How it works
- The outline is a hand-built capsule
<path>(corner radius = half the height) rather than<rect rx>, so the stroke can start at the middle of the left edge and run clockwise. The container is measured withResizeObserver, and the path is recomputed when the width changes. - The stroke draws via
pathLength0→1 with the easing[0.65, 0, 0.35, 1]; afterwards the step pills fade in with a 60ms stagger. Only then does the progress stroke start, following the same path on a spring (stiffness 120, damping 20) to "completed ÷ total steps". - The active step's width change expands with a layout animation, and the white check on completed steps draws in over 240ms.
- CSS variables:
--wb-drawn-capsule-draw(default900ms, same asdrawDuration),--wb-drawn-capsule-stagger(60ms) and--wb-drawn-capsule-accent(same asaccent); override the colour on a parent to match your theme. autoPlaystarts timing only after the draw and fade-in finish; once every step is complete it waits one moreintervalbefore returning to the first step.
Accessibility
- The step container is
role="list"witharia-label="Setup progress", and each step isrole="listitem"; the active step setsaria-current="step" - Completed steps carry screen-reader-only "completed" text; labels hidden in compact mode are kept as sr-only text so no step loses its name
- With
clickable, completed steps are nativetype="button"buttons — focusable with Tab, triggered with Enter or Space, with a focus-visible ring - Step changes are written to an
aria-live="polite"region, e.g. "Step 2 of 4: Add products" - The outline SVG, icons, numbers and pulsing halo are all
aria-hidden - When the user's system has reduce motion enabled, the outline appears immediately, the progress stroke jumps straight to its target, and the steps neither fade in nor pulse
Quiz Card
A single- or multiple-choice quiz card React component. The correct option draws a check, wrong picks shake, an explanation expands and the next question slides in, with progress, a rolling score and an end screen.
Mutation List Choreographer
Full CRUD list choreography — insertions open a gap and drop in, deletions collapse the neighbours, reordering moves continuously with FLIP, batches cascade in waves, and removals leave an undo ghost.