Overview
Pie and donut charts are tempting to ship as pure SVG, but a chart is data first and decoration second. This pattern uses a conic-gradient donut for the ring (zero SVG, zero JS for the math) paired with a real <ol><li> legend so screen readers and copy-paste users see the actual numbers, not just the colored slices. The chart and the legend stay synchronized because both read from the same data source.
When to use it
Reach for the conic-gradient + legend approach when you have three to six slices of comparable size and the data is categorical (browser share, plan tier breakdown, traffic sources). Skip it for time-series data — line and bar charts read the change better. Skip it for more than six slices — the eye loses track of which color maps to which legend row past that, and a horizontal stacked bar becomes easier to scan.
How it works
Build the donut by setting background: conic-gradient(slice1 0deg X, slice2 X Y, ...) on a circular element, then carve out the centre with a smaller pseudo-element using the surface background color so a thin ring remains. Add border-radius: 50% to the wrapper so the slices clip to a circle instead of a square. The legend is a real <ol> with each <li> wrapping the swatch and the label; nothing about the legend needs the chart to render correctly, and vice versa. The DOM contract is: data drives both surfaces independently.
Production gotchas
conic-gradient stops accept degrees, percentages, or a mix — mix them and Chromium and Firefox interpolate the stops differently. Pick one unit and stay with it across every slice. If you animate the gradient via custom property interpolation, register the property with @property and syntax: "<angle>" or the browser will discrete-step the change. Donut hole punched with a pseudo-element is solid color — if the parent background is a gradient or image, switch the pseudo to backdrop-filter: blur(0) + appropriate masking, or the hole will look fake.
Accessibility
The donut is aria-hidden="true" — it is a decorative repeat of the legend. The <ol> is the data; screen-reader users hear the labels and percentages in order. If you animate the slice sizes (e.g. on data refresh), the visual transition is fine because the data itself is not animated — only its rendering. prefers-reduced-motion: reduce jumps directly to the new chart state rather than interpolating.
References
Implementation depth
The chart is decoration; the ordered legend is the data. conic-gradient can draw the donut quickly, but the labels, values, and reading order need to live in semantic markup that survives if the visual ring fails.
Keep slice counts low and units consistent. Mixing degrees and percentages makes stop maintenance harder, while too many slices turns the legend into the real chart anyway. In those cases, use a bar or table instead.