Home/ Cookbook/ Charts/ Stacked area

Show a revenue mix with a stacked area chart

By the end of this recipe you'll have two-to-three components summing to a total over time — the silhouette gives you the total, the bands give you the mix.

Charts Intermediate ~12 min React · @corelithzw/react

Overview

Bands stacked on top of one another. The top edge is the total; each band is one component.

Stacked areas answer two questions at once: "how is the total trending?" and "what's the mix made of?". Reach for them when the components naturally add up — revenue by channel, costs by category, gold weight by purity. The total reads off the silhouette; the mix reads off the bands.

Skip stacked area if the components are independent (use a multi-line — stacking falsely implies they sum), or if there are more than four bands (the lower ones become unreadable). For two-to-three components with a clear total story, it's the cleanest chart in the kit.

Default opinion: the bottom band is the one your eye reads most accurately. Put your most important component there.

The chart

Revenue by channel — POS, online, and wholesale — summed to the silhouette across thirty days.

Revenue by channel, last 30 days Three stacked bands: POS, online, and wholesale. Total rises from $2.4k to $7.8k. $8k$6k$4k$2k$0 May 1May 8May 15May 22May 30
POS Online Wholesale
Total revenue rises from $2,400 to $7,800. POS is the largest, steady share. Online doubles. Wholesale adds the new top layer in week three.

Required pieces

Everything this recipe pulls from @corelithzw/react.

Intended exports used here: Chart.Area, Chart.Legend.

Roadmap. Chart.Area with stack ships in @corelithzw/react v0.2.

The React snippet

Set stack to add the series together rather than overlaying them.

RevenueMix.tsx@corelithzw/react

Customising

Three forks.

Switch to percent-stacked

If the question is "what's the share?" rather than "what's the total?", normalise each column to 100%.

<Chart.Area
  data={data}
  stack="percent"
  series={mySeries}
  yFormat={(v) => `${Math.round(v * 100)}%`}
  height={280}
/>

Show the total line

Overlay a stronger stroke along the top edge so the reader sees the silhouette as a real line, not just an edge.

<Chart.Area
  data={data}
  stack
  series={mySeries}
  totalLine             {/* draws the silhouette */}
  height={280}
/>

Reduce visual noise

Drop the gridlines and y-axis labels when the chart lives inside a dense dashboard — the silhouette alone tells the story.

<Chart.Area
  data={data}
  stack
  axes="x"
  grid="none"
  height={220}
/>

In context

Inside a revenue-mix card. The legend doubles as a "click to isolate" filter.

Revenue mix · May
Total $7,820

Accessibility

Bands are harder to read than lines — the labels do the heavy lifting.

  • Role and label. Root <svg> uses role="img" + aria-labelledby pointing to inline <title> + <desc>.
  • Each band has a name. Wrap each <path> in a <g aria-label="POS"> so the screen reader can announce it.
  • Screen-reader summary. A visually-hidden paragraph (.ck-sr-only) restates the start/end totals and per-channel share.
  • Pattern fills, not only colour. If you need accessible-by-default, add a low-opacity stripe pattern to one band so colour-blind readers see structure.
  • Keyboard. Each band is tabindex="0"; Arrow keys walk between data columns inside one band.