DonutChart

A `PieChart` with its middle opened up and filled with the number the chart is actually about — a total, a headline share — as ordinary DOM rather than SVG text.

html
<UiDonutChart :data="..." :categoryKey="..." :valueKey="..." :ariaLabel="..." />

Needs the charts module

This component ships in @jgastager/bundy-ui/charts, a separate module so projects that never draw a chart don't carry the paint classes these need. Nothing extra to install — just add the module entry. See Charts.


Data

required

data is one object per slice, all values non-negative. The hole in the middle is what a donut adds over a pie: room for the total the slices add up to.

  • Chrome · 6,400
  • Safari · 2,800
  • Firefox · 1,200
  • Edge · 780
11,180visits

A donut is a pie with the middle opened up, and the hole is the only reason to pick one — it's somewhere to put the number the chart is actually about. If you don't need that, PieChart is the same chart without the extra prop.

The hover fade is a CSS transition over the library's own 250ms duration, so it costs nothing and honours prefers-reduced-motion. There's no animate prop and nothing tweens on a data change — the marks are plain SVG elements Vue patches directly.

Category key

required

categoryKey sets which field on each row labels the slice.

  • Chrome
  • Safari
  • Firefox
  • Edge

Value key

required

valueKey sets which field holds its numeric size.

  • Chrome
  • Safari
  • Firefox
  • Edge

ARIA label

required

ariaLabel is required, and worth writing carefully — slice angles are the hardest thing on any chart to read back, so a screen reader gets nothing at all without it.

  • Chrome
  • Safari
  • Firefox
  • Edge

ARIA description

ariaDescription is the detail the label shouldn't carry.

Chrome dominates with 6,400 of 11,180 visits; Edge is the smallest share at 780.
  • Chrome
  • Safari
  • Firefox
  • Edge

Label

label names what the center number counts. Without it the number sits there unexplained.

  • Chrome · 6,400
  • Safari · 2,800
  • Firefox · 1,200
  • Edge · 780
  • Chrome · 6,400
  • Safari · 2,800
  • Firefox · 1,200
  • Edge · 780
11,180visits

Colors are the library's own semantic ones — primary, secondary, success, warning, error, info, pending, neutral — and any shade of them, primary-400 through primary-950, exactly as the bg-* utilities spell them. Set color per series (or per item) to pin one; leave it unset to take the next slot of the default sequence. neutral and secondary are never handed out automatically, so an unlabelled series never lands on them.

Value

value overrides the center number, which otherwise sums every slice. Use it when the headline isn't the total — a leading share, a budget remaining.

  • Chrome
  • Safari
  • Firefox
  • Edge
57%Chrome

The default centre value is the sum of every slice. Pass value when the headline is something else — a share, a delta, a target.

Everything the PieChart notes say about sorting, non-negative values and slice counts applies here too; the two share their slice allocation and geometry.

Inner radius

innerRadius is the size of the hole, as a fraction of the radius — and the whole difference between a donut and a PieChart. Shrink it toward zero and you get a pie with a caption in it.

Radius

radius rounds the corners of each slice. A little softens the ring; too much starts eating into thin slices, which is how a small share disappears.

Gap

gap is the space between slices, in degrees. Zero gives an unbroken ring; a degree or two is enough to separate neighbouring slices that share a similar colour.

Sort

sort orders the slices largest-first by default, which is what makes a donut readable at a glance. Turn it off when the rows already have a meaningful order — a rating scale, a funnel.

  • Medium
  • Low
  • High
  • Critical
  • Critical
  • High
  • Medium
  • Low

Colors

colors pins a paint per slice, matched positionally to data before any sorting — so the colours follow your rows rather than the rendered order.

  • Critical
  • High
  • Medium
  • Low
65issues

Value format

valueFormat covers the tooltip, the legend, and the default center total together.

  • Chrome · 6,400
  • Safari · 2,800
  • Firefox · 1,200
  • Edge · 780
11,180visits

Tooltip

tooltip shows the tooltip on hover.

  • Chrome
  • Safari
  • Firefox
  • Edge

Legend

legend shows the legend.

11,180visits

The centre is real DOM positioned over the plot, not SVG text — so it can hold any Bundy component, and the number stays selectable and searchable. It's pinned to whichever edge the plot occupies so the legend doesn't push it off-centre.

Legend position

legendPosition sets where the legend sits relative to the plot.

  • Chrome
  • Safari
  • Firefox
  • Edge

Legend align

legendAlign places the key horizontally. Centre reads well under a ring, which is itself centred, where start would leave it visibly off-axis.

  • Chrome
  • Safari
  • Other
  • Chrome
  • Safari
  • Other
  • Chrome
  • Safari
  • Other

Legend orientation

legendOrientation flows the entries in a row or a column. vertical reads well beside the ring — one slice per line, in the same order as the arcs.

  • Chrome · 6,400
  • Safari · 2,800
  • Firefox · 1,200
  • Edge · 780

Hovering dims every slice except the focused one to 0.7 rather than drawing a ring on it — a ring is a dot, which reads as a stray mark sitting on the arc. The hit test is polar: the angle and distance under the pointer, not a bounding box.

Height

height is the plot's height in pixels; the ring is sized to fit inside it. It's ignored once aspectRatio is set.

  • Chrome
  • Safari
  • Other
  • Chrome
  • Safari
  • Other

Aspect ratio

aspectRatio sizes the chart from its container's width instead of a fixed height.

  • Chrome
  • Safari
  • Firefox
  • Edge

Loading

loading is a skeleton at the chart's exact height; an empty data array gets the empty state instead.

No data to display

API Reference

Generated from the component's source — props, slots and emits as the component actually declares them.

Props

Prop
Type
Default
Description
datarequired
Record<string, unknown>[]
One object per slice. Every value must be non-negative.
categoryKeyrequired
string
Field holding each slice's label.
valueKeyrequired
string
Field holding each slice's numeric value.
ariaLabelrequired
string
What the chart shows, for screen readers. Required.
string
A longer accessible description, for detail the label shouldn't carry.
string
Text under the center number, naming what it counts — "sessions", "of budget".
string
The center number. Defaults to the sum of every slice, through `valueFormat`.
number
0.62
Size of the hole, as a fraction of the radius. This is the whole difference between a donut and a `UiPieChart` — the middle is traded for somewhere to put a number. Below about `0.4` there isn't room for one, at which point the pie is the honest choice.
number
6
Corner radius on each slice, in pixels.
number
1
Gap between slices in degrees.
boolean
true
Sorts slices largest-first.
ChartColorName[]
Per-slice colors, positionally matched to `data`.
(value: number) => string
Formats values in the tooltip, legend, and the default center total.
boolean
true
Shows the tooltip on hover.
boolean
true
Shows the legend.
"top" | "bottom"
'bottom'
Where the legend sits relative to the plot.
"start" | "center" | "end"
'center'
Horizontal placement of the legend.
"horizontal" | "vertical"
'horizontal'
How the legend entries flow.
number
320
Height of the plot in pixels. Ignored when `aspectRatio` is set.
number
Width-to-height ratio, sizing the chart from its container's width.
boolean
false
Replaces the plot with a skeleton of the same height.

Slots

Slot
default

Emits

Event
Payload
Description
focusChange
[label: string | null]
The slice under the pointer, or `null` once it leaves.

Types

ts
type ChartColorName = ChartColorFamily | `${ChartColorFamily}-${ChartColorShade}`;