# Hypnogram (/docs/charts/hypnogram)

Hypnogram answers "which discrete state was the system in over time, and how choppy were the transitions?". It refuses
interpolation — there are no diagonals, ever — because a state is a fact that holds until the next one, not a sample of
a continuum a line chart can slope between.

```tsx
import { Hypnogram } from "@microcharts/react/hypnogram";

<Hypnogram
  data={[
    { t: 0, state: "Awake" },
    { t: 8, state: "Light" },
    { t: 22, state: "Deep" },
    { t: 38, state: "Light" },
    { t: 50, state: "REM" },
    { t: 62, state: "Light" },
    { t: 74, state: "Deep" },
    { t: 86, state: "Light" },
    { t: 98, state: "REM" },
    { t: 110, state: "Awake" },
  ]}
  states={["Awake", "REM", "Light", "Deep"]}
  title="Sleep stages"
/>
```

## Install

```tsx
import { Hypnogram } from "@microcharts/react/hypnogram";

<Hypnogram data={sleep} states={["Awake","REM","Light","Deep"]} title="Sleep stages" />
```

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** — sleep stages, deploy / machine / incident state over time.
- **Avoid for** — continuous signals (Sparkline) or a single current state (StatusDot).


## Variants

```tsx
<Hypnogram
  data={[
    { t: 0, state: "Awake" },
    { t: 30, state: "REM" },
    { t: 60, state: "Deep" },
    { t: 90, state: "Light" },
  ]}
  mode="lanes"
/>
```

```tsx
<Hypnogram
  data={[
    { t: 0, state: "Awake" },
    { t: 8, state: "Light" },
    { t: 22, state: "Deep" },
    { t: 38, state: "Light" },
    { t: 74, state: "Deep" },
    { t: 110, state: "Awake" },
  ]}
  emphasis="Deep"
/>
```

## Edge cases

```tsx
<Hypnogram data={[{ t: 0, state: "Deep" }]} domain={[0, 120]} />
```

```tsx
// "Groggy" isn't in `states` — it's appended as its own row, not dropped
<Hypnogram
  data={[
    { t: 0, state: "Awake" },
    { t: 20, state: "Groggy" },
    { t: 40, state: "Light" },
  ]}
  states={["Awake", "Light", "Deep"]}
/>
```

A single entry holds its state across the whole domain — one flat run, and the summary names it directly — **"1 state,
no transitions; Deep throughout."** — rather than describing a one-point chart. A state present in the data but missing
from an explicit `states` order is never dropped: it's appended as its own row (with a dev-only console warning), so the
strip always accounts for every state it's given.


## Why this default

Right-angle steps with an explicit `states` order are the clinically proven read. Order is meaningful — for ordinal
states (sleep depth, incident severity) pass it explicitly; for nominal states with no rank, use `mode="lanes"` so the
vertical axis never implies one. Any smoothing or easing of the step corners would be a lie about the data, so the
entrance is a left-to-right clip reveal of the finished trace — the steps themselves are never interpolated.

## Accessibility

The accessible name summarises the shape — **"3 transitions across 4 states; longest run Awake."** The interactive entry
roves the runs; each announces its state and span (**"Light, from 8 to 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) | `{ t, state }[]` | State holds from t to the next entry. |
| `states` | `string[]` | Row order top→bottom; ordinal semantics live here. |
| `emphasis` | `string` | Accents one state — the decision read. |
| `mode` | `"steps" \| "lanes"` | Lanes for nominal states with no rank. |
| `connectors` | `boolean` | Vertical transition strokes (default true); off for ultra-dense strips. |
| `labels` | `boolean` | Left-gutter state names (default: on when width ≥ 96). |
| `colors` | `string[]` | Per-state lane colours (lanes mode), cycled; overrides --mc-cat-N. |
| `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).
