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:
Next, we need to add our single pie element to a page:
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
40 * 3.6). For segment representing 100%, the angle would be
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%:
A note on the
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:
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:
Now we can pass in our percentage values to the CSSOM using the
style attribute by setting the
--segment[n] properties, as shown below:
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:
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:
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.
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:
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:
You could also use this technique to highlight a specific segment when a user interacts with your user interface.
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: