A <figure class="radial-chart"> draws a
<table> of progress values as rings. The first
column of the table names each row and the second holds its value.
It takes the same markup as a column chart. Each row is a ring, outermost first, filled clockwise from the top as a share of 100. A legend lists the rows and their cells when there is more than one. A tooltip follows the pointer across the rings, and the arrow keys move it when the chart has focus. Keep a radial chart to about four rings. To compare more values, use a bar chart.
| Plan | Goal reached |
|---|---|
| Free plan | 80% |
| Basic plan | 70% |
| Premium plan | 60% |
| Enterprise plan | 50% |
<figure class="radial-chart">
<figcaption>Plan goals reached, 2026</figcaption>
<table>
<thead>
<tr><th>Plan</th><th>Goal reached</th></tr>
</thead>
<tbody>
<tr><th>Free plan</th><td>80%</td></tr>
<tr><th>Basic plan</th><td>70%</td></tr>
<tr><th>Premium plan</th><td>60%</td></tr>
<tr><th>Enterprise plan</th><td>50%</td></tr>
</tbody>
</table>
</figure>
The legend and tooltip show each cell's text as written, so format
values in the table. For values out of something other than 100,
set data-max on the figure.
Total
Add a <tfoot> row to show a total in the middle,
with its value large and its label below. The chart does not draw it
as a ring. A single ring with a total is a progress dial.
| Target | Reached |
|---|---|
| Sales | 55% |
| Sales | 250 |
<figure class="radial-chart" style="--md-comp-radial-chart-thickness: 14">
<figcaption>Monthly sales target</figcaption>
<table>
<thead>
<tr><th>Target</th><th>Reached</th></tr>
</thead>
<tbody>
<tr><th>Sales</th><td>55%</td></tr>
</tbody>
<tfoot>
<tr><th>Sales</th><td>250</td></tr>
</tfoot>
</table>
</figure>
Gauge
Add gauge to draw each ring as a half circle over the middle,
filling from the left. The total sits on the gauge's base line.
Here data-max="25" makes each value a share of 25 seats.
| Seats | Count |
|---|---|
| Filled | 18 |
| Offered | 21 |
| Total seats | 25 |
<figure class="radial-chart gauge" data-max="25">
<figcaption>Team capacity</figcaption>
<table>
<thead>
<tr><th>Seats</th><th>Count</th></tr>
</thead>
<tbody>
<tr><th>Filled</th><td>18</td></tr>
<tr><th>Offered</th><td>21</td></tr>
</tbody>
<tfoot>
<tr><th>Total seats</th><td>25</td></tr>
</tfoot>
</table>
</figure>
Labels
Add labels to name each ring at its start, so a reader can
match rings to rows without the legend. The rings then run three quarters
of the way round, from the top to the left. Each name sits in
the empty top left quarter and ends beside its ring's start. Names
shrink to fit between crowded rings, and long ones end in an
ellipsis. A sparkline ignores labels.
| Source | Share |
|---|---|
| Organic | 80% |
| Direct | 75% |
| Referral | 70% |
| Social | 65% |
<figure class="radial-chart labels">
<figcaption>Website visitors by source</figcaption>
<table>
<thead>
<tr><th>Source</th><th>Share</th></tr>
</thead>
<tbody>
<tr><th>Organic</th><td>80%</td></tr>
<tr><th>Direct</th><td>75%</td></tr>
<!-- One row per source -->
</tbody>
</table>
</figure>
A gauge keeps its half circles and hangs the names under its base line, a row for each ring with the innermost on top. Each name follows a line down from its ring's start.
| Seats | Count |
|---|---|
| Filled | 18 |
| Offered | 21 |
| Total seats | 25 |
<figure class="radial-chart gauge labels" data-max="25">
<figcaption>Team capacity</figcaption>
<table>
<!-- The same table as the gauge above -->
</table>
</figure>
Segments and gradients
Set data-segments to split each ring into that many
segments, with the ring gap between them. The chart fills whole segments
up to the one nearest the value, and a value under half a segment draws
none. The tooltip shows the value as written.
Add gradient to shade each fill along its ring, from a light
tint of its color at the start to the full color where the fill
ends. It works on full rings, gauges and segments alike.
| Survey | Score |
|---|---|
| This quarter | 78% |
| Score | 78% |
<figure class="radial-chart gauge gradient" data-segments="10">
<figcaption>Customer satisfaction</figcaption>
<table>
<thead>
<tr><th>Survey</th><th>Score</th></tr>
</thead>
<tbody>
<tr><th>This quarter</th><td>78%</td></tr>
</tbody>
<tfoot>
<tr><th>Score</th><td>78%</td></tr>
</tfoot>
</table>
</figure>
Ring size and tracks
--md-comp-radial-chart-thickness sets a ring's width and
--md-comp-radial-chart-gap the space between rings, both
in hundredths of the diameter. When there are too many rings to
leave a middle, the rings and gaps thin together to fit. Set --md-comp-radial-chart-track-color
to transparent to hide the tracks behind the rings.
| Source | Share of goal |
|---|---|
| Organic | 80% |
| Direct | 75% |
| Referral | 70% |
| Social | 65% |
<figure class="radial-chart"
style="--md-comp-radial-chart-track-color: transparent; --md-comp-radial-chart-gap: 3">
<figcaption>Visitors by source, share of goal</figcaption>
<table><!-- … --></table>
</figure>
Chart cards
Put a chart in a card with the figures it explains. The card adds
no chart styles. Size the rings with
--md-comp-radial-chart-height, which sets their diameter.
- Response rate
- 72%
- +14 points from last month
| Month | Response rate |
|---|---|
| This month | 72% |
| Last month | 58% |
| This month | 72% |
<article class="outlined">
<div class="p-4">
<dl class="stats my-0">
<div>
<dt>Response rate</dt>
<dd>72%</dd>
<dd class="stat-change up">+14 points from last month</dd>
</div>
</dl>
<figure class="radial-chart" style="--md-comp-radial-chart-height: 180px">
<figcaption class="visually-hidden">Response rate by month</figcaption>
<table><!-- … --></table>
</figure>
</div>
</article>
sparkline is a 48px chart with no legend or tooltip, for
a small dial or gauge beside a figure that already explains it.
Values and colors
Cells read as in a column chart. A cell's number is its
data-value, or its text without currency signs, percent
signs or grouping commas. A ring's fill is its value's share of
data-max, 100 by default. The chart ignores a maximum of
zero or less. Values past the maximum close the ring. A value under
about one ring's width of arc draws as a dot at the start, and zero,
negative and missing values leave only the track. Their rows stay in
the legend and the tooltip. A chart whose cells are all gaps shows
its table.
Rings take primary, tertiary,
secondary and on-surface-variant in turn.
A fifth ring repeats the first color.
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 drawn rings, total and legend
are hidden from assistive technology.
The plot is one Tab stop named by the caption. Down and right move to the next ring inwards. Up and left move to the one before. Both wrap between the innermost ring and the outermost. Home and End jump to the ends, and Escape hides the tooltip. On a touch screen, tap a ring to show it. In a right-to-left page the rings and the arrows still run clockwise. The tooltip is a polite live region, so a screen reader hears each row's values as it moves. A sparkline is not focusable. In forced colors the rings keep their colors so the legend still keys them.
JavaScript
AutoInit() starts every .radial-chart. To
start one yourself, call RadialChart.init. After changing its
ring tokens, or its table without data-watch, call update() to redraw it in place. It has the
line chart's methods. The max option sets the maximum
when the figure has no data-max, and min
does not apply.
const chart = Expressive.RadialChart.init(document.querySelector('.radial-chart'));
chart.show(1); // highlight the second ring; -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 .radial-chart.
| Token | Default |
|---|---|
--md-comp-radial-chart-height | 240px, the rings' diameter; 48px for a sparkline |
--md-comp-radial-chart-thickness | 8, a ring's width in hundredths of the diameter |
--md-comp-radial-chart-gap | 2, the gap between rings in the same units |
--md-comp-radial-chart-track-color | surface-container-highest; transparent hides the tracks |
--md-comp-radial-chart-color-1 … -4 | primary, tertiary, secondary, on-surface-variant |
For a selective Sass build, include "radial-chart" in $components.