Handoff: SpinnerSequence
What and why
A loading state for a wait of several seconds with stages (the NCX sign up flow: "Creating business account...", "Creating workspace...", then "Taking you to your business account"). It is a Spinner that walks through a list of steps, each with its own text, then ends on an illustration and a final text. It composes the existing PDS Spinner and Illustration; neither is changed. The team sets the steps (text, and optionally how long each shows), the final text and the illustration, and can run it on timers or drive it from the real work.
- Figma: Spinner Sequence page in PDS-Proposals (5 Storybook captures, light only; sent 2026-11-03) and a real Figma component,
Spinner Sequence(set625:2582:StateLoading/Complete,LabelandFinal label, with the library Spinner and Illustrations as exposed instances), plus an example flow. Jira: none. Source: the "Page 4 - Loading loop" artboard in the Claude Design project "Complete your profile". - Context:
context.md - Priority:
next, for the NCX sign up flow (docs/PRIORITY.md). Status:ready-for-dev(the designer's call, 2026-11-03).
Exploration and decisions
- New component, not Spinner props. The timers, the list of steps and the final state do not belong in a plain loading ring, and Spinner is used by Icon Button, Agentic Button and Timeline. So
SpinnerSequencecomposes the unchangedSpinnerandIllustration. - The ring is the current PDS Spinner, not the thicker ring in the Claude Design artboard. Default
5xl(64px);sizeis a prop. - The sequence stops at the end. The design loops its three screens as a demo; a real loading state ends.
onCompletefires once so the page can move on. - Timers or the real work. A timer is a guess, so
activeStepturns the timers off and lets the page move the steps. - The illustration is the same box as the ring, bound to the size token (no magic number). The ring's edge is about 1px inside its box, so the illustration reads about 1px larger; accepted by the designer (2026-11-03).
- Tried and not done: Spinner props for all of it; a
Sizeproperty on the Figma component (the designer wants the size set on the nested Spinner and Illustration).
API
| Prop | Type | Default | Purpose |
|---|---|---|---|
activeStep |
number |
none | Current step when the real work decides. 0 is the first step; a number equal to or greater than the number of steps shows the final state. Turns the timers off. |
className |
string |
none | Additional class names. |
defaultDuration |
number |
2000 |
How long each step shows, in milliseconds, unless the step has its own duration. |
finalLabel |
string |
required | Text shown with the illustration once the last step is done. |
illustration |
ReactNode |
none | Your own illustration for the final state. Takes over from illustrationName. Drawn at the ring size, decorative. |
illustrationName |
'task' | PDSIllustration |
'task' |
Which illustration to show in the final state. |
onComplete |
() => void |
none | Called once, when the final state shows. |
size |
's' | 'm' | 'l' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' |
'5xl' |
Spinner size. The illustration follows it. |
steps |
{ label: string; duration?: number }[] |
required | The loading steps, in order. Any number. |
Variants and states: Default (timed, two steps, then final), own timing and steps, all sizes (final state), controlled by the work (activeStep), other illustration.
Tokens used
--pds-animation-duration-reveal--pds-color-foreground-default--pds-spacing-2xl--pds-spacing-3xl--pds-spacing-4xl--pds-spacing-5xl--pds-spacing-6xl--pds-spacing-l--pds-spacing-m--pds-spacing-s--pds-spacing-xl
The label uses the PDS pds-heading-m class.
Custom variables (see custom.md): --sequence-fade, --sequence-size.
Impact
From impact.md: nothing reaches existing components; the new classes are scoped to the pds-spinner-sequence block, and one rule sizes an illustration inside it. No other proposal imports or uses it. It depends on Spinner (an extension, ready) and on the PDS Illustration component.
Port order (docs/PORT-ORDER.md): next, 13th, after Spinner. It is tagged ready for dev, so it is on the port list as ready.
Reused vs net-new
- Reused: the proposal's
Spinner(unchanged), the PDSIllustration, and thepds-heading-mclass. - Net-new: the component and its block,
.pds-spinner-sequence(seecustom.md); thetaskillustration (a local stand-in).
Accessibility
- The text sits in one
role='status'region (polite), so each new text and the final text are read out once. - The Spinner and the illustration are decorative (
aria-hidden), so nothing is announced twice. - The fade between the spinner and the illustration, and the text fade, stop under
prefers-reduced-motion; the timing still runs. - axe-core (the engine behind the Storybook a11y panel) found no violations, 2026-11-03: Default (loading step in dark; final state in light and dark), Own Timing And Steps (loading step, light), All Sizes (light), Controlled By The Work (dark) and Other Illustration (dark). Not every story in both themes.
- Keyboard: nothing in the component is interactive.
Decisions and open questions
- The
taskillustration is a local stand-in. The published package (beta.14) has four illustrations and notask. The path comes fromIllustration=TASKin the PDS Design Library v2 (node 5267:5707). The port addstask: { file: 'task.svg' }to the illustration registry and deletes the local data; the fill iscurrentColorhere, so the Illustration team may recolor it. - Is 5xl (64px) right for the 120px artwork? And a wide illustration (for example
underConstruction) is small at 64px high; should it follow the illustration's shape? - Figma: the illustration does not follow the Spinner's size in the Figma component; resize it by hand after changing the Spinner
Size. Steps and wait times are not Figma properties (a static component cannot count or time). - Not done: the Figma example's second step shows the first step's text (set it in Figma; the tool cannot write that font).
Port notes
- New component. Put it next to the Spinner in
pds-toolkit-reactundersrc/components/loading-indicators/SpinnerSequence/(generator:npm run generate:component). - Replace the local
mergeClassesstand-in with v2's, and the localtaskillustration data and the wrapper branch inSpinnerSequence.tsxwith the registry entry and the realIllustration. sizeTokensrepeats Spinner's size scale so Spinner stays untouched; in the port read it from Spinner or share it.- Copy
spinner-sequence.css.
Preview
npm run storybook, story Component Proposals/Spinner Sequence. Figma: Spinner Sequence, page Spinner Sequence.