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 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%:
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:
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.
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:
- Chrome - In development and available behind feature flag in v63
- Safari - In development
- Firefox - Under consideration
- Edge - Under consideration