155Donut & rings
Parts of one whole — five parts at most, assembled from pathLength-100 arcs with a surface gap between segments. The center is the one place a chart carries its own headline number.
Donut with center stat
Segments in slot order from 12 o'clock, legend beside it, the total in the hole. Arcs sweep in on load.
Lessons by typethis course
42lessons
- Video · 21
- Reading · 11
- Lab · 6
- Quiz · 4
markup
<figure class="ns-chart">
<div class="ns-chart__head"><span class="ns-chart__title">Lessons by type</span><span class="ns-chart__sub">this course</span></div>
<div class="ns-chart__donut">
<div class="ns-chart__ring-wrap">
<svg class="ns-chart__ring ns-chart__svg" viewBox="0 0 36 36" role="img" aria-label="42 lessons: 21 video, 11 reading, 6 lab, 4 quiz">
<circle class="ns-chart__arc" data-c="1" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:50;--off:0"/>
<circle class="ns-chart__arc" data-c="2" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:26;--off:50"/>
<circle class="ns-chart__arc" data-c="3" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:14;--off:76"/>
<circle class="ns-chart__arc" data-c="4" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:10;--off:90"/>
</svg>
<div class="ns-chart__center"><strong>42</strong><span>lessons</span></div>
</div>
<ul class="ns-chart__legend" style="flex-direction:column;gap:var(--space-2)">
<li class="ns-chart__key" data-c="1"><span class="ns-chart__swatch"></span>Video · 21</li>
<li class="ns-chart__key" data-c="2"><span class="ns-chart__swatch"></span>Reading · 11</li>
<li class="ns-chart__key" data-c="3"><span class="ns-chart__swatch"></span>Lab · 6</li>
<li class="ns-chart__key" data-c="4"><span class="ns-chart__swatch"></span>Quiz · 4</li>
</ul>
</div>
</figure>Progress ring
One value against a track — the track is the grid gray, the fill is slot 1. This is the donut's single-series form, so no legend.
Course progress
64%
markup
<figure class="ns-chart ns-chart--sm" style="max-inline-size:14rem">
<div class="ns-chart__head"><span class="ns-chart__title">Course progress</span></div>
<div class="ns-chart__ring-wrap" style="inline-size:6rem">
<svg class="ns-chart__ring ns-chart__svg" viewBox="0 0 36 36" role="img" aria-label="64% complete">
<circle class="ns-chart__track" cx="18" cy="18" r="15.9"/>
<circle class="ns-chart__arc" data-c="1" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:65.5;--off:0"/>
</svg>
<div class="ns-chart__center"><strong>64%</strong></div>
</div>
</figure>Ring weights
Thin for inline/compact placements, default, thick where the ring is the hero. Weight is a modifier, never a new component.
thin · 72%
default
thick
markup
<div style="display:flex;gap:var(--gap-grid);flex-wrap:wrap">
<div class="ns-chart__ring-wrap ns-chart" style="inline-size:8rem;padding:var(--space-4)">
<svg class="ns-chart__ring ns-chart__svg" viewBox="0 0 36 36" aria-hidden="true">
<circle class="ns-chart__track ns-chart__arc--thin" cx="18" cy="18" r="15.9"/>
<circle class="ns-chart__arc ns-chart__arc--thin" data-c="1" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:73.5;--off:0"/>
</svg>
<div class="ns-chart__center"><span>thin · 72%</span></div>
</div>
<div class="ns-chart__ring-wrap ns-chart" style="inline-size:8rem;padding:var(--space-4)">
<svg class="ns-chart__ring ns-chart__svg" viewBox="0 0 36 36" aria-hidden="true">
<circle class="ns-chart__track" cx="18" cy="18" r="15.9"/>
<circle class="ns-chart__arc" data-c="1" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:73.5;--off:0"/>
</svg>
<div class="ns-chart__center"><span>default</span></div>
</div>
<div class="ns-chart__ring-wrap ns-chart" style="inline-size:8rem;padding:var(--space-4)">
<svg class="ns-chart__ring ns-chart__svg" viewBox="0 0 36 36" aria-hidden="true">
<circle class="ns-chart__track ns-chart__arc--thick" cx="18" cy="18" r="15.9"/>
<circle class="ns-chart__arc ns-chart__arc--thick" data-c="1" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:73.5;--off:0"/>
</svg>
<div class="ns-chart__center"><span>thick</span></div>
</div>
</div>Gauge (semi-donut)
Half a donut for a score against a scale. The unfilled half of the scale stays visible — a gauge with no track is just an arc.
Quiz average
71/ 100
markup
<figure class="ns-chart ns-chart--sm" style="max-inline-size:14rem">
<div class="ns-chart__head"><span class="ns-chart__title">Quiz average</span></div>
<div class="ns-chart__ring-wrap" style="inline-size:8rem">
<svg class="ns-chart__ring ns-chart__svg" viewBox="0 0 36 36" role="img" aria-label="Average score 71 out of 100" style="transform:rotate(180deg)">
<circle class="ns-chart__track" cx="18" cy="18" r="15.9" pathLength="100" stroke-dasharray="50 100"/>
<circle class="ns-chart__arc" data-c="1" cx="18" cy="18" r="15.9" pathLength="100" style="--seg:37;--off:0"/>
</svg>
<div class="ns-chart__center" style="place-content:end center;padding-block-end:var(--space-4)"><strong>71</strong><span>/ 100</span></div>
</div>
</figure>When NOT a donut
- More than five parts — a horizontal bar chart ranks them honestly
- Comparing two wholes side by side — humans cannot compare angles; use stacked bars
- Change over time — donuts have no time axis; use a stacked column