A <figure class="pie-chart"> draws a
<table> of parts as slices of a whole. The first
column of the table names each part and the second holds its value.
It takes the same markup as a column chart. Slices run clockwise from the top in table order, so put the largest part first. A legend lists every row with its cell and its share of the total. A tooltip follows the pointer round the pie, and the arrow keys move it when the chart has focus. Keep a pie to about five parts. For more, or to compare parts that are close in size, use a bar chart.
| Plan | Revenue |
|---|---|
| Basic plan | $25,864 |
| Premium plan | $14,258 |
| Enterprise plan | $5,258 |
<figure class="pie-chart">
<figcaption>Revenue by plan, 2026</figcaption>
<table>
<thead>
<tr><th>Plan</th><th>Revenue</th></tr>
</thead>
<tbody>
<tr><th>Basic plan</th><td>$25,864</td></tr>
<tr><th>Premium plan</th><td>$14,258</td></tr>
<tr><th>Enterprise plan</th><td>$5,258</td></tr>
</tbody>
</table>
</figure>
The legend and tooltip show each cell's text as written, so format values in the table. The chart works out the shares from the numbers.
Donut
Add donut to cut a hole in the middle. A
<tfoot> row holds the total. Its value and label
show in the hole, and the chart does not draw it as a slice. Write the total
yourself, formatted as you want it read.
| Source | Leads |
|---|---|
| Referrals | 558 |
| Organic search | 434 |
| Paid campaigns | 248 |
| Total leads | 1,240 |
<figure class="pie-chart donut">
<figcaption>Lead sources, last six months</figcaption>
<table>
<thead>
<tr><th>Source</th><th>Leads</th></tr>
</thead>
<tbody>
<tr><th>Referrals</th><td>558</td></tr>
<tr><th>Organic search</th><td>434</td></tr>
<tr><th>Paid campaigns</th><td>248</td></tr>
</tbody>
<tfoot>
<tr><th>Total leads</th><td>1,240</td></tr>
</tfoot>
</table>
</figure>
Values
Add values to write each slice's share on it, in the
slice color's on-color. Slices under 5% are too thin for a label.
The legend still shows their share.
| Source | Revenue |
|---|---|
| Enterprise clients | $38M |
| Referral partners | $10.9M |
| Retainer contracts | $7.4M |
| Project-based work | $4.1M |
| Strategic partnerships | $2.7M |
| Total client revenue | $63.1M |
<figure class="pie-chart donut values">
<figcaption>Top revenue sources</figcaption>
<table>
<thead>
<tr><th>Source</th><th>Revenue</th></tr>
</thead>
<tbody>
<tr><th>Enterprise clients</th><td data-value="38000000">$38M</td></tr>
<tr><th>Referral partners</th><td data-value="10900000">$10.9M</td></tr>
<!-- One row per source -->
</tbody>
<tfoot>
<tr><th>Total client revenue</th><td>$63.1M</td></tr>
</tfoot>
</table>
</figure>
Labels
Add labels to write each slice's name outside it, so a
reader can match slices to parts without the legend. Names on the
right half run rightwards and those on the left run leftwards.
Neighbours that would touch move apart, away from the middle. A line
from each name back to its slice shows which slice a moved name
belongs to. The pie shrinks to
leave --md-comp-pie-chart-label-width on each side, or a
quarter of a narrow figure. Longer names get cut short with an
ellipsis. Combine it with
values to show the shares too.
| Source | Leads |
|---|---|
| Referrals | 558 |
| Organic search | 434 |
| Paid campaigns | 248 |
| Total leads | 1,240 |
<figure class="pie-chart donut values labels">
<figcaption>Leads by source</figcaption>
<table>
<!-- The same table as above -->
</table>
</figure>
Small parts
Set data-other to a percentage to group every row under
that share of the whole into one slice. The group comes last, so thin
slices do not crowd the pie or push their names into the caption. It
shows the rows' sum and share. The sum keeps the cells' currency and
decimals, and is compact when they use data-value.
Further columns do not add up, so the group shows no sum for them.
Set data-other-label to name the group. It defaults to
"Other". A single
small row keeps its own slice, and the table keeps every row for
screen readers.
| Channel | Visits |
|---|---|
| Search | 4,210 |
| Direct | 3,120 |
| Social | 2,480 |
| 310 | |
| Podcasts | 140 |
| 90 | |
| Radio | 60 |
<figure class="pie-chart labels" data-other="5">
<figcaption>Visits by channel</figcaption>
<table>
<thead>
<tr><th>Channel</th><th>Visits</th></tr>
</thead>
<tbody>
<tr><th>Search</th><td>4,210</td></tr>
<tr><th>Email</th><td>310</td></tr>
<!-- One row per channel; those under 5% become "Other" -->
</tbody>
</table>
</figure>
More columns
The slices come from the first value column. Further columns add lines to the tooltip, such as a change from last year, and stay in the table for screen readers.
| Plan | Revenue | Change |
|---|---|---|
| Basic plan | $25,864 | +10% |
| Premium plan | $14,258 | +4% |
| Enterprise plan | $5,258 | −2% |
<figure class="pie-chart">
<figcaption>Revenue by plan, with change</figcaption>
<table>
<thead>
<tr><th>Plan</th><th>Revenue</th><th>Change</th></tr>
</thead>
<tbody>
<tr><th>Basic plan</th><td>$25,864</td><td>+10%</td></tr>
<!-- One row per plan -->
</tbody>
</table>
</figure>
Chart cards
Put a chart in a card with the figures it explains. The card adds
no chart styles. Size the pie with
--md-comp-pie-chart-height, which sets its diameter.
- Total revenue
- $45,380
- +10% from last year
| Plan | Revenue |
|---|---|
| Basic plan | $25,864 |
| Premium plan | $14,258 |
| Enterprise plan | $5,258 |
<article class="outlined">
<div class="p-4">
<dl class="stats my-0">
<div>
<dt>Total revenue</dt>
<dd>$45,380</dd>
<dd class="stat-change up">+10% from last year</dd>
</div>
</dl>
<figure class="pie-chart donut" style="--md-comp-pie-chart-height: 200px">
<figcaption class="visually-hidden">Revenue by plan</figcaption>
<table><!-- … --></table>
</figure>
</div>
</article>
Use sparkline for a small pie beside a figure that
already explains it. It is a 48px pie with no legend or tooltip.
Values and colors
Cells read the same way as in a column chart. A cell's number is its
data-value, or its text without currency signs, percent
signs or grouping commas. Text that is not a single number, such as
"$38M" without a data-value, counts as nothing. So do
zero and negative values. Their rows stay in the legend and the
tooltip with no slice and no share. A chart whose cells are all
gaps shows its table.
Slices take primary, tertiary,
secondary, on-surface-variant, and then
primary-container and tertiary-container
mixed with on-surface, in turn. The mix keeps every
slice at 3:1 or more against the surface in light and dark themes. A seventh row would take the first row's color beside it,
so it takes the second. Past that, the colors repeat. Group small
parts into one "Other" row instead.
Accessibility
The table is the chart's text alternative. Give it a header row and
a header cell per row, and name the chart with a
<figcaption>. The chart hides the drawn pie,
values, total and legend from assistive technology.
The plot is one Tab stop, named by the caption. The arrow keys move round the pie. Right and down go to the next row, and left and up go to the one before. Past the last row they go on to the first. Home and End jump to the first and last rows, and Escape hides the tooltip. On a touch screen, tap a slice to show it. In a right-to-left page the slices still run clockwise, and so do the arrows. The tooltip is a polite live region, so a screen reader hears each row's values and share as it moves. A sparkline is not focusable. In forced colors the slices keep their colors so the legend still keys them.
JavaScript
AutoInit() starts every .pie-chart. To
start one yourself, call PieChart.init. After changing its
table, call update() to redraw it in place, or add
data-watch to the figure so it redraws itself. It has the line chart's
methods. The min and max options do not
apply.
const chart = Expressive.PieChart.init(document.querySelector('.pie-chart'));
chart.show(2); // highlight the third row; -1 hides the tooltip
chart.update(); // redraw from the table; focus and the highlighted row stay
chart.destroy(); // remove the drawing and show the table again
Tokens
Override these on the .pie-chart.
| Token | Default |
|---|---|
--md-comp-pie-chart-height | 240px, the pie's diameter; 48px for a sparkline |
--md-comp-pie-chart-hole | 0.6, the donut hole's share of the diameter |
--md-comp-pie-chart-label-width | 96px, the room on each side of the pie for labels, at most a quarter of the figure's width |
--md-comp-pie-chart-gap | 1.5, the gap between slices in hundredths of the plot's width |
--md-comp-pie-chart-color-1 … -4 | primary, tertiary, secondary, on-surface-variant |
--md-comp-pie-chart-color-5, -6 | primary-container, tertiary-container, each mixed 45% with on-surface |
--md-comp-pie-chart-on-color-1 … -6 | on-primary, on-tertiary, on-secondary, surface, surface, surface; text from values |
For a selective Sass build, include "pie-chart" in $components.