Pie chart
The custom element is <hui-pie-chart>.
Overview
A pie, or with inner-radius a donut with its total in the hole, one slice per row. The defaults are Recharts', which shadcn's examples keep: from three o'clock, counter-clockwise, 80 percent of the room. The shadcn reference's savings ring is this element, clockwise from the top.
Example
Source
<div style="max-width:15rem"><hui-pie-chart label="Browser share" inner-radius="50" legend center-label="935" center-caption="Visitors" style="height:220px"> <script type="application/json">{"series":[{"key":"visitors","label":"Visitors"}],"rows":[{"label":"Chrome","visitors":275},{"label":"Safari","visitors":200},{"label":"Firefox","visitors":287},{"label":"Edge","visitors":173}]}</script></hui-pie-chart></div>The Go template that renders it:
<hui-pie-chart label="Browser share" inner-radius="50" legend center-label="{{ .Total }}" center-caption="Visitors" style="height:220px;max-width:15rem"> <script type="application/json">{{ .Browsers }}</script></hui-pie-chart>On a phone
A legend wider than its chart wraps onto more lines, centred, and the plot gives up the height it needs. A hole set in pixels never takes more than 85 percent of the radius, so a donut squeezed by its legend is still a ring.
Source
<hui-pie-chart label="Visitors by browser" inner-radius="60" legend style="height:280px"> <script type="application/json">{"series":[{"key":"visitors","label":"Visitors"}],"rows":[{"label":"Chrome","visitors":275},{"label":"Safari","visitors":200},{"label":"Firefox","visitors":187},{"label":"Edge","visitors":173},{"label":"Samsung Internet","visitors":90},{"label":"Other","visitors":60}]}</script></hui-pie-chart>API
Attributes14
| Name | Type | Default | Description |
|---|---|---|---|
label | string | "" | Required. The chart's accessible name, followed by the centre text, and the data table's caption. |
index | string | "label" | The row property that names each slice or ring. |
start-angle | number | 0 | Where the first arc starts, in degrees from three o'clock, counter-clockwise - Recharts' convention, so shadcn's values carry over. |
end-angle | number | 360 | Where a full sweep ends. Below the start angle, the chart runs clockwise. |
inner-radius | number | 0 | The hole. Up to 1 a fraction of the room, above 1 pixels. |
outer-radius | number | 0.8 | The outer edge, the same way. |
gap | number | 1 | The space between slices at their outer edge, in pixels. 0 lets them touch, as the reference's do. |
center-label | string | - | Large text in the middle - a total. Read after the label as part of the chart's name. |
center-caption | string | - | Smaller, muted text beneath it. |
legend | boolean | false | A legend of the rows under the chart. |
tooltip | "index" | "item" | "none" | "index" | The row under the pointer, or no tooltip. |
indicator | "dot" | "line" | "dashed" | "dot" | The marker beside the tooltip row. |
format | string | - | Intl.NumberFormat options as JSON. |
locale | string | the document's lang | The locale values are formatted in. |
Properties14
| Name | Type | Default | Description |
|---|---|---|---|
label | string | as <code>label</code> | Reflects to the label attribute. |
index | string | as <code>index</code> | Reflects to the index attribute. |
startAngle | number | as <code>start-angle</code> | Reflects to the start-angle attribute. |
endAngle | number | as <code>end-angle</code> | Reflects to the end-angle attribute. |
innerRadius | number | as <code>inner-radius</code> | Reflects to the inner-radius attribute. |
outerRadius | number | as <code>outer-radius</code> | Reflects to the outer-radius attribute. |
gap | number | as <code>gap</code> | Reflects to the gap attribute. |
centerLabel | string | as <code>center-label</code> | Reflects to the center-label attribute. |
centerCaption | string | as <code>center-caption</code> | Reflects to the center-caption attribute. |
legend | boolean | as <code>legend</code> | Reflects to the legend attribute. |
tooltip | "index" | "item" | "none" | as <code>tooltip</code> | Reflects to the tooltip attribute. |
indicator | "dot" | "line" | "dashed" | as <code>indicator</code> | Reflects to the indicator attribute. |
format | string | as <code>format</code> | Reflects to the format attribute. |
locale | string | as <code>locale</code> | Reflects to the locale attribute. |
Events1
| Name | Description |
|---|---|
hui-chart-focus | The reader moved to a category, by pointer or keyboard: its position, and the row as the server sent it. It bubbles and is composed. Announced, never stored: a host can mirror it elsewhere on the page. |
Slots1
| Name | Description |
|---|---|
(default) | One <script type="application/json"> holding series and rows. Replacing it, or its text, redraws the chart. |
CSS custom properties2
| Name | Default | Description |
|---|---|---|
--hui-chart-1 … --hui-chart-5 | the preset's violet | Series colours, by position, repeating after the fifth. |
--hui-series-<key> | - | One series' colour, set from the host's stylesheet: --hui-series-mobile: var(--hui-chart-4). A colour field in the JSON wins over it. |
::part() hooks8
| Name | Description |
|---|---|
figure | The wrapper carrying role="figure" and the name. |
plot | The <svg>, hidden from assistive technology. |
mark | One slice or ring, with data-index. |
centre-label | The text in the middle. |
table | The visually hidden data table. |
tooltip | The tooltip, a manual popover. |
legend | The legend of rows. |
announcer | The visually hidden live region. |
States
A progress ring, clockwise from the top
The same element as a progress ring: a hole from inner-radius, a sweep set by start-angle and end-angle, and a total in the middle.
Source
<div style="width:220px"><hui-pie-chart label="Savings goal" inner-radius="70" outer-radius="95" start-angle="90" end-angle="-270" center-label="$24,000" center-caption="80% of $30,000" style="height:220px"> <script type="application/json">{"series":[{"key":"amount","label":"Amount"}],"rows":[{"label":"Saved","amount":24000,"colour":"var(--hui-chart-2)"},{"label":"Remaining","amount":6000,"colour":"var(--hui-chart-1)"}]}</script></hui-pie-chart></div>A pie
No inner-radius, so the slices meet in the centre.
Source
<div style="width:200px"><hui-pie-chart label="Browser share" style="height:200px"> <script type="application/json">{"series":[{"key":"visitors","label":"Visitors"}],"rows":[{"label":"Chrome","visitors":275},{"label":"Safari","visitors":200},{"label":"Firefox","visitors":287},{"label":"Edge","visitors":173}]}</script></hui-pie-chart></div>Server-side mechanics
The server writes one row per slice or ring into the element's <script type="application/json"> child, in the order they are drawn: the chart never sorts, so the server decides whether the biggest slice comes first. A row may carry its own colour. Only the first series is drawn. Replacing the child redraws the same element without animating.
Accessibility
- The chart is a
figurenamed by its label and the centre text - "Browser share: 935 Visitors" - because the SVG, and so the text drawn in the hole, is hidden from assistive technology. The data is a visually hidden table. - The plot is one tab stop: the arrow keys step the slices or rings with the tooltip following, and a polite live region reads each - "Safari 200".
- The first draw sweeps the arcs open over 1500ms after a 400ms pause, as the reference's do; under
prefers-reduced-motion: reducethey are simply there. - Slices part by a pixel at their outer edge by default, so neighbours whose colours sit 1.3:1 apart still read as two;
gap="0"matches the reference, where they touch.
Keyboard
| Keys | Action |
|---|---|
Tab | Focuses the plot, one stop per chart, and shows the tooltip at the category the keyboard was last on. |
Arrow Right, Arrow Down | The next category. |
Arrow Left, Arrow Up | The previous category. |
Home, End | The first and the last category. |
Escape | Hides the tooltip; focus stays on the plot. |
Gotchas
- Give a pie a width as well as a height, or its wrapper one: a chart fills its container's width, and a pie is only as big as the smaller of the two.
- A pie reads the first series only. A second series is ignored rather than drawn.
- Like every chart, it ships in
home-ui-charts.js.