# TraceFold (/docs/charts/trace-fold)

A request took 214 ms and you need to know which spans set that number. TraceFold draws one rect per span, placed by its
start time, sized by its duration, and stacked by its nesting depth. Widths sit on one shared linear time scale, never
normalized per row, so a wide span is a slow span. The critical path, the chain of spans that bound the total, is
accented and every other span muted.

```tsx
import { TraceFold } from "@microcharts/react/trace-fold";

<TraceFold
  data={[
    { label: "request", start: 0, duration: 214, depth: 0 },
    { label: "db.query", start: 10, duration: 86, depth: 1, parent: 0 },
    { label: "auth", start: 0, duration: 8, depth: 1, parent: 0 },
    { label: "render", start: 96, duration: 60, depth: 1, parent: 0 },
    { label: "serialize", start: 156, duration: 40, depth: 1, parent: 0 },
    { label: "index-scan", start: 12, duration: 70, depth: 2, parent: 1 },
    { label: "decode", start: 82, duration: 12, depth: 2, parent: 1 },
    { label: "log", start: 200, duration: 14, depth: 1, parent: 0 },
    { label: "gc", start: 90, duration: 5, depth: 2, parent: 1 },
  ]}
  format={(n) => `${Math.round(n)} ms\
```

## Install

```tsx
import { TraceFold } from "@microcharts/react/trace-fold";

<TraceFold data={spans} title="Request trace" />
```

Setup (package + stylesheet): [Quickstart](/docs/quickstart#set-up-with-an-ai-agent) or paste [`/agent-setup.md`](/agent-setup.md) into your agent.

## Try it

```tsx
import { TraceFold } from "@microcharts/react/trace-fold/interactive";

<TraceFold
  data={spans}
/>
```

## When to use it

Use it for request traces, flame charts, and p95-exemplar latency breakdowns. For a single duration use Bullet; for a
time series use Sparkline.

## Sizing

**endpoint cell**

```tsx
<TraceFold data={row.spans} labels={false} width={80} height={24} />
```

**structure audit**

```tsx
<TraceFold data={spans} emphasis="none" />
```

## Variants

```tsx
<TraceFold
  data={[
    { label: "request", start: 0, duration: 214, depth: 0 },
    { label: "db.query", start: 10, duration: 86, depth: 1, parent: 0 },
    { label: "render", start: 96, duration: 60, depth: 1, parent: 0 },
    { label: "index-scan", start: 12, duration: 70, depth: 2, parent: 1 },
  ]}
  emphasis="none"
  format={(n) => `${Math.round(n)} ms\
```

`emphasis="none"` drops the accent when the nesting, not the latency, is what you are reading. The critical-path walk
itself is deterministic: ties resolve to the earliest start.

```tsx
<TraceFold
  data={[
    { label: "request", start: 0, duration: 14868, depth: 0 },
    { label: "db.query", start: 1200, duration: 9234, depth: 1, parent: 0 },
  ]}
  format={{ style: "unit", unit: "millisecond", unitDisplay: "narrow", maximumFractionDigits: 0 }}
  locale="de-DE"
/>
```

`format` also takes `Intl.NumberFormatOptions` — with a `locale`, the accessible summary's numbers follow that locale's
own grouping and decimal marks ("14.868 ms" in German, not "14,868 ms"). In-rect text stays the plain span label either
way; only the announced numbers are localized.

## Edge cases

```tsx
<TraceFold data={[{ label: "request", start: 0, duration: 42, depth: 0 }]} title="Single span" />
```

```tsx
// 3 depths squeezed into a 24px cell — each row falls under the floor
// font's seat height, so labels drop everywhere instead of overlapping
<TraceFold
  data={[
    { label: "request", start: 0, duration: 120, depth: 0 },
    { label: "db.query", start: 10, duration: 70, depth: 1, parent: 0 },
    { label: "index-scan", start: 12, duration: 40, depth: 2, parent: 1 },
  ]}
  height={24}
/>
```

A zero-duration span keeps a one-unit floor so it stays visible. That is the only width distortion in the chart.

## Four homes

**In a sentence**

```tsx
<p>
  Request trace{" "}
  <span className="mc-inline">
    <TraceFold data={spans} summary={false} />
  </span>{" "}
  — 214 ms total, DB span dominates.
</p>
```

**In a table cell**

```tsx
<td>
  <TraceFold data={spans} />
</td>
```

**In a KPI card**

```tsx
<div className="kpi">
  <span className="figure">214ms</span>
  <span className="unit">total trace</span>
  <TraceFold data={spans} />
</div>
```

**In a tab header**

```tsx
<button className="tab">
  /api <TraceFold data={spans} />
</button>
```

## Accessibility

The accessible name gives the biggest span and its path status: **"4 spans over 214 ms; longest db.query (86 ms) on the
critical path."** The interactive entry roves spans within a depth with ←/→ and between depths with ↑/↓, announcing each
span's duration, share of the total, depth, and whether it is on the critical path.

The interactive entry follows the shared [interaction contract](/docs/accessibility#one-interaction-contract):
arrow keys rove between units on both axes, `Home` and `End` jump to the ends, and a click, tap, `Enter` or
`Space` selects a unit — pinning its readout so it survives blur, until you select it again, press `Escape`, or
press outside the chart. On touch, a tap pins and a drag scrubs.

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `data` (required) | `Span[]` | Flat span list; parent = index. |
| `emphasis` | `"critical" \| "none"` | Mute non-critical spans, or uniform. |
| `labels` | `boolean` | Width-gated in-rect labels. |
| `animate` | `boolean` | (interactive) Opt-in entrance motion when the chart mounts client-side — add `import "@microcharts/react/motion"` once. Inert on the server, on hydrated server HTML, and under `prefers-reduced-motion`. |

Plus the shared grammar — `data`, `domain`, `color`, `title`, `summary`, `format` — and the layout props (`width`, `height`, `className`, `style`) that every chart accepts. Interactive entries also share `animate` and `live`, and — wherever a chart has more than one navigable unit — `onActive`, `onSelect`, `selectedIndex` and `defaultSelectedIndex`; and — wherever the chart shows a hover value — `readout`. See [the shared grammar](/docs/quickstart#the-shared-grammar).
