Radial Bar Chart
ProConcentric progress rings, one per track, with per-track scales, a configurable sweep and start angle, a centre readout and a legend. States under the plot what concentric arcs cannot be read for, and marks any track past the end of its scale. Pure SVG.
import { SvRadialBarChartComponent } from '@styloviz-pro/charts';Demo & usage
Basic usage
ProUsage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Centre readout
ProDial sweep
ProTrack interactions
ProReact to a track running past its scale
ProWhen to use
- A handful of progress-toward-capacity figures that share a scale — utilisation, quota, completion.
- A compact card where a row of bars would not fit.
- For one value against banded thresholds, use the Gauge; for part-to-whole, a Donut.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the tracks are being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| emptyMessage | string | 'No data to plot' | Copy for the no-data state. |
| filtered | boolean | false | True when the emptiness is caused by a filter rather than an empty set. |
| tracks | readonly RadialBarTrack[] | [] | Concentric tracks, outermost first. |
| max | number | 100 | Chart-wide scale maximum; a per-track `max` still wins. |
| size | number | 260 | Overall diameter in pixels. |
| thickness | number | 14 | Ring thickness in pixels. |
| gap | number | 8 | Gap between rings in pixels. |
| startAngle | number | 0 | Where the rings start, in degrees clockwise from twelve o'clock. |
| sweep | number | 360 | How far a full-value ring sweeps, in degrees. `360` is a closed ring; `270` leaves a gap at the bottom, which is where a dial normally puts its scale. |
| rounded | boolean | true | Round the leading edge of each arc. |
| showLegend | boolean | true | Show the legend (swatch, label, value and share) under the rings. When off, each track's share is still printed as one plain line: arc length cannot be compared across rings, so the share is never optional. |
| centerValue | string | '' | Large text in the middle of the rings (e.g. a headline figure). |
| centerLabel | string | '' | Caption under `centerValue`. |
| animated | boolean | true | Animate arcs on value change. |
| title | string | '' | Chart title, rendered above the plot. |
| ariaLabel | string | '' | Accessible name override. Falls back to a generated summary. |
| customClass | string | '' | Extra CSS classes applied to the root wrapper. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
| trackHover | RadialBarTrackEvent | Emitted when a track is hovered or keyboard-focused. |
| trackSelect | RadialBarTrackEvent | Emitted when a track is clicked or activated with Enter/Space. |
Wire it to your API
This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.
Template
ProBound to the names this component actually exposes.
Host class
ProDrive the three async inputs from your own fetch.
- This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `tracks`, `loading` and `error`.
- Set `filtered` when a search or filter is active. It is the difference between "you have no orders" and "no orders match this filter" — telling a customer with 4,000 orders the first one reads as a bug.
- Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.
Full walkthrough: Connecting to a backend.
Customization & theming
- Ring thickness, gap and diameter.
- Start angle and sweep (360° ring or a 270° dial).
- Per-track scale and colour, rounded or butt caps.
- Legend and centre readout each optional.
- A track past its maximum draws a full ring with a dashed rim outside it and reports its real percentage; one with a non-finite or negative value leaves its ring empty rather than drawing a zero nobody measured.
Best practices
- Do: Keep it to four or five rings — an inner ring is shorter for the same percentage and reads as smaller.
- Do: Put the headline figure in `centerValue` rather than making the reader add up rings.
- Don't: Do not use it for part-to-whole; the rings are independent scales, not slices.
- Don't: Do not compare two rings by eye and conclude one is larger. Concentric rings have different circumferences, so the same percentage draws a visibly shorter arc on an inner track than on an outer one — the eye compares arc LENGTH, and arc length is not the value. The chart says so under the plot and prints each track's share — in the legend, or on a plain line under the rings when `showLegend` is off; those are the comparable figures.
- Do: Give every track the same maximum unless you have a reason not to. Mixed maxima are supported and disclosed, but two arcs of equal length standing for entirely different numbers is the worst reading this chart can produce.
- Do: Send a value past its maximum rather than clamping it yourself. A ring has nowhere to go past a full revolution, so the chart draws it full and adds a dashed rim, reports the real percentage, and names the track in the note. Clamping upstream throws that away and leaves 150 of 100 looking exactly like 100 of 100.
- Tip: Angles are degrees clockwise from twelve o’clock, shared with `sv-gauge-chart`.
Accessibility
- Each ring’s track is a `role="button"` focus target named with its value, scale and percentage — a 0 % track stays reachable because the focus target is the track, not the value arc.
- An inner ring is physically shorter than an outer one at the same percentage, so arc length cannot be compared by eye at all — the encoding misleads even a sighted reader. `sv-data-summary-table` publishes each track's value, share and, on per-track scales, its scale maximum: the pair of numbers the rings themselves cannot convey.
- The caveat that arc lengths are not comparable is stated in the rendered output, not only in the table — the reader being misled by unequal baselines is the one looking straight at the rings. It names the differing scale maxima when the tracks disagree, and each over-scale track with how far over it is. There is no input to hide it, on the same rule as the bullet chart's scale note.
- An over-scale track says so in its accessible name as well as with the dashed rim, because a rim has nothing to say to a screen reader; a track with no usable value is named as such rather than announced as zero — "no value" in the chart's name, "No value" in the table's Share cell and the legend, and `value: null` / `percent: null` on the emitted event.
- Animation is disabled under `prefers-reduced-motion`.
Feedback on Radial Bar Chart
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.