# PercentileTrace (/docs/charts/percentile-trace)

PercentileTrace answers "is this entity's standing rising or slipping inside the pack?". It traces one **percentile
rank** over time on a scale **locked to 0–100**. Because the axis _is_ rank, the population is constant by definition —
the middle-half (**p25–75**) and near-full (**p5–95**) bands are fixed fields, not estimates — so the only line on the
chart is the entity itself, drifting through a known landscape.

```tsx
import { PercentileTrace } from "@microcharts/react/percentile-trace";

<PercentileTrace
  data={[40, 46, 52, 58, 63, 68, 72, 76, 79, 81]}
  unit="week"
  title="Standing"
/>
```

## Install

```tsx
import { PercentileTrace } from "@microcharts/react/percentile-trace";

<PercentileTrace data={ranks} title="Standing" />
```

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


## When to use it

- **Good for** — one player's or product's rank drifting over time, whether a standing has crossed into the top or
  bottom of the pack, a percentile KPI where the population context matters.
- **Avoid for** — a raw value over time (Sparkline) or one absolute number vs a target (Bullet / Delta).


## Variants

```tsx
<PercentileTrace data={[40, 46, 52, 58, 63, 68, 72, 76, 79, 81]} />
<PercentileTrace data={[40, 46, 52, 58, 63, 68, 72, 76, 79, 81]} showBands={false} />
```

The endpoint dot carries **valence**: by default a rising standing is good, so it turns positive; set `positive="down"`
when slipping _down_ the pack is the win (a support ticket's backlog rank, say). The line already encodes the direction,
so the color is only a redundant cue — never the sole signal.

```tsx
<PercentileTrace
  data={[78, 72, 64, 55, 47, 40, 34, 29, 26, 24]}
  positive="down"
/>
```

## Edge cases

```tsx
<PercentileTrace data={[63]} unit="week" />
```

```tsx
// ranks are 0–100 — values outside are clamped (and dev-warn once)
<PercentileTrace data={[102, 88, 96]} unit="week" />
```

```tsx
<PercentileTrace
  data={[40, 55, 68, 81.5]}
  format={{ maximumFractionDigits: 1 }}
  locale="de-DE"
  label="last"
/>
```

A single reading is a lone endpoint with no line to draw — it still reports its standing and holds the accessible-name
contract. Ranks outside 0–100 are clamped to the axis (a value of `102` reads as `p100`) and warn once in development.
With a `locale`, the percentile label and every announced number follow that locale's own formatting — `de-DE` renders
`p81,5` with a comma.


## Why this default

The full 0–100 axis with fixed bands, because a percentile is a standing _within a population_ — truncating the axis
would hide how much headroom or floor is left, and re-estimating the bands from the single traced series would be a
fiction. The middle-half and near-full fields are exact by construction, so the chart shows one honest line over a
landscape you can trust, never a thicket of competing series.

## Accessibility

The accessible name states the current percentile, the change from the first reading, and how the standing moved
relative to the middle half — **"p81 now, up 41 points from the first reading; moved above the middle half."** The
interactive entry steps the readings and announces each one's percentile.

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 or press `Escape`.
On touch, a tap pins and a drag scrubs.

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `data` (required) | `number[]` | Percentile ranks 0–100, one per reading; out-of-range values are clamped. |
| `showBands` | `boolean` | Draw the fixed p25–75 and p5–95 population fields (default true). |
| `positive` | `"up" \| "down"` | Which direction is good — colors the endpoint dot (default up). |
| `label` | `"last" \| "none"` | Final percentile in a right gutter. |
| `unit` | `string` | (interactive) Reading noun for the interactive announcement (default 'step'). |
| `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).
