Compare metrics with a multi-series line chart
By the end of this recipe you'll have two-to-four lines on a shared axis — colour-coded, legend-tagged, and honest about which series you intend the reader to compare against which.
Overview
Two-to-four ordered series on the same time axis. The reader compares slopes; you pick the colours; the legend names the lines.
A multi-series line is for when the story is comparison. Revenue this year vs. last; signups by plan; gold-price vs. scrap-price. The shared axis is what makes the comparison legitimate — if your series don't share the y-unit, you don't have a multi-line, you have two charts pretending to be one.
Skip multi-line above 5 series — the colours collide and the eye gives up. Switch to small multiples: a 2×2 grid of single lines beats a five-colour spaghetti every time.
Default opinion: two series share one axis; three is the upper bound for a single panel. Beyond that, pick a different chart.
The chart
Revenue, costs and refunds across one month. Solid for healthy series, dashed for the one you'd rather was lower.
Required pieces
Everything this recipe pulls from @corelithzw/react.
Intended exports used here: Chart.Line, Chart.Legend, Chart.Series.
Chart.Series ships in @corelithzw/react v0.2. Until then, render the SVG inline or use a headless lib — the API shape below is what we're committing to.
The React snippet
Pass an array of series. Each series sets its own colour and an optional dashed style.
Customising
Three forks — colours, axes, animation.
Pick safer palettes
Above 3 series, switch from semantic tones to a categorical palette so colours don't carry meaning the data doesn't.
<Chart.Line
data={data}
palette="categorical" {/* brand-rotated */}
series={mySeries}
height={280}
/>
Pin and align the axes
If you redraw on filter changes, pin the y-domain so the line doesn't jump around between renders.
<Chart.Line
data={data}
yDomain={[0, 5000]}
xTicks={6}
legend="bottom"
height={280}
/>
Stagger the draw
Animate each series 80ms after the last so the reader's eye follows them in order — most-important first.
<Chart.Line
data={data}
series={mySeries}
animate="draw"
animateStaggerMs={80}
height={280}
/>
In context
Drop into a "compare two periods" report card. The legend doubles as the period picker.
Accessibility
Multi-series adds one risk: telling the lines apart without colour.
- Role and label. Root
<svg>usesrole="img"with anaria-labelledbypointing to inline<title>+<desc>that name every series. - Pattern, not only colour. Dashed strokes carry a second signal (here, "refunds is the dashed one"). Pair every colour with a legend entry that repeats the line style.
- Screen-reader summary. A visually-hidden paragraph (
.ck-sr-only) reads out each series' start, end and direction in one sentence. - Series-level navigation. Each series is a
<g tabindex="0">with anaria-labellike "Revenue: 1,800 to 4,800" — Tab walks between series, Arrow keys walk between points within one. - Reduced motion.
prefers-reduced-motion: reducedisables the stagger; all series render at final state.