Charts
FunnelChart
Stages that only ever narrow.
Usage
When to reach for it
Ships from@misoto22/design/charts
import { FunnelChart } from '@misoto22/design/charts'Notes
Stages that only ever narrow — a signup flow, a hiring pipeline, a checkout.
The caveat is the same one every funnel has: the taper encodes a RATIO between neighbouring stages, and the eye reads the enclosed area, so a funnel exaggerates a shallow drop and flattens a steep one. Where the exact fall-off is the point, put the percentages on the stages — <Funnel.Label> does — or use a <BarChart>, which encodes each stage on one honest scale.
Reach for <SankeyChart> instead when the flow can SPLIT rather than only shrink: a funnel has one path through it by construction.
Anatomy
| Element | Description |
|---|---|
| Figure framerequired | ChartFigure’s <figure>, with an empty state at zero stages. There is still no loading skeleton, so that state remains the call site’s job. |
| Stagesrequired | <FunnelChart.Funnel>. The rows are drawn in the order given and never sorted, and each stage is cut from the next by a --chart-surface stroke gap pixels wide rather than by a transparent gap, so the stages still touch. |
| Stage labels | <FunnelChart.Label>, positioned right by default. Its dataKey defaults to the NAME field, not the value — printing the numbers means pointing it at the value key yourself. |
| Tooltip | <FunnelChart.Tooltip>, heading suppressed and cursor off. There is no axis here for a crosshair to run along. |
| Hidden data table | The sr-only table, one row per stage. It is the only exact reading this form offers, because a funnel has no axis and no ticks anywhere on it. |
Best practices
Do
- Point <FunnelChart.Label> at the value field. With no dataKey it prints the stage NAME, which the reader already has, and the number — the one thing the taper cannot be read for — goes unprinted.
- Order the rows widest first yourself. The component draws them in the order it is handed and does not sort, so a stage out of place renders a funnel that widens, which a reader will read as a data error.
- Keep variant="stepped" unless the stages are also a sequence of kinds. ramp walks the series ramp stage by stage, which encodes the drop a second time when the taper has already said it.
Don’t
- Do not use it for a flow that splits. A funnel has one path through it by construction; where a stage divides into two outcomes the honest form is a SankeyChart, which can draw both branches.
- Do not read the fall-off off the shape. The taper is a ratio between neighbours and the eye reads the enclosed AREA, so a shallow drop is exaggerated and a steep one flattened, with no axis anywhere to check it against.
Examples
default
The default funnel: four stages, widest first, each with its name printed beside it. The taper encodes a ratio between neighbouring stages and the eye reads the enclosed area, so a funnel exaggerates a shallow drop and flattens a steep one — the printed label is the relief for that, and where the exact fall-off is the point a BarChart puts every stage on one honest scale instead. Order is the funnel and the data is never sorted for you, so the widest stage has to be first.
| stage | Value |
|---|---|
| Visited | 4,200 |
| Signed up | 1,800 |
| Activated | 900 |
| Paid | 320 |
variant
stepped is the honest default: every stage keeps the same fill and the taper alone carries the drop. ramp darkens each stage as well, which encodes the same fact twice and invites a reader to compare two things that are one — the toggle is here to make that visible, not to offer the two as equals. The label prints people at the centre of each stage rather than the stage's name, since the number is exactly what the taper cannot be trusted to convey; the stage's name is what the tooltip carries.
| stage | Value |
|---|---|
| Visited | 4,200 |
| Signed up | 1,800 |
| Activated | 900 |
| Paid | 320 |
stage to stage rates
Two labels on the same funnel: the stage's name inside it, and the share it kept of the stage above it out to the right. The taper is a ratio the eye reads as area, so it exaggerates a shallow drop and flattens a steep one, and a printed rate is the only thing that makes the fall-off exact. The steepest drop here is activations to paid, where barely a third carries through, and it is the one the shape says least about because it happens where the funnel is already narrow. Each rate is a share of the stage above it rather than of the top, because those are two different numbers that look alike, and a funnel that does not say which one it is printing gets read as whichever flatters it.
| stage | Value |
|---|---|
| Visited | 4,200 |
| Signed up | 1,800 |
| Activated | 900 |
| Paid | 320 |
Types
export type FunnelVariant = 'stepped' | 'ramp'Accessibility
- title is required; the stages are also rendered as a visually hidden table.
- The taper encodes a ratio between neighbouring stages and the eye reads the enclosed area, so a funnel exaggerates a shallow drop. Compose a Label to print the numbers — that is the relief.
- The default variant holds one fill for every stage and lets the shape carry the drop. A ramp that also darkens each stage encodes the same fact twice.