# BiasStrip (/docs/charts/bias-strip)

BiasStrip answers "do two ways of measuring the same thing systematically disagree?" — the agreement story a scatter
can't tell. Each dot is one pair placed at its mean and its difference; the dashed line is perfect agreement, the accent
line is the measured bias, and the faint band is the limits of agreement. Dots render at 75% opacity so overplot reads
as density, and pairs beyond the limits are enlarged and re-inked so outliers read on shape, not color alone.

```tsx
<span className="text-lg">
  {"Device and reference "}
  <BiasStrip
    data={Array.from({ length: 20 }, (_, i) => ({
      a: i + [1.8, 2.4, 1.5, 2.9, 2.1, 1.2, 2.6, 3.0, 1.9, 2.3, 6.5, 2.0, 1.7, 2.8, 2.2, -1.5, 2.5, 1.6, 2.7, 2.0][i],
      b: i,
    }))}
    title="Device vs reference"
    style={{ width: "3.4em", height: "1.9em" }}
  />
  {" agree, +2 bias."}
</span>
```

## Install

```tsx
import { BiasStrip } from "@microcharts/react/bias-strip";

<BiasStrip data={pairs} title="Device vs reference" />
```

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** — checking whether a new instrument, method, or model agrees with a reference; instrument drift in a KPI
  card.
- **Avoid for** — unpaired samples (use MicroScatter) or a single time series (Sparkline).

A correlation plot answers "do these move together?" — but two methods can correlate perfectly and still disagree by a
constant offset. BiasStrip is built for that offset: it plots the difference, so a bias jumps off the zero line where a
scatter would hide it on the diagonal.


## Variants

```tsx
// widen the band from ~95% (1.96σ) to ~99% (2.58σ)
<BiasStrip data={pairs} limits={2.58} />
```

```tsx
<BiasStrip data={pairs} label="none" />
```

```tsx
<BiasStrip data={pairs} r={2.5} />
```

## Edge cases

```tsx
<BiasStrip data={[]} title="No pairs" />
```

```tsx
<BiasStrip data={[{ a: 2, b: 0 }, { a: 3, b: 1 }, { a: 4, b: 2 }, { a: 5, b: 3 }]} />
```

```tsx
<BiasStrip data={[{ a: 20, b: 20 }, { a: 21, b: 21 }, { a: 22, b: 22 }, { a: 23, b: 23 }, { a: 24, b: 24 }, { a: 25, b: 25 }]} />
```

Empty data draws just the frame with "No data." as the summary. Under 5 pairs the limits aren't meaningful, so the band
and bias line drop out and only the dots and the zero reference remain — the mean difference is still stated in the
summary. When the two methods agree perfectly the band collapses to a hair around the zero line rather than vanishing,
so "nothing to see" still reads as a deliberate, measured result. Pairs with a non-finite value are dropped before the
stats and the count; past 40 pairs the dots are uniformly downsampled for display while the bias and limits still use
every pair.


## Why this default

The y-axis is symmetric about zero so perfect agreement is always the visual center: a systematic offset then reads as
the whole cloud drifting up or down, which is exactly the decision the chart exists to surface. The band is the classic
±1.96σ limits of agreement, and `limits` widens or tightens it (2.58 for ~99%) without ever redefining what the marks
mean. Under five pairs the chart makes no interval claim at all — too little evidence to draw one honestly.

## Accessibility

The accessible name is the bias plus the evidence-backed agreement share — **"Bias +2.21 across 20 pairs; 90% within the
limits of agreement."** — or the shorter **"Bias +2 across 4 pairs."** when there are too few pairs to draw limits.
Localizing carries the number format through: with `locale="de-DE"` the same chart reads **"Bias +2,21 across 20 pairs;
90% within the limits of agreement."** The interactive entry steps pairs ordered by mean and announces each pair's mean,
difference, and whether it clears the limits.

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) | `{ a; b }[]` | Paired measurements. |
| `limits` | `number` | k in bias ± k·σ (default 1.96 ≈ 95% limits of agreement). |
| `label` | `"bias" \| "none"` | Seat-gated bias caption (default) or hidden. |
| `r` | `number` | Base dot radius, clamped [1, 3]. |
| `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).
