Charts
Pie Chart
Show parts of a whole with pie, donut, rose, and labeled slice layouts.
Charts / Pie Chart
Overview
import { PieChart } from "@kamod-ch/charts/pie";
PieChart renders proportional slices from a single value column. Use innerRadius for donuts, rose for equal-angle Nightingale layouts, and labels="outside" when category names need gutter space with leader lines.
API notes
Typed PieChart props — innerRadius for donuts, rose for equal-angle layouts, and pie-specific tooltip and legend options.
- Value policy
- Only finite values strictly greater than zero render as slices. Non-finite, zero, or negative entries resolve to
0and are omitted from geometry, navigation, and tooltips. When every value is non-positive, passemptyContentfor a custom fallback. - Percentages
PieSliceContext.percentageis the slice value divided by the sum of visible slices. Legend toggles remove hidden keys from that total, so percentages in tooltips and announcements stay consistent with what is on screen.- innerRadius (donut)
- Set
innerRadiusto any pixel value greater than zero to open a donut hole. Pair withcenterContentfor KPI summaries. There is no separatedonutprop — radius controls the shape. - Pie vs rose
- A standard pie maps value to angle. Rose (Nightingale) charts keep equal angles and map value to radius via
rose. Default scaling issqrt, which compresses outliers so small categories stay readable; usescale: "linear"only when proportional radius is intentional. - Outside labels
labels="outside"enables leader lines and defaultscollision: "shift". TunePieLabelLineOptionsfor radial and horizontal segments, or pass a fullPieLabelOptionsobject forminAngle, customrender, andcollision: "hide".- Legend & tooltip
showLegendrenders slice toggles keyed by stable slicekey. Setlegend.interactivetofalsefor a read-only legend.tooltipdefaults to off;trueshows value and percentage viaPieTooltipOptions.- Color & categories
- Do not rely on slice color alone — pair fills with labels, legend text, and tooltips. Pie charts work best for roughly three to six categories; beyond that prefer a BarChart or a sorted table for exact comparisons.
- Locale formatting
- Use
Intl.NumberFormatinformatValueorformatPercentageonPieTooltipOptions(see the Basic example). Slice labels accept the same formatters throughPieLabelOptions.render.
Gallery
Six functional patterns covering legend tooltips, donuts, center KPIs, rose geometry, outside labels, and a carrier capacity snapshot. Each preview uses Kamod’s real PieChart API — no copied option objects.
Basic pie with legend and tooltip
Inside labels, an interactive legend, and tooltips that show raw value plus share of visible slices. Percentages update when legend items hide segments.
Donut via innerRadius
Set innerRadius to open the hole — there is no separate donut prop. Outside labels leave gutter space automatically.
Donut with center KPI
centerContent renders Preact children in the hole — useful for totals, units, or status text without extra SVG markup.
Rose (Nightingale) chart
Equal slice angles with value mapped to radius. Default sqrt scaling keeps small regions visible; angle no longer encodes magnitude.
Outside labels with leader lines
Leader lines connect slices to gutter labels. collision: shift nudges overlapping labels vertically; narrow slices respect minAngle.
Resize horizontally — gutter labels shift to avoid overlap.
Carrier capacity donut
Operational snapshot: allocated lanes versus remaining headroom. Pair color with labels and a center KPI so utilization is readable without hue alone.
Keyboard and screen reader behavior
- Focus the chart SVG and use Arrow keys to move between visible slices; Home and End jump to the first and last slice.
- Enter or Space activates the focused slice; Escape clears the active slice without moving focus.
- Tooltips follow pointer hover; keyboard selection announces value and percentage through a live region.
- Legend buttons toggle visibility without adding extra tab stops inside the SVG.
- Prefer title and description text that states the metric and unit — do not rely on slice color alone.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| valueKey | DataAccessor<TDatum, number | null | undefined> | — | Numeric value for each slice. Only finite values strictly greater than zero render as slices. |
| labelKey | DataAccessor<TDatum, string | number | null | undefined> | — | Label accessor for slices, legend items, and tooltips. |
| innerRadius | number | 0 | Inner radius in pixels. Values above zero create a donut hole; pair with centerContent for KPI summaries. |
| outerRadius | number | — | Outer radius in pixels. Defaults to the available plot size; outside labels auto-shrink the radius to leave gutter space. |
| padAngle | number | — | Gap between adjacent slices in radians. |
| cornerRadius | number | — | Rounded outer corners for slice arcs. |
| sort | boolean | PieSortComparator<TDatum> | false | When true, sorts slices by descending value. Pass a comparator for custom ordering. |
| labels | PieLabelPosition | PieLabelOptions<TDatum> | "inside" | Shorthand: "none", "inside", or "outside". Outside enables leader lines and defaults collision to shift. |
| rose | boolean | PieRoseOptions | — | Nightingale layout: equal slice angles with value mapped to radius. Default scale is sqrt when rose is true. |
| colors | readonly string[] | — | Explicit palette for slices. Colors stay stable by original data index. |
| centerContent | ComponentChildren | — | Content rendered in the donut hole when innerRadius is set. |
| emptyContent | ComponentChildren | — | Fallback when data exists but no slice has a finite value greater than zero, or when every slice is hidden via legend. |
| activeOffset | number | 6 | Pixel expansion applied to the active slice outer radius on pointer or keyboard focus. |
| tooltip | boolean | PieTooltipOptions<TDatum> | false | Pointer and keyboard tooltips for the active slice. true uses default value and percentage formatting. |
| showLegend | boolean | false | Render an HTML legend with per-slice visibility toggles keyed by stable slice keys. |
| legend | PieLegendOptions | — | Legend behavior. interactive defaults to true for segment toggling. |
| onSegmentClick | (datum, index, value) => void | — | Called when a slice is activated with pointer click or keyboard Enter/Space. |
| animate | boolean | ChartAnimateOptions | — | Optional data animation. No-op on the static donut entry unless MotionConfig is present. |
PieSliceContext
Passed to tooltip.render, tooltip.formatValue, tooltip.formatPercentage, and PieLabelOptions.render.
| Prop | Type | Default | Description |
|---|---|---|---|
| datum | TDatum | — | Original data row for the slice. |
| index | number | — | Row index in the source data array. |
| key | string | — | Stable identifier used for legend toggles and React/Preact keys. |
| label | string | — | Resolved display label for the slice. |
| value | number | — | Normalized slice value (finite and greater than zero). |
| total | number | — | Sum of visible slice values; used for percentage calculation. |
| percentage | number | — | Slice value as a percentage of total visible slices (0–100). |
| color | string | — | Resolved fill color for the slice. |
| startAngle / endAngle | number | — | Slice arc bounds in radians. |
| innerRadius / outerRadius | number | — | Resolved radii for the slice, including activeOffset when active. |
| active | boolean | — | True when the slice is keyboard- or pointer-selected. |
PieTooltipOptions
Chart-level via tooltip. Defaults show numeric value and percentage of visible slices.
| Prop | Type | Default | Description |
|---|---|---|---|
| placement | TooltipPlacement | — | Tooltip placement relative to the slice anchor. |
| offset | number | — | Pixel gap between the anchor and tooltip content. |
| render | (context: PieSliceContext<TDatum>) => ComponentChildren | — | Fully custom tooltip body. Receives the active slice context. |
| formatValue | (value, context) => ComponentChildren | null | undefined | — | Format the numeric value. Return null to fall back to the default string. |
| formatPercentage | (percentage, context) => ComponentChildren | null | undefined | — | Format the percentage (0–100). Return null to fall back to one decimal place with a percent sign. |
PieLabelOptions
Chart-level via labels when not using the "inside" | "outside" | "none" shorthand.
| Prop | Type | Default | Description |
|---|---|---|---|
| position | "none" | "inside" | "outside" | "inside" | Label placement. outside draws leader lines to gutter labels. |
| render | (context: PieSliceContext<TDatum>) => ComponentChildren | null | undefined | — | Custom label content per slice. |
| minAngle | number | — | Minimum slice span in degrees required to show a label and leader line. |
| collision | "none" | "shift" | "hide" | — | Outside-label overlap strategy. Defaults to shift for outside placement. |
| line | PieLabelLineOptions | — | Leader line geometry and styling for outside labels. |
| offset | number | — | Pixel gap between the leader line elbow and label text. |
| color / class | string | — | SVG fill color or class for label text. |
PieLabelLineOptions
Nested under PieLabelOptions.line for outside placement.
| Prop | Type | Default | Description |
|---|---|---|---|
| length | number | — | Radial segment length from the slice outer edge to the elbow. |
| horizontalLength | number | — | Horizontal segment length from the elbow to the label anchor. |
| color | string | — | Stroke color for leader lines. |
| strokeWidth | number | — | Stroke width for leader lines. |
| class | string | — | Optional class applied to leader line paths. |
PieRoseOptions
Nested under rose when not passing rose={true}.
| Prop | Type | Default | Description |
|---|---|---|---|
| scale | "sqrt" | "linear" | "sqrt" | How slice values map to radius. sqrt compresses large outliers; linear is proportional. |
| minRadius | number | — | Minimum outer radius for the smallest visible slice. Defaults to innerRadius plus 8% of the radius span. |
PieLegendOptions
Nested under legend. Legend items key off stable slice key values from the data row index.
| Prop | Type | Default | Description |
|---|---|---|---|
| interactive | boolean | true | When true, legend items toggle slice visibility. Hidden slices are excluded from totals and navigation. |
Value policy and percentages
- Only finite values strictly greater than zero become slices. Zero, negative, and non-finite entries are treated as non-visible.
- When every value is non-positive, render
emptyContentinstead of an blank chart. PieSliceContext.percentagedivides each visible slice by the sum of currently visible slices. Legend toggles remove hidden keys from that total, so tooltips and announcements stay aligned with what is on screen.activeOffsetexpands the active slice radius on pointer hover or keyboard focus (default6px).
Pie vs rose and when to pick another chart
| Layout | Angle | Radius | Best for |
| ------------- | --------------------- | ----------------------------- | ------------------------------------------------------------ |
| Standard pie | proportional to value | fixed (minus donut hole) | three to six categories, familiar part-to-whole |
| Rose (rose) | equal per category | proportional (sqrt default) | many categories where small values would vanish as thin arcs |
| Bar / table | — | — | more than ~six categories or when exact rank order matters |
Rose charts default to sqrt scaling so one large category does not swallow the plot; use scale: "linear" only when radius should track value linearly.
Do not use color as the only signal — pair fills with labels, legend text, tooltips, or explicit description copy. For dense comparisons, prefer Bar Chart or a sorted table.
Donut and empty states
Set innerRadius to any pixel value above zero to open a donut hole. centerContent accepts Preact children for KPIs or status text. There is no separate donut boolean. For ratio-based rings, semicircles, and automatic center totals, see Donut Chart.
When all slices are hidden through the legend, the chart shows emptyContent while the legend remains so users can restore segments.