Skip to content
microcharts
ReferenceDecisionABStrips

ABStrips

React A/B strips chart — two graded quantile strips on one scale so A/B overlap is visible at a glance. Tiny accessible SVG microchart; RSC-safe static or /interactive.

ABStrips answers "did B beat A — and by more than the overlap?". It 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. The visible overlap of the two middle halves is the answer — and the overlap number is always in the summary, because an average delta without its spread is how A/B results lie.

ABStrips
interactive · 5.71 kB · static · 3.09 kB

Install

Import & usereact/ab-strips
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" />
Needs package + stylesheetSet up with AI

Try it

When to use it

  • Good for — an A/B experiment result in a KPI card, control vs test in an experiments table, any two-sample comparison where the spread matters.
  • Avoid for — a single distribution (BenchmarkStrip) or more than two arms (small multiples).

Sizing

Variants

labelled arms + clearly separated

Edge cases

no data
small sample

Either arm with zero finite values makes the whole comparison unanswerable — no strips draw, and the accessible name reads plainly "No data." rather than a comparison against nothing. An arm with fewer than 8 points still draws (a small sample is still the honest sample you have); only its outer band changes meaning, from the 5th–95th percentile to the plain min–max, because percentile estimates need more than a handful of points to mean anything.

Four homes

Why this default

Overlap in the summary, because an average delta without spread is how A/B results lie. The strips are mandatory context — it never degrades to a bare mean bar — and the delta label never appears without the distributions behind it. When the middle halves fully overlap the summary says "no clear difference"; when they're disjoint it says "clearly separated"; and an arm with fewer than 8 points falls back to a min–max band.

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%.". 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

PropTypeNotes
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.
animatebooleaninteractiveOpt-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.

Related charts