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

A new device tracks its reference closely and still reads about 2 units high. BiasStrip plots that offset: 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. The y-axis is symmetric about zero, so a systematic offset reads as
the whole cloud sitting above or below the line. 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.

## Try it

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

<BiasStrip
  data={pairs}
/>
```

## When to use it

Use it to check whether a new instrument, method, or model agrees with a reference, and for instrument drift in a KPI
card. For unpaired samples use MicroScatter; for a single time series use Sparkline.

Two methods can correlate perfectly and still disagree by a constant offset. BiasStrip plots the difference, so that
offset shows up as distance from the zero line where a correlation plot would hide it along the diagonal.

## Sizing

**in a sentence**

```tsx
device and reference <BiasStrip data={pairs}
  style={{ width: "3em", height: "1.6em" }} /> agree, +2 bias
```

**99% limits of agreement**

```tsx
<BiasStrip data={pairs} limits={2.58} />
```

## Variants

The band is the classic ±1.96σ limits of agreement. `limits` widens or tightens it (2.58 for about 99%) without changing
what any mark means.

```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 chart makes no interval claim at that sample
size, and 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 the measured result stays visible. 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.

## Four homes

**In a sentence**

```tsx
<p>
  Device vs reference{" "}
  <span className="mc-inline">
    <BiasStrip data={pairs} width={90} height={24} summary={false} />
  </span>{" "}
  — +2 bias, 18 of 20 within limits.
</p>
```

**In a table cell**

```tsx
<td>
  <BiasStrip data={pairs} width={72} height={28} />
</td>
```

**In a KPI card**

```tsx
<div className="kpi">
  <span className="figure">+2.0</span>
  <BiasStrip data={pairs} limits={2.58} width={200} height={48} />
</div>
```

**In a tab header**

```tsx
<button className="tab">
  Sensor A <BiasStrip data={pairs} label="none" width={44} height={22} />
</button>
```

## 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, press `Escape`, or
press outside the chart. 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).
