ABStrips
Two graded quantile strips on one scale, so you can see whether the A and B middle halves still overlap.
- encodes
- two graded quantile strips on one scale; the overlap is the answer
- precision
- medium — interval read; the delta label restores a number
- nodes
- ≤ 10
ABStrips takes two samples of raw values, a control arm and a test arm, and draws two graded quantile strips on one shared scale: a faint p5–95 band, a stronger p25–75 middle half, and a median dot, with row A muted and row B accent. Whether the two middle halves still overlap is the read: while they do, the gap between the medians isn't a result yet. The overlap number is always in the summary, because a delta without its spread doesn't say whether the two arms differ. The strips are mandatory context: there is no bare-mean-bar mode, and the delta label never renders without the distributions behind it.
Install
import { ABStrips } from "@microcharts/react/ab-strips";const control = Array.from({ length: 80 }, (_, i) => 130 + ((i * 13) % 44) - 22);const test = Array.from({ length: 80 }, (_, i) => 116 + ((i * 13) % 44) - 22);<ABStrips data={{ a: control, b: test }} positive="down" title="Latency A/B" />Try it
When to use it
Use it for an A/B experiment result in a KPI card, control vs test in an experiments table, and any two-sample comparison where the spread matters. For a single distribution use BenchmarkStrip; for more than two arms use small multiples.
Sizing
Variants
Edge cases
Either arm with zero finite values makes the comparison unanswerable: no strips draw, and the accessible name reads "No data." An arm with fewer than 8 points still draws. Only its outer band changes meaning, from the 5th–95th percentile to the plain min–max, because tail percentiles need more than a handful of points to estimate.
Four homes
Accessibility
The accessible name states both medians, the delta, and the overlap — "Test median 115 ms vs Ctrl 129 ms (-11%); middle halves overlap 35%.". When the middle halves fully overlap the summary says "no clear difference"; when they are disjoint it says "clearly separated". The interactive entry roves the rows (↑/↓) and the quantile edges (←/→): the median announces the delta vs the other arm, other edges announce the percentile.
The interactive entry follows the shared 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 | Notes |
|---|---|---|
| data* | { a: number[]; b: number[] } | The two arms — raw samples, not summaries. |
| seriesLabels | [string, string] | Row identities for the gutter tags + summary (default ['A', 'B']). |
| positive | "up" | "down" | Which direction of the B−A delta reads as good (colors the delta). |
| label | "delta" | "none" | Signed median delta in a right gutter. |
| animate | boolean | interactiveOpt-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.