# TreeRings (/docs/charts/tree-rings)

TreeRings answers "how did growth accumulate, period over period?" like the rings of a tree: the oldest period is at the
centre and each new period adds a ring outward, whose **thickness** is that period's value. The channel is thickness,
not area — equal thickness at a larger radius spans more area (the ring illusion), so read the thicknesses, not the
wedges; exact per-period reads steer to `SparkBar`.

```tsx
<TreeRings
  data={[8, 12, 10, 18, 22, 15, 20, 14]}
  label="last"
  unit="years"
  periodWord="year"
  title="Account age"
  size={40}
/>
```

## Install

```tsx
import { TreeRings } from "@microcharts/react/tree-rings";

<TreeRings data={years} unit="years" periodWord="year" title="Account age" />
```

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** — account or company age at a glance, a cohort-age marker in a table cell, or a per-period growth story
  in a KPI card.
- **Avoid for** — exact per-period reads (SparkBar), many periods (over twenty-four), or non-cumulative series.


## Variants

```tsx
const years = [8, 12, 10, 18, 22, 15, 20, 14];

<TreeRings data={years} rings="fill" />
<TreeRings data={years} total={200} />
```

```tsx
<TreeRings data={[820, 1240, 990, 1810]} label="last" locale="de-DE" />
```

## Edge cases

```tsx
<TreeRings data={[8, 12, 0, 18, 22]} title="A flat quarter" />
```

```tsx
<TreeRings data={[]} title="New account" />
```


## Why this default

Stroke rings are the default because at twenty-four pixels hairlines keep the disc quiet and let the accent ring — the
current period, drawn 1.5× heavier in the accent color (weight and color, never color alone) — stay loud. The channel is
radial **thickness**, and the docs say so explicitly, because equal thickness at a larger radius spans more area.
TreeRings never enforces a minimum visual thickness: a near-zero period looks near-zero, and a zero-value period
collapses its two boundaries onto each other. Pass `total` for the cohort-age story — the disc then fills only
Σdata/total of the radius, so a young account reads as part-grown.

## Accessibility

The accessible name summarizes the growth — **"8 periods; latest 14, biggest 22 in period 5."** The interactive entry
steps the rings from the centre out with ←/→ (or hover), announcing each period as "Year 5: 22."

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[]` | Per-period growth, oldest first. |
| `highlight` | `"last" \| "none" \| number` | Which period's ring to pick out. |
| `total` | `number` | Expected lifetime Σ — the disc fills only Σdata/total. |
| `rings` | `"stroke" \| "fill"` | Boundary rings (default) or filled annuli. |
| `periodWord` | `string` | Singular period noun for the summary (default "period"). |
| `unit` | `string` | Plural period noun for the summary (default "periods"). |
| `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).
