AI Patterns

traces

Expandable Trace

A collapsible "Thought for Xs" summary that expands into a step-by-step trace.

  • Reading the component's UX doc

  • Comparing it against similar patterns

  • Checking accessibility noteskeyboard + reduced motion

  • Scaffolding the component

"use client";

import * as React from "react";
import { AnimatePresence, motion } from "motion/react";
import { Check, ChevronDown, Sparkles } from "lucide-react";

import { cn } from "@/lib/utils";

export interface TraceStep {
  label: string;
  meta?: string;
}

export interface ExpandableTraceProps {
  durationSeconds?: number;
  steps: TraceStep[];
  defaultOpen?: boolean;
  className?: string;
}

export function ExpandableTrace({
  durationSeconds = 4,
  steps,
  defaultOpen = false,
  className,
}: ExpandableTraceProps) {
  const [open, setOpen] = React.useState(defaultOpen);

  return (
    <div className={className}>
      <button
        type="button"
        onClick={() => setOpen((v) => !v)}
        aria-expanded={open}
        className="flex w-full items-center gap-2 px-4 py-3 text-left text-xs font-medium"
      >
        <Sparkles className="size-4 text-muted-foreground" />
        <span>Thought for {durationSeconds} seconds</span>
        <ChevronDown
          className={cn(
            "ml-auto size-4 text-muted-foreground transition-transform duration-200",
            open && "rotate-180"
          )}
        />
      </button>
      <AnimatePresence initial={false}>
        {open && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: "auto", opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={{ duration: 0.2, ease: "easeInOut" }}
            className="overflow-hidden"
          >
            <ul className="px-4 pb-4">
              {steps.map((step, i) => (
                <li key={i} className="flex gap-3">
                  <div className="flex flex-col items-center">
                    <span className="flex size-4 shrink-0 items-center justify-center rounded-full bg-muted">
                      <Check className="size-2.5 text-muted-foreground" />
                    </span>
                    {i < steps.length - 1 && <span className="my-1 w-px flex-1 bg-border" />}
                  </div>
                  <p className="pb-3 text-xs text-foreground/90">
                    {step.label}
                    {step.meta && (
                      <span className="ml-1.5 text-muted-foreground">{step.meta}</span>
                    )}
                  </p>
                </li>
              ))}
            </ul>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
}

A UX spec for this pattern — written for agents implementing or reusing it, not the code.

# Expandable Trace

## Summary
A collapsed-by-default summary line ("Thought for N seconds") that expands into a checklist of the discrete steps an agent took to produce its answer. It gives curious users a way to audit the reasoning without forcing everyone to read it by default.

## When to use
- Right after an agent finishes a multi-step task (tool calls, reasoning, search, edits) and you want to offer transparency without cluttering the default view.
- When the steps have a natural, ordered sequence with a clear notion of "done" per step.

## When not to use
- For a single atomic action with no real sub-steps — there's nothing meaningful to expand into; just state the result.
- As a substitute for real error handling. If a step failed, show that explicitly as a distinct state on that step (not by silently omitting it).
- Auto-expanded by default in a dense feed. Default collapsed keeps the primary answer scannable; only auto-expand when the user asked to see reasoning, or in a dedicated debugging surface.

## Anatomy
- Header button: icon + "Thought for N seconds" + chevron. The entire header is the toggle target, not just the chevron.
- Collapsible body: a vertical list of steps, each with a completion mark, a short label, and optional trailing metadata (e.g. "6 sources").
- A connecting line between steps so they read as one continuous sequence, not disconnected items.

## Behavior
- Collapsed by default.
- Expand/collapse animates height smoothly rather than snapping instantly.
- The step list is a static historical record once rendered — it does not keep updating live. Use the Thinking Loader for the in-progress version of this information.
- The duration shown in the header is fixed once the run is complete; it does not keep counting like the Thinking Loader's timer does.

## Content guidelines
- Step labels are short, neutral action phrases ("Reading the uploaded document"), not first-person narration ("I read the uploaded document").
- Trailing metadata should be a single scannable fact, not another full sentence.

## Accessibility
- The header button needs `aria-expanded` reflecting current state.
- The whole component must be operable by keyboard (Enter/Space on the header toggles it).
- Never hide information the user needs to trust or act on the primary answer exclusively inside the collapsed trace — the answer must stand on its own without expanding this.

## Related patterns
- Thinking Loader is the "in progress" counterpart — this component is what it becomes once work finishes.