← All proposals

Timeline

Net-new, in two parts.

Net-new componentOpen in Storybook

proposals/Timeline/decisions.md70 lines · View on GitHub

Decisions: Timeline

Append as you go, newest last. Record choices someone could reasonably question later, not every edit.

The full event log is a recipe, not a component

  • Decision: components are TimelineItem (row) and Timeline (thin list that owns ol semantics, spacing and the rail). The composition with an agentic region (start divider, agentic rows, Chain of Thought, live ThinkingState, end divider) is a story and Usage section, not a component.
  • Why: where the agent starts and stops, the divider wording and which rows are agentic are screen decisions; a component would need props for them and break for a log with two runs or none. A component can be added later (TimelineGroup); removing one is harder.
  • Port impact: none beyond the two components. The recipe is documentation the engineer can copy.

Names

  • Decision: Timeline and TimelineItem, chosen over ActivityLog / ActivityItem and Step.
  • Why: pairs with LabeledDivider as the marker; "Step" implies a sequence.
  • Port impact: a rename touches AgentStep and ChainOfThought, both tagged ready-for-dev.

Carried over from AgentStep (now TimelineItem)

These decisions were made for AgentStep and still apply to the row:

  • Its own primitive, not a part of ChainOfThought. A single tool call or progress line can sit inline without a list, so the row is element-agnostic (a div) and ChainOfThought wraps it in an <li>. Not merged with ThinkingState: that one owns the shimmer, pulse and timer; this one owns status, secondary text and detail.
  • status is optional: done (check), active (monochrome Spinner in the secondary status color, not the AI gradient), pending (empty circle), error (critical diamond exclamation). Without status or iconName there is no icon, which is the tool-log look. Status text is visually hidden for screen readers and comes from labelStrings (translatable).
  • Configurable icon: iconName replaces the status icon (for example wrench). The active spinner still wins so work in progress stays visible; the status color and screen reader text still follow status. With iconName and no status the icon shows in the secondary color.
  • Tool calls are steps with detail, not another component. A step with detail becomes a details toggle with a chevron (angle down, flipping to up when open; it was a right chevron turning down until 2026-10-21).
  • Secondary text is a slot; <code> inside it takes the code font.
  • Uses the local Spinner and Icon wrapper; the port swaps in the real ones. Storybook: Proposals/Timeline Item, tagged new-pattern.
  • Open: custom icon on a done step. The check is replaced, so completion shows only through color and hidden text. Keep a small check beside the custom icon?
  • Diff counts use pds-timeline-item__added / __removed spans in the secondary text slot (success and critical foreground). Reasoning text is a variant of Chain of Thought, not a step type.
  • Expandable step affordance: the row is inline-flex (only as wide as its content) with the chevron right after the text, like the Chain of Thought summary, and the chevron darkens on hover. It was pushed to the far edge of the list, which read as decoration, not part of the click target. Chain of Thought's summary chevron got the same hover cue.
  • Detail text inherits the step's size (and line height); paragraphs inside detail have their PDS default size and margins reset, and code uses the code font. Before, a <p> in the detail rendered larger than its label.
  • children slot under the step (2026-10-15). Always-visible content (a log block, file tags, a table) rendered below the row, indented to line up with the label when the step has an icon. Reference: Zappy investigating a failed build, where each step carries its own terminal output and file chips. Extends TimelineItem instead of adding a component, because the row, status and icon are the same. detail stays for content that starts collapsed. The slot is children (not content, which clashes with the native HTML content attribute). Chain of Thought gets it for free since its rows are TimelineItem.
  • Open: should content collapse when the step finishes? Today the consumer decides by rendering or not rendering it.
  • Open: larger, bolder step style and the Chain of Thought header with a metadata line ("Investigation started…") from the same reference are not built.
  • File identifiers under a step are PDS Tags, tagColor='slate' (the greyest Tag color; stone is beige), size='xs'. Tag, not Badge: these are objects, not status, and Tag supports linkContent, isRemovable and truncationLength. Chosen by Erika 2026-10-15.

AgentStep is renamed to TimelineItem (2026-10-16)

  • Decision: the row is now TimelineItem in proposals/Timeline/. New: time (and timeDateTime), isAgentic. AgentStep/ is removed; Chain of Thought renders a Timeline for its body.
  • Why: an agent run is one region of a larger event log, so the row should be generic and the agent flavor a modifier (isAgentic, the same boolean as Button). Designer chose rename over a thin wrapper.
  • Port impact: classes renamed pds-agent-step → pds-timeline-item, variable --agent-step-* → --timeline-item-*. The HANDOFF.md files of AgentStep and Chain of Thought need rewriting; AgentStep's ready-for-dev tag is gone with it.

Rail is optional

  • Decision: Timeline takes hasRail (default false). Chain of Thought renders Timeline without it and keeps its own left rule; an event log turns it on. The rail joins only neighbouring TimelineItems, so a divider or Chain of Thought between them breaks it.
  • Why: designer's answer. The rail is drawn by each item's entry (::after), because it has to run through the gap between items.
  • Port impact: uses the lh CSS unit.

Semantic type classes instead of font primitives (2026-10-19)

  • Decision: Timeline takes a new size prop (xs default, s) and puts pds-copy-<size> on the list; items inherit it, so --timeline-item-font-size is gone. A TimelineItem outside a Timeline inherits the surrounding text size (its stories wrap it in pds-copy-xs). The item's time and secondary text carry pds-copy-xs.
  • Why: The designer asked for semantic tokens over primitives where they exist; the semantic layer for type is the pds-copy-* classes. The classes are !important, so they only go on elements whose own font properties nothing else overrides. A child that sets its own weight (the medium labels in Chain of Thought) still wins, because the !important only beats rules on the same element, not inheritance. Computed sizes match before the change (summary 14px, rows 12px at s; divider 14px / 12px).
  • Port impact: adds the size prop to Timeline; drops --timeline-item-font-size.

TimelineItem takes an optional size (2026-10-19)

  • Decision: TimelineItem gets size?: 'xs' | 's', which puts pds-copy-<size> on the item. No default: unset, the item inherits its text size, so items in a Timeline (or Chain of Thought) still follow the list's size.
  • Why: items are used standalone (the Chat Panel header rows, the event log recipe), where the only way to size them was to wrap them in a pds-copy-* class; forgetting it rendered them at the page's body size. A prop is easier to find than the wrapper. The designer agreed.
  • Port impact: adds the size prop and its type to TimelineItem after the handoff was written; HANDOFF.md is updated. The item's own size wins over the list's.

Open questions

  • Tone: per-item isAgentic only, or also a TimelineGroup later (designer).
  • Is ThinkingState a plain child of Timeline, or does TimelineItem get an active look?

One chevron for every disclosure (2026-10-21)

  • Decision: the expandable step's chevron is now an angle-down that flips to up when open, the same as the Chain of Thought summary and the PDS Panel Row's expand button. It was a right chevron that turned down. The change is the icon (angleDown) and the open rotation (180 degrees instead of 90).
  • Why: the Chain of Thought summary and its steps pointed different ways when closed (down and right). Both reveal content in place, so up and down reads as show or hide below; a right chevron suggests going somewhere else.
  • Port impact: none beyond the icon name and the rotation.