Charts
PieChart
A pie or donut chart in pure SVG. Non-positive and non-finite slices are dropped - they have no meaning in a part-to-whole picture - and the remaining values are normalised, so slices always add up to the circle.
Import
Charts live at their own subpath with their own stylesheet, so an app that never draws one pays nothing for them.
import { PieChart } from '@the_viveksingh/vivek-ui/charts'
import '@the_viveksingh/vivek-ui/charts.css'Donut with a centre label
Percentages are printed on the slices, so the chart does not rely on colour matching a legend.
| Category | Value | Share |
|---|---|---|
| Direct | 42 | 42% |
| Search | 31 | 31% |
| Social | 17 | 17% |
| Referral | 10 | 10% |
<PieChart
data={[
{ label: 'Direct', value: 42 },
{ label: 'Search', value: 31 },
{ label: 'Social', value: 17 },
{ label: 'Referral', value: 10 },
]}
donut
showLabels
centerLabel="100%"
centerSublabel="of traffic"
title="Traffic by source"
/>Full pie
| Category | Value | Share |
|---|---|---|
| Free | 620 | 65.3% |
| Pro | 240 | 25.3% |
| Team | 90 | 9.5% |
<PieChart
data={[
{ label: 'Free', value: 620 },
{ label: 'Pro', value: 240 },
{ label: 'Team', value: 90 },
]}
showLabels
title="Customers by plan"
/>Accessibility
Every chart carries role="img" with a generated accessible name, and renders a real <table> alongside it — visually hidden, never display: none — so a screen-reader user gets the actual numbers instead of "chart". Set accessibleTable={false} only if you have provided the data in a table elsewhere on the page.
No series is ever encoded by colour alone: each carries a distinct dash pattern and marker shape on top of its colour, so the chart survives greyscale printing and every common form of colour blindness.
The palette is verified rather than asserted. Each theme has its own six colours, because one set cannot clear the 3:1 non-text contrast threshold against both a white and a near-black plot surface. Separation is measured under simulated protanopia, deuteranopia and tritanopia: for one to four series — which is nearly every chart — the closest pair sits at ΔE 17, slightly better than the unmodified Okabe-Ito palette it is derived from. Past four series the dash patterns and marker shapes carry the distinction.
Hostile data
Real series contain gaps and rubbish. Every chart is tested against empty arrays, a single point, all-equal values, negatives crossing zero, NaN, ±Infinity, 1e308 and 1e-320, with an assertion that no rendered SVG attribute ever contains NaN. A bad row degrades the chart; it does not blank your page.
Props
Generated from the package's own type declarations, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
data | readonly PieDatum[] | — | Slices, in drawing order (clockwise from 12 o clock). |
donut | boolean | — | Punch out the middle. |
innerRadius | number | — | Hole size as a fraction of the outer radius, `0` to `0.95`. Only applies with `donut`; defaults to `0.6`. |
size | number | — | Side of the square `viewBox`, in px, and the widest the chart will draw. |
showLegend | boolean | — | Show the legend. Defaults to `true`. Note there is deliberately no `interactiveLegend` here, unlike LineChart, AreaChart and BarChart. A pie shows parts of a whole, and hiding one wedge would leave a gap while the rest kept their original angles - so the chart would no longer sum to 100% and would quietly misreport every remaining share. Re-proportioning needs the geometry recomputed, which CSS cannot do. Use a BarChart when series need to be toggled. |
showLabels | boolean | — | Percentage inside each slice. Slices under 4% are skipped, as they cannot fit. |
startAngle | number | — | Degrees clockwise from 12 o clock for the first slice. |
padAngle | number | — | Gap between slices, in degrees. |
centerLabel | string | — | Big text in the middle of a donut, e.g. the total. |
centerSublabel | string | — | Smaller text under `centerLabel`. |