← All proposals

Spinner: agentic color variant

Extend the existing PDS v2 Spinner (not a new component).

Extends a PDS componentOpen in Storybook

proposals/Spinner/decisions.md41 lines · View on GitHub

Decisions

  • Extend Spinner, not a new component. color already accepts any CSS color; color='agentic' is one more value. Matches how Erika described it ("a color variant"). Typed 'agentic' | (string & {}) so autocomplete shows the keyword without losing free-form colors.
  • Gradient token: --pds-gradient-ai-active, the same one pds-ai-active-border uses. One value for light and dark (only defined in variables.light.css, no dark override), so both themes use the same gradient.
  • Not an SVG gradient. The token is a CSS linear-gradient() string; SVG strokes can't use it and hardcoding its hex stops in <stop> would break the tokens-only rule. The agentic variant uses CSS boxes with the gradient as background and an alpha mask for ring/arc shapes.
  • Fixed gradient, spinning arc. The arc box rotates and its gradient layer counter-rotates, so colors stay put while the arc moves (as the ring does in the default spinner). Not animating the gradient shift (--pds-animation-duration-ai-active): the arc is already moving.
  • Track: same gradient at 0.3 opacity, matching the default track.
  • Mask data URIs contain stroke='black'. It is alpha-only (never painted), so it is not a color use, but a hex/color grep in handoff may flag it.
  • Open: round arc ends come from the mask's stroke-linecap; confirm at 2x in the smallest sizes.
  • Local mergeClasses and visually-hidden span stand in for v2's mergeClasses and HiddenLabel (not exported by the published package).
  • agentic is the only new color option (the AI gradient). A discovery color was tried in the stories and removed on 2026-10-15 at Erika's call.
  • hasAvatar puts the Zappy Agent Avatar inside the ring (2026-10-15). A boolean, not a slot: the avatar is always the agent avatar (hasHalo={false}, since the spinner is the ring) and the size is fixed at half the spinner, so the ring and avatar stay in proportion (2xl->xs, 3xl->s, 4xl->m, 5xl->l, 6xl->xl). Below 2xl the avatar would be under 16px, so it is ignored, as it is when isInline. The avatar is hidden from screen readers; the spinner's own label is announced. Depends on the local AgentAvatar. Open: should the avatar's dark disc be replaced by a transparent bolt at small sizes? Should the gap between ring and avatar be bigger?
  • Avatar gap tightened (2026-10-15). The avatar was half the ring, which left a wide gap. It is now one size up (2xl->s, 3xl->m, 4xl->l, 5xl->xl, 6xl->xl). xl is the largest Agent Avatar, so 6xl still has a wider gap; a bigger avatar size would close it.
  • Gradient bolt tried and removed (2026-10-15). Filling the bolt with the AI gradient (an overlay with mix-blend-mode) was built and rejected by Erika. The bolt stays the Agent Avatar's own color.
  • avatarVariant: disc (default) | bolt (2026-10-15). bolt is the Zappy bolt alone, no disc, filled with the AI gradient through a mask (same technique as the ring), half the ring tall. Added so the two can be compared side by side in the WithAvatar story. Open: pick one; if bolt wins, the prop can become the default and disc go.
  • The bolt uses the PDS shimmer (2026-10-15): a static AI-gradient bolt with the PDS pds-ai-active-shimmer utility on it (driven by --pds-animation-duration-ai-shimmer; Thinking State no longer uses this utility since 2026-10-19). A hand-made sliding gradient was tried first and replaced. Open: the shimmer is a white band with no dark value, so check it reads on the gradient in light and dark.

Only the plain bolt remains (2026-10-21)

  • Decision: the disc (the Agent Avatar in the ring) was removed, along with the avatarVariant prop, and the bolt is no longer colorized: it is the plain foreground color (--pds-color-foreground-default), with no gradient and no shimmer. hasAvatar just shows that bolt, half the ring tall, from 2xl up. The Spinner no longer imports the Agent Avatar.
  • Why: the designer's call from the comparison story: drop the first (disc) and do not colorize the icon of the second.
  • Port impact: the Spinner no longer depends on AgentAvatar (and so not on Icon through it); avatarVariant and SpinnerAvatarVariant are not ported.

Reverse, to compare: gradient bolt in a monochrome ring (2026-10-21)

  • Decision: a second row in the With Icon story (called With Avatar before 2026-10-21) shows the reverse of the first: the default (monochrome) ring with the bolt filled with the AI gradient and the PDS shimmer. To make it possible, avatarColor (default | agentic, default default) came back as a prop for the bolt's color; the gradient bolt reuses the earlier mask technique and the pds-ai-active-shimmer utility. The plain-bolt-in-gradient-ring look is still the default.
  • Why: the designer wants to compare the two.
  • Port impact: if the gradient bolt is not chosen, drop avatarColor and the --agentic bolt rule.
  • Open: which one ships.

Monochrome comparison removed; the bolt never shows inline (2026-10-21)

  • Decision: the second row (monochrome ring with the gradient bolt) and the avatarColor prop were removed after the comparison; the bolt stays the plain foreground color in the gradient ring. A rule, now documented and shown in the Inline with text story (which sets hasAvatar): the bolt does not show when isInline is set, whatever the size, as well as below 2xl.
  • Why: the designer's call.
  • Port impact: no avatarColor; hasAvatar is ignored for isInline.

The With Avatar story is now With Icon (2026-10-21)

  • Decision: the story that shows the bolt in the ring is renamed from With Avatar to With Icon, since the bolt is an icon, not an avatar. The prop is still hasAvatar; only the story name changed. The story id is now spinner--with-icon.
  • Port impact: none.