Skip to content

Commit f87d60c

Browse files
feat(stepper): layered frame, progress fill exploration, completion view
Move Stepper onto LayerDialog's layered frame: steps sit on the elevated gray and the active step lifts onto a white card, with actions on the frame. Add an experimental `fill="progress"` mode that fills white through the furthest step reached, `Stepper.Complete` with `reset()`, and gate `goToStep` on reached steps. Fix unhandled rejections from `beforeNext`, a loading flash on sync Next, vertical headers locking already-reached steps, and rail error states. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 85aa932 commit f87d60c

7 files changed

Lines changed: 900 additions & 154 deletions

File tree

‎.changeset/add-stepper-component.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
feat(stepper): add `Stepper` component
66

7-
New `Stepper` component for multi-step flows, with `useStepper`/`useStep` hooks,
8-
horizontal and vertical orientations, and composable `Step`, `StepHeader`,
9-
`StepPanel`, `StepIndicator`, `StepperFooter`, `StepperBack`, and `StepperNext`
10-
parts. Exported from the package root and as a dedicated `components/stepper`
11-
entry point.
7+
New `Stepper` component for multi-step flows, laid out in LayerDialog's layered
8+
frame. Vertical and horizontal orientations, async `beforeNext` validation,
9+
error states, controlled or uncontrolled state, an optional `Stepper.Complete`
10+
view with `reset()`, and `useStepper`/`useStep` hooks. Exported from the
11+
package root and as a dedicated `components/stepper` entry point.

