Donut
Zero-dependency SVG pie and donut chart with theme-synced palette, hover focus with center readout, and a toggleable legend
Donut renders categorical proportions as SVG — no chart library underneath. Series colors come from useChartPalette, so slices follow brand, surface and dark-mode changes automatically. Hovering a slice or its legend entry dims the others; in donut mode the center shows the hovered label and value (the total when idle). Legend entries are clickable to hide or restore slices.
Part of the chart family: it shares the chart-kit math layer with Chart, Radar and Sparkline — reach for Chart when you need axes, tooltips or large time series. Each member installs independently.
Installation
Usage
Donut
"use client";
import { Donut } from "@/ui";
const data = [
{ label: "Direct", value: 420 },
{ label: "Search", value: 365 },
{ label: "Social", value: 210 },
{ label: "Email", value: 140 },
{ label: "Referral", value: 65 },
];
export default function DonutBasicDemo() {
return <Donut data={data} ariaLabel="Website traffic by channel" className="max-w-xs" />;
}
Each datum is { label, value, color? }. Colors are picked from the palette by index unless a datum carries an explicit color. Negative and zero values are clamped; the wedge gap is controlled by gap in degrees so thin slices stay readable.
Pie
"use client";
import { Donut } from "@/ui";
const data = [
{ label: "Free", value: 8420 },
{ label: "Pro", value: 3160 },
{ label: "Team", value: 1240 },
{ label: "Enterprise", value: 310 },
];
export default function DonutPieDemo() {
return (
<Donut
data={data}
variant="pie"
gap={1.5}
ariaLabel="Subscribers by plan"
valueFormatter={(value) => value.toLocaleString()}
className="max-w-xs"
/>
);
}
variant="pie" removes the hole and the center readout. Pass valueFormatter to control how values render anywhere the component displays them.
API Reference
Donut
| Prop | Type | Default | Description |
|---|---|---|---|
data | DonutDatum[] | - | Slices: { label, value, color? } |
variant | "pie" | "donut" | "donut" | pie closes the hole and hides the center readout |
gap | number | 2 | Angular gap between slices in degrees |
valueFormatter | (value: number) => string | toLocaleString | Formats values shown in the center readout |
showLegend | boolean | true | Renders the clickable legend above the chart |
className | ClassNameValue | - | Extra classes appended to the root element |
classNames | DonutClassNames | - | Slot overrides: root / svg / segment / legend |
styles | DonutStyles | - | Slot inline styles, matching the classNames slots |
Behavior Notes
- Hover focus: hovering a slice or its legend entry dims every other slice to 35% opacity; the donut center swaps the idle total for the hovered label and value.
- Legend toggle: clicking a legend entry hides or restores that slice; hidden slices are excluded from the total.
- Theme sync: slice colors rebuild whenever
data-brand,data-surfaceor dark mode changes, with per-datumcoloralways winning over the palette. - Sizing: the chart is a square that fills the container up to
max-w-64— override viaclassNames.svg; legend and layout flow above it.