# Slope (/docs/charts/slope)

Slope answers "who rose and who fell between two moments — and did the order change?". Lines stay neutral until you
declare `positive`: a rank change is not automatically good or bad. Both columns share one y-domain — per-column
normalization would fake convergence.

```tsx
import { Slope } from "@microcharts/react/slope";

<Slope data={[
  { label: "East", from: 40, to: 47 },
  { label: "West", from: 55, to: 41 },
  { label: "South", from: 30, to: 33 },
]} title="Before vs after" />
```

## Install

```tsx
import { Slope } from "@microcharts/react/slope";

<Slope data={cohorts} title="Before vs after" />
```

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** — before/after experiments, rank shuffles, two-moment comparisons.
- **Avoid for** — the path between the moments (Sparkline) or more than 7 categories.


## Variants

```tsx
const rows = [
  { label: "East", from: 40, to: 47 },
  { label: "West", from: 55, to: 41 },
  { label: "Mid", from: 20, to: 35 },
];

<Slope data={rows} label="both" />
<Slope data={rows} positive="up" highlight="West" />
```

## Edge cases

```tsx
// NaN for either side draws a dashed stub toward the missing end,
// announced "incomplete" — never interpolated
<Slope
  data={[
    { label: "East", from: 40, to: 47 },
    { label: "New", from: NaN, to: 41 },
  ]}
/>
```

```tsx
<Slope data={[{ label: "East", from: 40, to: 47 }]} />
```

```tsx
<Slope
  data={[
    { label: "East", from: 4000, to: 4700 },
    { label: "West", from: 5500, to: 4100 },
  ]}
  label="both"
  locale="de-DE"
/>
```

A row missing one side (`from` or `to` as `NaN`) draws a short dashed stub toward the end it does have, rather than a
full line — there's no second point to connect to, so nothing is interpolated. With a `locale`, both column labels and
the announced values follow that locale's own grouping.


## Why this default

A two-point line implies nothing about the path between — the docs steer to Sparkline when the journey matters. End
labels drop deterministically, never by measurement: when the rows are denser than the label font (height ÷ count), and
when the reserved label gutters would squeeze the two columns under ~35% of the width — in which case the reclaimed room
goes back to the lines. Endpoints that would collide inside a column are nudged apart to a full glyph pitch instead of
overlapping. A missing end renders a dashed stub and is announced "incomplete", never interpolated.

## Accessibility

The accessible name counts directions and leads with the biggest mover — **"3 categories: 2 up, 1 down. Largest change
Mid, up 75%."** The interactive entry finds the nearest line under the pointer and roves categories ordered by their
after-value, announcing each slope (**"East: 40 to 47, up 18%."**).

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) | `{ label; from; to }[]` | Two aligned moments per category. |
| `label` | `"none" \| "value" \| "label" \| "both"` | End labels; dropped deterministically when rows collide. |
| `highlight` | `number \| string` | The one-vs-field editorial read. |
| `positive` | `"up" \| "down"` | Direction valence; unset = neutral ink. |
| `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).
