Charts
RadialChart
Values on an arc — a gauge, or a few totals against one scale.
Usage
When to reach for it
Ships from@misoto22/design/charts
import { RadialChart } from '@misoto22/design/charts'Notes
Values on an arc — a gauge, or a small set of totals against one scale.
The caveat worth knowing before reaching for it: a radial bar's LENGTH is its value, but its RADIUS is not, so an inner bar and an outer bar of the same value are drawn different lengths. That makes it a poor comparison and a good single-value gauge; past about four bars, a <BarChart> is the honest choice.
Anatomy
| Element | Description |
|---|---|
| Figure framerequired | ChartFigure’s <figure>, with an empty state at zero rows. Its hidden table needs a value field, which it takes from valueKey or, failing that, from the dataKey of the composed <RadialChart.RadialBar> — so a chart that names neither still has no table. |
| Arcsrequired | <RadialChart.RadialBar>, one per row, 14px thick with a 5px cap. variant="semi" drops the centre to 70% so a half arc sits in the middle of its own box rather than at the top of it. |
| Track | showTrack, on by default, painting the unfilled remainder behind each arc in --chart-track. It is what makes a gauge a gauge: without it there is no visible whole for the fill to be a part of. |
| Scale | The PolarAngleAxis the root inserts when max is set, with domain [0, max] and its ticks off. Leave max unset and the domain is taken from the data instead. |
| Legend | <RadialChart.Legend>. An arc has no category axis, so above one bar this is the only thing naming them. |
Best practices
Do
- Set max on anything that is a gauge. Without it the scale comes from the data, so the largest bar always fills the arc and 62% and 98% are drawn identically.
- Pass valueKey when the arc is not the only mark. It names the field the hidden table prints and the field the LEGEND reports a selection from; without either it or a composed <RadialChart.RadialBar> there is no value field, and the table is not empty but absent.
- Use variant="semi" for a single value. A half arc reads as a dial with a floor and a ceiling, where a full ring asks the reader to work out what a whole circle was worth.
Don’t
- Do not compare bars across radii. A radial bar’s LENGTH is its value but its RADIUS is not, so an inner arc and an outer arc holding the same number are drawn different lengths — past about four bars a BarChart is the honest form.
- Do not let a reader take an arc as a share of the ring when max is unset. A full sweep then means the biggest thing here, which is a different sentence from all of it.
Examples
default
Three totals on one arc with no max, so the scale comes from the data and the largest bar always fills the ring. The caveat is geometric: a bar's LENGTH is its value but its RADIUS is not, so an inner bar and an outer bar holding the same number are drawn at different lengths, and the ring flatters whatever sits outermost. That makes this a display for three or four values a reader will glance at, and a BarChart the honest choice for anything being ranked. The legend does the naming, since the arcs carry no axis of their own.
| tier | Value |
|---|---|
| documents | 72 |
| photos | 54 |
| backups | 31 |
gauge
One value against a fixed total — the case a radial chart is unambiguously right for. max is what makes it a gauge: without it the scale comes from the data and the lone bar always fills the arc, which would draw 62 and 98 identically. The track behind the bar is the other half of the reading, since it is the remainder the value is a fraction of. semi drops the centre to 70 percent of the box so the half circle sits in the middle of its own space rather than at the top of it.
| tier | Value |
|---|---|
| used | 62 |
loading
The skeleton. isLoading swaps the arcs for five of their own, re-rolled every 1.5 seconds and morphing between lengths, and keeps the figure's measured height so the page does not jump when the data lands; under prefers-reduced-motion they hold still instead. The lengths come from a deterministic function rather than Math.random, so the server and the client draw the same skeleton and hydration has nothing to disagree about. The composed RadialBar draws nothing in this state — it is here so the chart comes back as itself, with the same bar, once the rows arrive.
Types
export type RadialVariant = 'full' | 'semi'Accessibility
- title is required; pass valueKey and the rows are also rendered as a visually hidden table.
- Set max or the scale comes from the data and the largest bar always fills the arc — which makes 62% and 98% look identical.
- showTrack draws the unfilled remainder, which is what makes a gauge readable at all.