This article explores a technique for creating a single element CSS pie chart that can display an arbitrary set of values, using a few basic style rules.

NOTE: The examples in this article use experimental CSS features. At the time of writing only Chrome 69+ implements everything required to view the examples.

Creating a static, single element pie chart

Let's get the basics out of the way. First, we'll need a few style rules to create the pie shape:

.pie {
  /* Basic layout */
  display: inline-block;
  width: 3.75em;
  height: 3.75em;
  border-radius: 50%;

  /* A little styling */
  border: .15em solid #fff;
  box-shadow: 0 .075em .2em .05em rgba(0,0,0,.25);

  /* fixes a minor clipping issue in Chrome */
  background-origin: border-box; 

Next, we need to add our single pie element to a page:

<div class="pie"></div>

Drawing of the chart segments will be handled by the CSS conic-gradient function. We'll be using the gradient colour stops to draw the individual segments of our pie chart.

Unlike its linear and radial counterparts, a conic gradient uses an angle to position colour stops. To determine the colour stop angle for a given segment, we need to multiply its percentage value by 3.6. So, for a segment representing 40%, the angle would be 144deg (40 * 3.6). For segment representing 100%, the angle would be 360deg (100 * 3.6).

Unfortunately, colour stop angles aren't relative to the previous stop so, as we step around the gradient drawing pie segments, we need to sum all the previous segment angles and use the resulting value as an offset for the current segment. Here's how to draw a three segment pie chart with values of 20%, 45% and 35%:

.pie {
  background-image: conic-gradient(
    #d44 72deg,      /* 20%               20  * 3.6 =  72 degrees */
    #fc3 0 234deg,   /* 45%         (45 + 20) * 3.6 = 234 degrees */
    #ac0 0           /* 35%       last stop is always 360 degrees */
A single element, 3 segment pie chart using conical gradients

A note on the color-stop syntax

I'm using the double-position color-stop syntax in these code samples to create a "hard" colour stop for each segment. Some browsers don't support this syntax so, to prevent linear interpolation between each stop, you'll need to add additonal color-stops between each segment, like this:

.pie {
  background-image: conic-gradient(
    #d44 72deg,      /* 20%             20  * 3.6 =  72 degrees      (end of segment 1) */
    #fc3 72deg,      /* 45%       (45 + 20) * 3.6 = 234 degrees    (start of segment 2) */
    #fc3 234deg,     /* 45%       (45 + 20) * 3.6 = 234 degrees      (end of segment 2) */
    #ac0 0           /* 35%     last stop is always 360 degrees    (start of segment 3) */

Dynamic pie charts

The previous example isn't very flexible. In practice, you'd need to generate a unique gradient for each pie chart you wanted to render. Fortunately, we can use CSS Custom Properties to work around this limitation.

If we take the previous example and replace the fixed angles in the conical-gradient function with var(--segment[n]), we can use CSS custom properties to control the generation of the gradient. To make life easier for content authors, we can also use the calc() function to handle the conversion of a percentage value to an angle. Here's the updated style rule for implementing all that:

.pie {
  background-image: conic-gradient(
    #d44 calc(3.6deg * var(--segment1)),
    #fc3 0 calc(3.6deg * var(--segment2)),
    #ac0 0

Now we can pass in our percentage values to the CSSOM using the style attribute by setting the --segment[n] properties, as shown below:

<div class="pie" style="--segment1: 40; --segment2: 70;"></div>
<div class="pie" style="--segment1: 20; --segment2: 50;"></div>
<div class="pie" style="--segment1: 10; --segment2: 80;"></div>
A set of single element, three segment pie charts rendered using custom properties

Great! It's now possible to configure each segment of our pie chart directly using markup. However, our pie chart is fixed to three segments. The next step is to remove that restriction.

Supporting a variable number of segments

Creating a variable number of segments is possible if we pass an additional argument to the var() function, allowing us to define a fallback value. Until now we've been using the following calculation to determine the stop angle of a segment:

calc(3.6deg * var(--segment[n]))

If we pass 100 as the second argument to the var() call, we can now resolve any missing --segment properties to 360deg, forcing them to render (invisibly) at the end of the gradient:

calc(3.6deg * var(--segment[n], 100))

This simple change allows us to support charts with different segment counts without having to write any additional CSS. All we have to do is ensure that there are enough gradient stops to cover the maximum number of segments our pie chart will display.

.pie {
  background-image: conic-gradient(
    #d44 calc(3.6deg * var(--segment1, 100)),
    #fc3 0 calc(3.6deg * var(--segment2, 100)),
    #ac0 0 calc(3.6deg * var(--segment3, 100)),
    #0ac 0 calc(3.6deg * var(--segment4, 100)),
    #f7b 0
<div class="pie"></div>
<div class="pie" style="--segment1: 40;"></div>
<div class="pie" style="--segment1: 40; --segment2: 70;"></div>
<div class="pie" style="--segment1: 40; --segment2: 70; --segment3: 80;"></div>
<div class="pie" style="--segment1: 40; --segment2: 70; --segment3: 80; --segment4: 90;"></div>
Five pie charts, each with a different number of segments, rendered from a single style rule

Updating the pie chart

Since we're using the CSSOM to control the gradient stops, it's trivial to add, remove and update (or animate) our chart values. A call to the setProperty() method of the pie chart element's style object is all that's needed to update a chart:

let chart = document.getElementById('myChart');'--segment1', 50);'--segment2', 70);
Five pie charts dynamically updated through the CSSOM, using JavaScript

Alternate colour options

For consistency reasons, I recommend using a modifier class to set alternative pie chart colours. However, if you need an extra level of configuration, it's also possible to pass segment colours using the style attribute. Here's an example that defines a default colour palette but also provides 'per-chart' overrides:

:root {
  --segment-1-color: #d44;
  --segment-2-color: #fc3;
  --segment-3-color: #ac0;

.pie {
  background-image: conic-gradient(
    var(--segment-1-color) calc(3.6deg * var(--segment1, 100)),
    var(--segment-2-color) 0 calc(3.6deg * var(--segment2, 100)),
    var(--segment-3-color) 0
<div class="pie" style="--segment1: 40; --segment2: 70;"></div>
<div class="pie" style="--segment1: 40; --segment2: 70; --segment1-color: #8f8; --segment2-color: #4dc; --segment3-color: #0bf;"></div>
<div class="pie" style="--segment1: 40; --segment2: 70; --segment1-color: #c6f; --segment2-color: #64f; --segment3-color: #02f;"></div>
Three alternatively styled pie charts using a single CSS rule

You could also use this technique to highlight a specific segment when a user interacts with your user interface.

Browser Support

Support for conical gradients is limited at the moment, but there are positive signals from browser vendors regarding implementation. Blink already supports these gradients in current stable versions of Chrome (behind a feature flag) and the Webkit team are working their implementation. If you would like to see Microsoft and Mozilla implement conic gradients, let them know by voting for the feature on the relevant tickets:

Personal Achievements

  • 2017 Web Designer Magazine: CSS VR interview
  • 2015 JS1k – winner
  • 2014 Net Awards: Demo of the year – winner
  • 2014 Net Awards: Developer of the year – longlist
  • 2013 Public speaking for the first time
  • 2011 .net Magazine innovation of the year – shortlist

Referenced in…

Smashing CSS, CSS3 for web designers, Programming 3D Applications with HTML5 and WebGL and more.

My work is referenced in a number of industry publications, including books and magazines.