‎packages/kumo-docs-astro/src/components/demos/StepperDemo.tsx‎

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,7 @@ export function StepperHeroDemo() {
6060
<Stepper.Header icon={<UsersIcon />}>Team access</Stepper.Header>
6161
<Stepper.Panel>
6262
<div className="space-y-4">
63-
<Input
64-
label="Invite teammates"
65-
placeholder="name@company.com"
66-
/>
63+
<Input label="Invite teammates" placeholder="name@company.com" />
6764
<Radio.Group legend="Default role" defaultValue="developer">
6865
<Radio.Item
6966
value="admin"
@@ -267,3 +264,124 @@ export function StepperAsyncDemo() {
267264
</div>
268265
);
269266
}
267+
268+
/**
269+
* Exploration — `fill="progress"`: white fills every step up to the furthest
270+
* one reached, so the surface itself shows how far along you are.
271+
*/
272+
export function StepperProgressFillDemo() {
273+
return (
274+
<div className="w-full max-w-2xl">
275+
<Stepper.Root fill="progress">
276+
<Stepper.Step>
277+
<Stepper.Header icon={<BuildingsIcon />}>
278+
Project details
279+
</Stepper.Header>
280+
<Stepper.Panel>
281+
<Input label="Project name" placeholder="acme-web" />
282+
<Stepper.Footer>
283+
<Stepper.Back hideOnFirst />
284+
<Stepper.Next>Continue</Stepper.Next>
285+
</Stepper.Footer>
286+
</Stepper.Panel>
287+
</Stepper.Step>
288+
289+
<Stepper.Step>
290+
<Stepper.Header icon={<UsersIcon />}>Team access</Stepper.Header>
291+
<Stepper.Panel>
292+
<Input label="Invite teammates" placeholder="name@company.com" />
293+
<Stepper.Footer>
294+
<Stepper.Back />
295+
<Stepper.Next>Continue</Stepper.Next>
296+
</Stepper.Footer>
297+
</Stepper.Panel>
298+
</Stepper.Step>
299+
300+
<Stepper.Step>
301+
<Stepper.Header icon={<GearIcon />}>Configuration</Stepper.Header>
302+
<Stepper.Panel>
303+
<div className="flex flex-col items-start gap-3">
304+
<Checkbox label="Enable automatic deployments from main" />
305+
<Checkbox label="Enable preview environments" />
306+
</div>
307+
<Stepper.Footer>
308+
<Stepper.Back />
309+
<Stepper.Next>Continue</Stepper.Next>
310+
</Stepper.Footer>
311+
</Stepper.Panel>
312+
</Stepper.Step>
313+
314+
<Stepper.Step>
315+
<Stepper.Header icon={<RocketLaunchIcon />}>
316+
Review &amp; deploy
317+
</Stepper.Header>
318+
<Stepper.Panel>
319+
<Text variant="secondary">
320+
Creating your project will provision resources and run the first
321+
deployment.
322+
</Text>
323+
<Stepper.Footer>
324+
<Stepper.Back />
325+
<Stepper.Next finishLabel="Create project" />
326+
</Stepper.Footer>
327+
</Stepper.Panel>
328+
</Stepper.Step>
329+
</Stepper.Root>
330+
</div>
331+
);
332+
}
333+
334+
/** Exploration — `fill="progress"` in horizontal: the rail's pill grows. */
335+
export function StepperProgressFillHorizontalDemo() {
336+
return (
337+
<div className="w-full max-w-2xl">
338+
<Stepper.Root orientation="horizontal" fill="progress">
339+
<Stepper.Step>
340+
<Stepper.Header icon={<BuildingsIcon />}>Details</Stepper.Header>
341+
<Stepper.Panel>
342+
<Input label="Project name" placeholder="acme-web" />
343+
<Stepper.Footer>
344+
<Stepper.Back hideOnFirst />
345+
<Stepper.Next>Continue</Stepper.Next>
346+
</Stepper.Footer>
347+
</Stepper.Panel>
348+
</Stepper.Step>
349+
350+
<Stepper.Step>
351+
<Stepper.Header icon={<UsersIcon />}>Team</Stepper.Header>
352+
<Stepper.Panel>
353+
<Input label="Invite teammates" placeholder="name@company.com" />
354+
<Stepper.Footer>
355+
<Stepper.Back />
356+
<Stepper.Next>Continue</Stepper.Next>
357+
</Stepper.Footer>
358+
</Stepper.Panel>
359+
</Stepper.Step>
360+
361+
<Stepper.Step>
362+
<Stepper.Header icon={<GearIcon />}>Config</Stepper.Header>
363+
<Stepper.Panel>
364+
<Checkbox label="Enable preview environments" />
365+
<Stepper.Footer>
366+
<Stepper.Back />
367+
<Stepper.Next>Continue</Stepper.Next>
368+
</Stepper.Footer>
369+
</Stepper.Panel>
370+
</Stepper.Step>
371+
372+
<Stepper.Step>
373+
<Stepper.Header icon={<RocketLaunchIcon />}>Deploy</Stepper.Header>
374+
<Stepper.Panel>
375+
<Text variant="secondary">
376+
Review your settings and create the project.
377+
</Text>
378+
<Stepper.Footer>
379+
<Stepper.Back />
380+
<Stepper.Next finishLabel="Create project" />
381+
</Stepper.Footer>
382+
</Stepper.Panel>
383+
</Stepper.Step>
384+
</Stepper.Root>
385+
</div>
386+
);
387+
}

‎packages/kumo-docs-astro/src/pages/components/stepper.mdx‎

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
layout: ~/layouts/MdxDocLayout.astro
33
title: "Stepper"
4-
description: "A layered, accordion-style wizard for multi-step forms and flows, with first-class support for async transitions."
4+
description: "A layered wizard for multi-step forms and flows, with first-class support for async transitions."
55
sourceFile: "components/stepper"
66
---
77

@@ -12,6 +12,8 @@ import {
1212
StepperHeroDemo,
1313
StepperHorizontalDemo,
1414
StepperAsyncDemo,
15+
StepperProgressFillDemo,
16+
StepperProgressFillHorizontalDemo,
1517
} from "~/components/demos/StepperDemo";
1618

