← All proposals

SpinnerSequence

A new component, SpinnerSequence, that composes the existing PDS v2 Spinner and Illustration (the Spinner is not changed).

Net-new componentOpen in Storybook

proposals/SpinnerSequence/decisions.md23 lines · View on GitHub

Decisions

  • New component, not a Spinner prop (2026-11-03). The timers, the list of steps and the final state do not belong in a plain loading ring. SpinnerSequence composes the existing Spinner and Illustration and leaves Spinner as it is, so nothing that uses Spinner (Icon Button isWorking, Agentic Button, Timeline) can change. The designer chose this over adding the props to Spinner.
  • The ring is the current PDS Spinner, not the Claude Design ring. The design draws a thicker ring with a longer arc and no round ends; the designer said to use the current PDS Spinner. Only the size differs: default 5xl (64px, as in the design), settable with size.
  • Steps are data. steps: { label, duration? }[], any number of them, with defaultDuration (2000 ms, as in the design) for steps with no time of their own. No text lives in the component.
  • The final state is its own text and an illustration. finalLabel is required. illustrationName defaults to task; illustration takes any node and wins over the name. The designer asked that the illustration can be changed.
  • The sequence stops at the end. The design loops its three screens to demo it; a real loading state ends, so the final state stays. onComplete fires once, so a page can move on (the design redirects after 2 more seconds; that is the page's job).
  • Timers or the real work (activeStep). A timer is a guess; the actual work may take longer or shorter. When activeStep is set, the timers are off and the page moves the steps (equal to or past the number of steps shows the final state).
  • One live region. The text sits in role='status' (polite), so each new text is read out once. The Spinner is aria-hidden (as in v2) and the illustration is decorative, so nothing is announced twice. The Spinner's own hidden "Loading" is not read inside this.
  • The illustration is the same box as the spinner, bound to a token (2026-11-03). The ring's visible edge stops about 1px inside its box, so the illustration reads about 1px larger. The designer chose the token over a number that matches the ring (62.7px in Figma, 23.5 / 24 in code, both removed): no magic number, nothing to update when Spinner changes. Principle: no overrides; every value comes from a token, a style or a property.
  • Cross-fade. The spinner and the illustration sit on top of each other in a box the size of the spinner and fade over --pds-animation-duration-reveal (300ms); each new text fades in. Reduced motion turns both off.
  • Text style. The label uses the PDS pds-heading-m class, as the design does, not the Spinner's smaller label.
  • The task illustration is a local stand-in. The package (beta.14) has four illustrations and no task. The path comes from Illustration=TASK in the PDS Design Library v2 (node 5267:5707), exported from Figma as SVG. Its fill is currentColor (the export was #1A1A1A) so it follows the theme. The port adds task: { file: 'task.svg' } to the illustration registry and deletes the local data; the Illustration team may recolor it with their palette tokens.
  • Own folder. A new component has its own folder and its own Storybook page, Component Proposals/Spinner Sequence. It imports ../Spinner/Spinner.
  • Status tag: in-progress at first; ready-for-dev from 2026-11-03 (the designer's call); also new-pattern.
  • Open: whether the size the sequence should default to for the illustration (5xl, 64px) is right for the 120px source artwork; check it reads at 5xl.
  • Figma page sent (2026-11-03): Spinner Sequence, 5 frames, light only. The Default and Own Timing frames are captured on the first step (defaultDuration set high so the capture does not move on); Controlled By The Work also shows the first step; All Sizes and Other Illustration show the final state. The capture read the ring's dashed arc as four dashes, so the arc in the three frames that show it was rebuilt by hand as one round-capped arc (the same share of the circle as in the code). The Task illustration in the frames is the captured vector, not the library's Illustration=TASK component. Priority: next, for the NCX sign up flow.
  • Figma component built (2026-11-03). Spinner Sequence on the Figma page: two variants, State=Loading and State=Complete, with text properties Label and Final label. It reuses the PDS library Spinner and Illustrations components as exposed nested instances, so the Spinner's Color and Size and the Illustration (default TASK) are set from the instance's Properties panel, the way size and illustrationName are in code. There is no Size property on our component, by the designer's call (2026-11-03: "people should be able to set the size from the spinner and illustration prop").
  • Limit: the illustration does not follow the Spinner's size in Figma. The two nested instances are independent. In code size drives both (one box); in Figma the Illustration is bound to the primitive/spacing/5XL variable (64), so after changing the Spinner Size the Illustration's size variable must be changed too. Accepted by the designer.
  • Both text properties show on both variants. Label is linked only in Loading and Final label only in Complete, but Figma lists both on every instance; the one that is not linked does nothing.
  • Not covered by the checks: the steps and wait times are not properties in Figma (a static component cannot count or time); the example frame shows the idea with three instances and "after 2 s" notes. Its second step shows the same text as the first because the tool cannot write the Inter Tight text property; change it in Figma.
  • Tool limits (2026-11-03): Inter Tight cannot be loaded here, so no node holding an Inter Tight text can be moved to a new parent. The text layers are copies of the captured text, and the component set and example could not be put inside a section or an auto-layout frame; they sit on the page and the designer can group them in Figma.