# Constellation (/docs/charts/constellation)

Constellation answers "when did the rare events happen, and how big were they?" Each event is a dot placed by time (x)
and value (y), with an optional magnitude driving its area-true size. A hairline line connects the events in time order,
so the sequence of a handful of incidents reads at a glance. It is built for _rare_ events — a dozen or fewer; dense
streams belong to `Seismogram` or `EventTimeline`.

```tsx
import { Constellation } from "@microcharts/react/constellation";

const month = (x) => ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep"][x];

<Constellation
  data={[
    { x: 0, y: 40, m: 2 },
    { x: 2, y: 90, m: 7 },
    { x: 5, y: 30, m: 3 },
    { x: 8, y: 65, m: 5 },
  ]}
  xFormat={month}
  title="Incidents"
/>
```

## Install

```tsx
import { Constellation } from "@microcharts/react/constellation";

<Constellation
  data={[
    { x: 0, y: 40, m: 2 },
    { x: 2, y: 90, m: 7 },
    { x: 5, y: 30, m: 3 },
    { x: 8, y: 65, m: 5 },
  ]}
  title="Incidents"
/>
```

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** — sparse incidents or outages on a timeline, milestones carrying a magnitude, or any rare events where
  the sequence is the story.
- **Avoid for** — dense event streams (`Seismogram`, `EventTimeline`), a continuous trend (`Sparkline`), or precise
  value comparison (dot area is a low-precision channel).


## Variants

```tsx
<Constellation data={events} label="max" />
<Constellation data={events} connect={false} />
```

## Edge cases

```tsx
<Constellation data={[]} title="No incidents" />
```

```tsx
<Constellation data={[{ x: 3, y: 40 }]} title="One incident" />
```

```tsx
<Constellation data={[{ x: 0 }, { x: 2 }, { x: 5 }, { x: 8 }]} title="No values" />
```

Empty data draws no marks at all — just the sized box — with "No data." as the summary. A single event has no line to
draw — just one dot, and the summary says "1 event at …" rather than forcing a range. When every point omits `y`, the
vertical spread is deterministic jitter for legibility only — the summary and the interactive readout never mention it,
and the connector's slope carries no meaning in that mode.


## Why this default

The chronology line is on by default because the sequence is usually the story of rare events — you want to see the
order and rhythm, not just a scatter. Dot size is _area-true_ (`r ∝ √m`), because area is how the eye reads magnitude;
it is a deliberately low-precision channel, so the number lives in the summary and the hover readout, not in a size you
are asked to measure.

When no event carries a value, the vertical position is **deterministic jitter that encodes nothing** — it only spreads
the dots so they do not stack. In that case the connector's slope is meaningless (it still runs in time order), and the
summary never mentions vertical position. Time is sacred: the x position is never jittered, so simultaneous events sit
at the same x.

## Accessibility

The accessible name states the count, the span, and the largest event — **"4 events between 0 and 8; largest at 8."**
The interactive entry lets you arrow through the events chronologically, each announced with its time, value, and
magnitude through a polite live region; a hover readout shows the same. Magnitude is redundant with the dot size, never
carried by it alone.

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) | `{ x: number; y?: number; m?: number }[]` | Events: x = time, y = value, m = magnitude (area-true size). |
| `connect` | `boolean` | The faint chronology line (default true). |
| `label` | `"max" \| "none"` | Numeral at the largest event. |
| `xFormat` | `(x: number) => string` | Formats time for the summary (e.g. a month name). |
| `xDomain` | `[number, number]` | Time (x) extent (default: data extent). |
| `rBase` | `number` | Base dot radius in viewBox units (default 1.6). |
| `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).