1719
{/* Hero Demo */}
@@ -22,6 +24,27 @@ import {
2224
</ComponentExample>
2325
</ComponentSection>
2426

27+
{/* Exploration: progress fill */}
28+
29+
<ComponentSection>
30+
31+
## Exploration: progress fill
32+
33+
Temporary, for comparison with the default above. With `fill="progress"` the
34+
white surface covers every step up to the furthest one reached instead of only
35+
the active step, so it grows as you move through the flow and doesn't shrink
36+
when you jump back.
37+
38+
<ComponentExample demo="StepperProgressFillDemo">
39+
<StepperProgressFillDemo client:load />
40+
</ComponentExample>
41+
42+
<ComponentExample demo="StepperProgressFillHorizontalDemo">
43+
<StepperProgressFillHorizontalDemo client:load />
44+
</ComponentExample>
45+
46+
</ComponentSection>
47+
2548
{/* Installation */}
2649

2750
<ComponentSection>
@@ -51,8 +74,11 @@ import { Stepper } from "@cloudflare/kumo/components/stepper";
5174
Stepper favors composition over configuration. Assemble `Header`, `Panel`, and
5275
`Footer` parts inside each `Step` rather than driving the component through a
5376
wall of props. The Root owns wizard state — active step, completion, and async
54-
transitions — and lays the steps out as a layered card where every header stays
55-
visible and the active step expands.
77+
transitions — and lays the steps out in the same layered frame as
78+
`LayerDialog`: every step sits on the gray frame, and the active step lifts onto
79+
a white card so it's clear where to focus. Completed steps keep their check,
80+
even after you jump back to an earlier one, and any step you've already reached
81+
stays clickable.
5682

5783
```tsx
5884
<Stepper.Root onComplete={submit}>
@@ -77,11 +103,13 @@ visible and the active step expands.
77103

78104
## Orientation
79105

80-
Set `orientation="horizontal"` for a compact, overflow-proof header: a Select
81-
that lists every step (jump to any you've already visited) flanked by prev/next
82-
arrows, iOS-picker style, with the active panel below. This stays clean no
83-
matter how many steps you have or how long the labels are. The state machine,
84-
async support, and error states are identical — only the layout changes.
106+
Set `orientation="horizontal"` to render the headers as a connected rail across
107+
the top of the frame. The active step's panel sits below it as a titled white
108+
card, and a direct `Stepper.Footer` moves onto the frame beneath that card, the
109+
same place `LayerDialog` puts its actions. On narrow viewports the rail becomes
110+
a Select that lists every step, with prev/next arrows on either side. The state
111+
machine, async support, and error states are identical — only the layout
112+
changes.
85113

86114
<ComponentExample demo="StepperHorizontalDemo">
87115
<StepperHorizontalDemo client:load />
@@ -167,7 +195,7 @@ const { activeStep, totalSteps, isLastStep } = useStepper();
167195
| Prop | Type | Default | Description |
168196
|------|------|---------|-------------|
169197
| `icon` | `ReactNode` | — | Leading icon slot. |
170-
| `clickable` | `boolean` | `false` | Allow clicking the header to jump to this step. |
198+
| `clickable` | `boolean` | reached steps | Allow clicking the header to jump to this step. Defaults to `true` for any step already reached; pass `false` to lock it. |
171199
| `indicator` | `ReactNode` | — | Override the default numbered/check status badge. |
172200
| `className` | `string` | — | Additional CSS classes. |
173201

‎packages/kumo/src/components/stepper/index.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ export {
66
type StepperProps,
77
type StepperRootProps,
88
type StepperOrientation,
9+
type StepperFill,
10+
type StepStatus,
11+
type GoToStepOptions,
912
type StepProps,
1013
type StepHeaderProps,
1114
type StepPanelProps,
1215
type StepIndicatorProps,
16+
type StepperCompleteProps,
1317
type StepperFooterProps,
1418
type StepperBackProps,
1519
type StepperNextProps,

0 commit comments

Comments
 (0)