Talking of charts… When was the final time you had to make use of a pie chart? If you’re a type of individuals who have to provide displays proper and left, then congratulations! You might be each in my private hell… and likewise surrounded by pie charts. Fortunately, I feel I haven’t wanted to make use of them in ages, or a minimum of that was till not too long ago.
Final 12 months, I volunteered to make ta webpage for a youngsters’ charity in México1. Every thing was fairly commonplace, however the workers needed some information displayed as pie charts on their touchdown web page. They didn’t give us a number of time, so I admit I took the straightforward route and used one in all the numerous JavaScript libraries on the market for making charts.
It regarded good, however deep down I felt soiled; pulling in a complete library for a few easy pie charts. Appears like the straightforward method out somewhat than crafting an actual answer.
I wish to amend that. On this article, we’ll strive making the proper pie chart in CSS. Meaning avoiding as a lot JavaScript as doable whereas addressing main complications that comes with handwriting pie charts. However first, let’s set some targets that our “excellent” ought to adjust to.
So as of precedence:
This should be semantic! That means a display screen reader ought to be capable of perceive the info proven within the pie chart.
This must be HTML-customizable! As soon as the CSS is completed, we solely have to vary the markup to customise the pie chart.
This could preserve JavaScript to a minimal! No drawback with JavaScript usually, it’s simply extra enjoyable this manner.
As soon as we’re achieved, we should always get a pie chart like this one:
Is that this an excessive amount of to ask? Possibly, however we’ll strive it anyhow.
Conic gradients suck aren’t one of the best
We are able to’t speak about pie charts with out speaking first about conic gradients. In the event you’ve learn something associated to the conic-gradient() perform, you then’ve doubtless seen that they can be utilized to create easy pie charts in CSS. Heck, even I’ve stated so in the almanac entry. Why not? If solely with one component and a single line of CSS…
We are able to have seemlessly excellent pie chart:
Nevertheless, this technique blatantly breaks our first purpose of semantic pie charts. Because it’s later famous on the identical entry:
Don’t use the conic-gradient() perform to create an actual pie chart, or every other infographics for that matter. They don’t maintain any semantic which means and will solely be used decoratively.
Keep in mind that gradients are photographs, so displaying a gradient as a background-image doesn’t inform display screen readers something concerning the pie charts themselves; they solely see an empty component.
This additionally breaks our second rule of constructing pie charts HTML-customizable, since for every pie chart we’d have to vary its corresponding CSS.
So ought to we ditch conic-gradient() altogether? As a lot as I’d wish to, its syntax is simply too good to move so let’s a minimum of attempt to up its shortcomings and see the place that takes us.
Bettering semantics
The primary and most dramatic drawback with conic-gradient() is its semantics. We would like a wealthy markup with all the info laid out so it may be understood by display screen readers. I need to admit I don’t know one of the simplest ways to semantically write that, however after testing with NVDA, I consider it is a adequate markup for the duty:
Candies offered final month
Goodies
Gummies
Onerous Sweet
Bubble Gum
Ideally, that is all we want for our pie chart, and as soon as types are achieved, simply enhancing the data-* attributes or including new
parts ought to replace our pie chart.
Only one factor although: In its present state, the data-percentage attribute gained’t be learn out loud by display screen readers, so we’ll should append it to the top of every merchandise as a pseudo-element. Simply keep in mind so as to add the “%” on the finish so it additionally will get learn:
So, is it accessible? It’s, a minimum of when testing in NVDA. Right here it’s in Home windows:
You might have some questions relating to why I selected this or that. In the event you belief me, let’s preserve going, but when not, right here is my thought course of:
Why use data-attributes as a substitute of writing every proportion immediately?
We may simply write them inside every
, however utilizing attributes we are able to get every proportion on CSS by way of the attr() perform. And as we’ll see later it makes working with CSS a complete lot simpler.
Why ?
The
component can be utilized as a self-contained wrapper for our pie chart, and in addition to photographs, it’s used quite a bit for diagrams too. It is useful since we can provide it a title inside after which write out the info on an unordered record, which I didn’t know was among the many content material permitted inside since is taken into account circulation content material.
Why not use ARIA attributes?
We may have used an aria-description attribute so display screen readers can learn the corresponding proportion for every merchandise, which is arguably crucial half. Nevertheless, we might have to visually present the legend, too. Meaning there is no such thing as a benefit to having percentages each semantically and visually since they could get learn twice: (1) as soon as on the aria-description and (2) once more on the pseudo-element.
Making it a pie chart
Now we have our information on paper. Now it’s time to make it appear to be an precise pie chart. My first thought was, “This must be straightforward, with the markup achieved, we are able to now use a conic-gradient()!”
Effectively… I used to be very improper, however not due to semantics, however how the CSS Cascade works.
Let’s peek once more on the conic-gradient() syntax. If we’ve got the next information:
Merchandise 1: 15%
Merchandise 2: 35%
Merchandise 3: 50%
…then we’d write down the next conic-gradient():
.gradient {
background:
conic-gradient(
blue 0% 15%,
lightblue 15% 50%,
navy 50% 100%
);
}
This mainly says: “Paint the primary coloration from 0 to fifteen%, the following coloration from 15% to 50% (so the distinction is 35%), and so forth.”
Do you see the problem? The pie chart is drawn in a single conic-gradient(), which equals a single component. You might not see it, however that’s horrible! If we wish to present every merchandise’s weight inside data-percentage — making every part prettier — then we would wish a approach to entry all these percentages from the mum or dad component. That’s unattainable!
The one method we are able to get away with the simplicity of data-percentage is that if every merchandise attracts its personal slice. This doesn’t imply, nonetheless, that we are able to’t use conic-gradient(), however somewhat we’ll have to make use of multiple.
The plan is for every of this stuff to have their very own conic-gradient() portray their slice after which place all of them on prime of one another:
To do that, we’ll first give every
some dimensions. As a substitute of hardcoding a measurement, we’ll outline a --radius property that’ll turn out to be useful later for holding our types maintainable when updating the HTML.
Then, we’ll get the data-percentage attribute into CSS utilizing attr() and its new sort syntax that permits us to parse attributes as one thing aside from a string. Simply beware that the brand new syntax is presently restricted to Chromium as I’m scripting this.
Nevertheless, in CSS it is much better to work with decimals (like 0.1) as a substitute of percentages (like 10%) as a result of we are able to multiply them by different items. So we’ll parse the data-percentage attribute as a after which divide it by 100 to get our proportion in decimal type.
Lastly, we’ll get the data-color attribute from the HTML utilizing attr() once more, however with the sort this time as a substitute of a :
.pie-chart li {
/* ... */
--bg-color: attr(data-color sort());
}
Let’s put the --weighing variable apart for now and use our different two variables to create the conic-gradient() slices. These ought to go from 0% to the specified proportion, after which grow to be clear afterwards:
I’m defining the beginning 0% and ending 100% explicitly, however since these are the default values, we may technically take away them.
Right here’s the place we’re at:
Maybe a picture will assist in case your browser lacks help for the brand new attr() syntax:
Now that each one the slices are achieved, you’ll discover every of them begins from the highest and goes in a clockwise path. We have to place these, you realize, in a pie form, so our subsequent step is to rotate them appropriately to type a circle.
That is after we hit an issue: the quantity every slice rotates relies on the variety of objects that precede it. We’ll should rotate an merchandise by no matter measurement the slice earlier than it’s. It will be perfect to have an accumulator variable (like --accum) that holds the sum of the chances earlier than every merchandise. Nevertheless, because of the method the CSS Cascade works, we are able to neither share state between siblings nor replace the variable on every sibling.
And consider me, I attempted actually exhausting to work round these points. However it appears we’re pressured into two choices:
Hardcode the --accum variable on every component.
Use JavaScript to calculate the --accum variable.
The selection isn’t that tough if we revisit our targets: hardcoding --accum would negate versatile HTML since transferring an merchandise or altering percentages would pressure us to manually calculate the --accum variable once more.
JavaScript, nonetheless, makes this a trivial effort:
With --accum out of the best way, we are able to rotate every conic-gradient() utilizing the from syntax, that tells the conic gradient the rotation’s start line. The factor is that it solely takes an angle, not a proportion. (I really feel like a proportion must also work advantageous, however that’s a subject for one more time).
To work round this, we’ll should create yet one more variable — let’s name it --offset — that is the same as --accum transformed to an angle. That method, we are able to plug the worth into every conic-gradient():
This little little bit of CSS arranges the entire slices within the lifeless heart of the .pie-chart container, the place every slice covers the container’s solely row and column. They slices gained’t collide as a result of they’re correctly rotated!
Apart from these overlapping labels, we’re in actually, actually fine condition! Let’s clear that stuff up.
Positioning labels
Proper now, the identify and proportion labels contained in the are splattered on prime of each other. We would like them floating subsequent to their respective slices. To repair this, let’s begin by transferring all these objects to the middle of the .pie-chart container utilizing the identical grid-centering trick we we utilized on the container itself:
Fortunately, I’ve already explored learn how to lay issues out in a circle utilizing the newer CSS cos() and sin(). Give these hyperlinks a learn as a result of there’s a number of context in there. In brief, given an angle and a radius, we are able to use cos() and sin() to get the X and Y coordinates for every merchandise round a circle.
For that, we’ll want — you guessed it! — one other CSS variable representing the angle (we’ll name it --theta) the place we’ll place every label. We are able to calculate that angle this subsequent method:
360deg * var(--weighing)) / 2: Will get the proportion as an angle then divides it by two to search out the center level.
+ var(--offset): Strikes the angle to match the present offset.
- 90deg. cos() and sin(): The angles are measured from the precise, however conic-gradient() begins from the highest. This half corrects every angle by -90deg.
We are able to discover the X and Y coordinates utilizing the --theta and --radius variables, like the next pseudo code:
Oh wait, only one extra minor element. The label and proportion for every merchandise are nonetheless stacked on prime of one another. Fortunately, fixing it’s as straightforward as translating the proportion a little bit extra on the Y-axis:
I’d name this a very good begin towards a “excellent” pie chart, however there are nonetheless a number of issues we may enhance:
The pie chart assumes you’ll write the chances your self, however there must be a approach to enter the uncooked variety of objects after which calculate their percentages.
The data-color attribute is ok, but when it isn’t supplied, we should always nonetheless present a approach to let CSS generate the colours. Maybe a great job for color-mix()?
What about various kinds of charts? Bar charts, anybody?
That is sorta screaming for a pleasant hover impact, like possibly scaling a slice and revealing it?
That’s all I may provide you with for now, however I’m already planning to chip away at these at observe up with one other piece (get it?!). Additionally, nothing is ideal with out numerous suggestions, so let me know what you’d change or add to this pie chart so it may be actually excellent!
1 They’re nice individuals serving to youngsters by way of extraordinarily troublesome occasions, so if you’re serious about donating, you could find extra on their socials. ↪️