CHARTS & MAPS · CHARTS
Pie & Donut
Parts of a whole in plain SVG — pie, donut and gauge; ring, gaps, rounded ends, “Other” grouping, centre value, legend sync, nested rings, KPI donuts and period comparison.THEME
01
Playground
Revenue mix — type, ring, gaps, ends, sort, Other, centre, legend, stateTYPE
RING
GAP
ENDS
SORT
SMALL
CENTER
LEGEND
STATE
- Total$347k100%
NOW SPEAKING
CHART“Revenue by service 2026, 7 segments. Largest Web 27%, Branding 26%, Packaging 16%. Use arrow keys to read each segment.”
SLICE“Focus the chart and press → to read each segment”
TABLE“Table alternative: 7 rows — segment, value, share”
RevenueMix.tsx
1<DonutChart
2 data={revenueByService}
3 type="donut" thickness="mid"
4 padAngle="sm" cornerRadius="square"
5 sort="size" groupBelow={0.05}
6 center="sel" legend="right"
7 keyboard tableFallback
8/>
EVENT LOG
02
Variants
Select, hide, gauge, nested, KPI donuts, two periods, tableHover & selectsegment pops out · centre shows it · ← → Enter
- Total$327k100%
Hide from the legendshares recalculate to 100%
- Total$327k100%
Gaugebudget used · zones · target
Nested donutcategories inside, clients outside
- Total$347k100%
KPI donutsone value against its remainder
Two periodsQ3 vs Q4 · change in points
Q3 2026
Q4 2026
Web28%29%+1.4 pt
Branding30%27%−3.2 pt
Packaging16%18%+1.3 pt
Photo13%8.2%−4.3 pt
Motion8.8%11%+2.6 pt
Print5.0%7.2%+2.2 pt
Chart or tablesame shares, either way
- Total$327k100%
03
States
Loading, empty, 100%, error, too many slicesLoadinggrey ring keeps the layout
Loading…
Emptydashed ring, explain why
No revenue yetNothing invoiced in this period.
One segment100% is a fact, not a chart
- Total$89k100%
Errorkeep the frame, offer Retry
Couldn’t loadThe data service didn’t respond.
Too many slices12 segments — bars read better
04
Behavior
When to use, order, colour, labelsWhen it fitsparts of one whole
Use2–6 parts that add up to 100%
Question“What share is Web?”, not “which is bigger?”
AvoidComparing similar sizes — use bars
TimeNever for change over time
Ordermake it scannable
Start12 o’clock, clockwise
SortLargest first; “Other” always last
GroupMerge anything under 5% into Other
HighlightPop out one slice, dim the rest
Colournot colour alone
PaletteMax 6 distinct hues + grey for Other
LegendName, value and % on every row
LabelsOn chart only for slices ≥ 6%
Gaps2–3 px gaps help separate similar colours
Centre & sizedonut specifics
CentreTotal by default, the hovered slice on hover
RingThin for KPIs, medium for mixes
GaugeZones + target tick, value as text
Minimum160 px — below that, show the number
05
In Avava
Clients, invoice status, utilizationRevenue by clientQ4 · total in the centre
- Total$227k100%
Invoice statuscount in the centre · amount in the legend
Studio utilizationbillable vs available hours
06
Rules & a11y
Pie vs bar, screen readersPie, donut or bar?pick by the question
Donut
One mix, 2–6 parts, total mattersRevenue by client
Pie
Same, when the total is not shownShare of hours
Gauge
One value against a limitBudget used, utilization
Bar
Comparing sizes or > 6 partsRevenue by 12 services
Screen readersevery slice is a number
✓role="group" aria-roledescription="donut chart"aria-label lists the three largest shares
✓← → through slices, Enter pins, Esc clearsEach slice is announced: “Web: $94k, 27% of total”
✓Legend rows are buttonsaria-pressed shows visible / hidden
✓Table view of the same dataSegment, value, share
✓SVG is aria-hiddenThe label, live region and table carry the meaning
DO✓Keep to 2–6 slices, group the rest into Other✓Sort largest first from 12 o’clock✓Put the total in the donut’s centre✓Show value and % in the legend✓Use a gauge for one value against a target
DON’T✕Use pie charts for change over time✕Compare two pies by eye — show the difference✕Use 3D or exploded pies✕Label slices under 6% on the chart✕Rely on colour alone to tell slices apart