← All proposals

Chain of Thought

A collapsible list.

Net-new componentOpen in Storybook

proposals/ChainOfThought/HANDOFF.md109 lines · View on GitHub

Handoff: ChainOfThought

What and why

A user needs to see what an agent did, and open it up when they want the detail: its reasoning, a web search, tool calls, or plain progress. PDS has nothing for this. This proposal is a collapsible record: a summary line (a Thinking State with a chevron, such as "Thought for 4 seconds") over a list of steps. Each step is a TimelineItem: an optional status icon, a label, quiet secondary text, optional expandable detail, and a slot for any content under it (a log block, file tags, a table). The list itself is a Timeline without the rail.

Chain of Thought builds on two other proposals. It renders the summary with ThinkingState, its body is a Timeline and its rows are TimelineItem (../Timeline/HANDOFF.md, written 2026-10-16; AgentStep was renamed to TimelineItem). Timeline and TimelineItem must port with it; the row's API is in that handoff.

  • Figma / Jira: none. Sources were designer screenshots: a tool-call log, a reasoning-and-tools trace, and an agent investigating a failed build.
  • Context: context.md

Exploration and decisions

  • variant: steps (default), reasoning, search and coding. It sets the body: steps is rows with status icons; reasoning is running text; search is a quiet query row with result rows (a bold title and the domain as secondary text) and a quiet "+N more" row; coding is rows with a bold verb, code-font secondary text and +/- diff counts. The rows are TimelineItem, so the body is the consumer's content.
  • Built on native <details>, not ExpansionPanel. ExpansionPanel is a bordered card with a space-between chevron. The references have a borderless, left-aligned summary with the chevron beside the label and a rule down the left of the steps.
  • The summary is rendered by Chain of Thought from label, isActive, time, showTimer and iconName, so the size hierarchy is enforced. The cost is that there is no custom summary.
  • expandWhenActive opens the list while isActive is true and closes it when the agent finishes. The first time the user opens or closes it, the automatic behavior stops for that instance.
  • size: s is a 14px summary over 12px rows; m is 16px over 14px. Rows read the size from the list (Timeline's size, or a pds-copy-* class on the reasoning list).
  • The body is a Timeline. Chain of Thought renders Timeline (no rail) for the list variants, so list semantics belong to the list and a step also works alone inline. It keeps its own rule down the left of the steps. Steps are children, not a data array.
  • Content under a step is a slot (children on TimelineItem), always visible and lined up with the label. detail stays for content that starts collapsed. In the example, a finished step keeps its output in detail (collapsed) and the active step shows its content open.
  • File tags under a step are PDS Tag (tagColor='slate', size='xs'), not Badge: they are objects, not status, and Tag can link and truncate.

API

ChainOfThought

Prop Type Default Purpose
children ReactNode none The body. TimelineItem components for the list variants (each is wrapped in a list item), or paragraphs for reasoning.
className string none Additional class names.
expandWhenActive boolean false Open while isActive is true and close when it turns false, until the user toggles it.
iconName IconProps['iconName'] 'zappy' Icon before the summary label.
isActive boolean false Agent still working: shimmers the label and pulses the icon.
isOpen boolean false Initial open state.
label string required The always-visible line that toggles the list.
showTimer boolean false Show the elapsed time. Only renders with time.
size 's' | 'm' 's' Size of the whole block.
time string none Elapsed time, formatted by the consumer.
variant 'coding' | 'reasoning' | 'search' | 'steps' 'steps' What the list shows.

The row props (TimelineItem) are in ../Timeline/HANDOFF.md.

Variants and states: steps, reasoning, search and coding; sizes s and m; active, completed and expanded or collapsed; steps with detail, content, secondary text and diff counts; all four step statuses.

Tokens used

Chain of Thought:

  • --pds-animation-transition-rotation
  • --pds-border-radius-default
  • --pds-border-width-default
  • --pds-border-width-outline
  • --pds-color-border-default
  • --pds-color-foreground-default
  • --pds-color-foreground-default-secondary
  • --pds-color-interactive-focus
  • --pds-spacing-2xs
  • --pds-spacing-s
  • --pds-spacing-xs
  • --pds-typography-ff-default
  • --pds-typography-fw-medium
  • --pds-typography-fw-regular
  • --pds-typography-lh-m
  • --pds-typography-size-s
  • --pds-typography-size-xs

Timeline and TimelineItem add the status and diff colors and the code font; see ../Timeline/HANDOFF.md. Custom variables here: --chain-of-thought-indent, --timeline-gap (set on the list).

Impact

From impact.md: nothing reaches existing PDS components; everything is scoped to pds-chain-of-thought. It depends on ThinkingState, Timeline, TimelineItem, the local Icon and Spinner (the active step uses a monochrome spinner). The example stories use the package's CodeBlock, Tag and FlexContainer.

Reused vs net-new

  • Reused: ThinkingState (summary), Timeline and TimelineItem (body), Icon, Spinner, and in the stories CodeBlock, Tag and FlexContainer.
  • Net-new: Chain of Thought (and, through its body, Timeline and TimelineItem; see their handoff). See custom.md.

Accessibility

  • The summary is a summary inside details, so Enter and Space toggle it natively, and it takes the visible focus ring (--pds-color-interactive-focus). The state is mirrored in React so the chevron rotates.
  • The list is a Timeline (an ol), so a screen reader announces the number of steps.
  • Each step's status is announced as visually-hidden text (labelStrings, translatable); the active spinner has its own label.
  • Shimmer and pulse on the summary stop under prefers-reduced-motion.
  • The a11y panel was clean on the Event log with agent story, which includes a Chain of Thought, in light. It has not been run on the Chain of Thought stories themselves, nor in dark. Run it in both before the port.

Decisions and open questions

  • Reasoning text. Plain paragraphs now; italic, links and markdown are not handled.
  • Motion. Steps appear instantly when added. Does a new step animate in?
  • Long lists. No max-height or scroll; decide when steps can run to dozens.
  • Result favicons. Search results use a globe icon; the reference shows each site's favicon. A leading slot on TimelineItem would take an image.
  • "+7 more" is a plain quiet row. Should it be a link or button that expands the rest?
  • Custom icon on a done step replaces the check, so completion shows only by color and hidden text. Keep a small check?
  • Content when a step finishes. Should content under a step collapse when it is done? The consumer decides now.
  • Not built: a larger, bolder step style and a Chain of Thought header with a metadata line ("Investigation started..."), both from the investigation screenshot.
  • Stopped runs. Should a stopped run pass through to the summary (stopped state)?
  • The dark-mode shimmer question from ThinkingState is settled (text-clipped gradient, confirmed 2026-10-19); it applies here unchanged.

Port notes

  • New components. Where they live is the engineer's call, but a reasonable home is src/components/ for Chain of Thought with Timeline beside it. Use npm run generate:component for each, then copy the files over.
  • Port ThinkingState first, then Timeline and TimelineItem, then Chain of Thought. Swap the local Icon wrapper and mergeClasses for the real ones, and port the Spinner proposal for the step spinner (ready: ../Spinner/HANDOFF.md). The order is in docs/PORT-ORDER.md.

Preview

npm run storybook, then Component Proposals/Chain of Thought (rows: Component Proposals/Timeline/Timeline Item).

Figma: https://www.figma.com/design/AMGWJbraJoi2PKWnxo29Ah/PDS-Proposals?node-id=374-45, page Chain of Thought (6 stories, light only; captures of Storybook with library icons swapped in, sent 2026-10-20).