Donut Chart
Donut chart with the total in the middle and a legend with figures: segments grow clockwise one after the other when the ring appears.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Data has 28 effects, including 1 free. Explore the category →
3 usage examples



How it works
The data are the legend items: each li in ul.dnc-legend has a label and a data-value. The script adds up the values, draws an SVG in .dnc-ring with one circle per segment (radius 40, width --dnc-thickness, 24 by default) and gives each a proportional length: the ring always goes exactly all the way round, with a thin 1.5-unit gap between two segments so they never overlap.
In the middle, .dnc-total receives the sum formatted by Intl.NumberFormat in the page language: data-currency="USD" gives “$1,240” in English, data-currency="EUR" “1 240 €” in French; data-unit appends a unit (h, kg), data-decimals sets the number of decimals and data-compact shortens it (“12K”). Text written in data-total replaces the computed sum. If the total is too wide for the hole, its font size is reduced to fit, and recomputed when the ring changes size. Each legend row gets a swatch and its value in the same format.
Each segment starts in place with zero length. When 35% of the ring is visible, the dnc-on class gives the strokes their final length, with a 1 s transition (ease-out) offset by 0.15 s per segment: the ring fills piece by piece, once.
Accessibility
- What is read: the total and its label (“$1,240 Total”), then the legend, a list named by its
aria-labelwhose rows read “Housing $480”. The SVG is hidden from screen readers. - Reduced motion: under
prefers-reduced-motion: reduce, the transition is removed and the ring shows complete. - Hideable legend:
data-legend="hidden"removes the legend from the screen but not from screen readers. - Any background: the hole is truly empty (no painted disc), it shows your page background, light or dark; just adapt
--dnc-textand--dnc-muted.
Browser compatibility
SVG created by the script, CSS transition on stroke-dasharray, aspect-ratio, IntersectionObserver, ResizeObserver and Intl.NumberFormat (currencies, compact notation). Measured on 8 Nov 2026 in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): ring filled to 100% (480/320/140/300), two independent copies, reduced motion, 390 px, no errors.
Without IntersectionObserver, the ring fills on load. Without JavaScript, what remains is the legend (labels only) and the word “Total”.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0231-root" data-currency="USD">
<div class="dnc-ring">
<div class="dnc-center">
<div class="dnc-total"></div>
<div class="dnc-label">Total</div>
</div>
</div>
<ul class="dnc-legend" aria-label="Spending this month">
<li data-value="480">Housing</li>
<li data-value="320">Groceries</li>
<li data-value="140">Energy</li>
<li data-value="300">Going out</li>
</ul>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Segments (HTML — li data-value="…" in .dnc-legend) |
Housing 480, Groceries 320, Energy 140, Going out 300 | One row per segment; the label is the li text, the value can be in any unit. |
Number format (HTML — data-currency or data-unit on the root) |
data-currency="USD" (EN page), "EUR" (FR page) |
ISO currency code for a localised amount, or a free unit appended after the number. |
Decimals and shortening (HTML — data-decimals, data-compact) |
0 for a currency, 1 otherwise; no shortening | data-decimals="2" for cents; data-compact writes “12K” instead of “12,000”. |
Centre text (HTML — data-total on the root, text of .dnc-label) |
Computed sum, “Total” | data-total forces the displayed text (e.g. an average); change the label in the HTML. |
Legend (HTML — data-legend="hidden") |
Visible | Hidden on screen, still read out. |
Ring (CSS — --dnc-size, --dnc-thickness, --dnc-total-size) |
180 px, 24 (out of 120), 1.5rem | Maximum diameter, thickness in drawing units, starting size of the total (shrunk automatically if needed). |
Colours (CSS — --dnc-c1 to --dnc-c6) |
#6366f1, #8b5cf6, #d946ef, #ec4899, #f43f5e, #06b6d4 | One colour per segment, in legend order, repeating after six. |
Background and text (CSS — --dnc-bg, --dnc-text, --dnc-muted, --dnc-min-height) |
#0a0a0f, white, 50% white, 300 px | Root background, main text, secondary text and minimum height. |
FAQ
data-compact (“$128K”).--dnc-bg: transparent and darken --dnc-text and --dnc-muted. The centre is not painted, so no black disc is left in the middle.