Talking of charts… When was the final time you had to make use of a pie chart? In case you are a kind of individuals who have to provide displays proper and left, then congratulations! You might be each in my private hell… and in addition surrounded by pie charts. Fortunately, I believe I haven’t wanted to make use of them in ages, or no less than that was till lately.
Final yr, I volunteered to make ta webpage for a youngsters’ charity in México1. All the things was fairly normal, however the workers wished some knowledge displayed as pie charts on their touchdown web page. They didn’t give us quite a lot of time, so I admit I took the straightforward route and used considered one of the various JavaScript libraries on the market for making charts.
It appeared good, however deep down I felt soiled; pulling in an entire library for a few easy pie charts. Appears like the straightforward manner out moderately than crafting an actual resolution.
I wish to amend that. On this article, we’ll strive making the right 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 “good” ought to adjust to.
So as of precedence:
This should be semantic! Which means a display reader ought to be capable to 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 basically, it’s simply extra enjoyable this fashion.
As soon as we’re executed, we must always get a pie chart like this one:
Is that this an excessive amount of to ask? Perhaps, however we’ll strive it anyhow.
Conic gradients suck aren’t the perfect
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, then you definitely’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 ingredient and a single line of CSS…
Nevertheless, this technique blatantly breaks our first aim 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.
Do not forget that gradients are photos, so displaying a gradient as a background-image doesn’t inform display readers something concerning the pie charts themselves; they solely see an empty ingredient.
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 prefer to, its syntax is just too good to cross so let’s no less than 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 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 this can be a adequate markup for the duty:
Candies bought final month
Goodies
Gummies
Onerous Sweet
Bubble Gum
Ideally, that is all we’d like for our pie chart, and as soon as types are executed, 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 received’t be learn out loud by display readers, so we’ll need to append it to the tip 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, no less than when testing in NVDA. Right here it’s in Home windows:
You will have some questions concerning 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 share immediately?
We may simply write them inside every
, however utilizing attributes we are able to get every share on CSS via the attr() perform. And as we’ll see later it makes working with CSS an entire lot simpler.
Why ?
The
ingredient can be utilized as a self-contained wrapper for our pie chart, and in addition to photos, it’s used so much for diagrams too. It turns out to be useful since we may give it a title inside after which write out the info on an unordered listing, 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 readers can learn the corresponding share for every merchandise, which is arguably crucial half. Nevertheless, we might have to visually present the legend, too. Meaning there isn’t any 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
We now have our knowledge on paper. Now it’s time to make it seem like an precise pie chart. My first thought was, “This must be straightforward, with the markup executed, we are able to now use a conic-gradient()!”
Effectively… I used to be very incorrect, however not due to semantics, however how the CSS Cascade works.
Let’s peek once more on the conic-gradient() syntax. If we’ve the next knowledge:
Merchandise 1: 15%
Merchandise 2: 35%
Merchandise 3: 50%
…then we might write down the next conic-gradient():
.gradient {
background:
conic-gradient(
blue 0% 15%,
lightblue 15% 50%,
navy 50% 100%
);
}
This principally 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 difficulty? The pie chart is drawn in a single conic-gradient(), which equals a single ingredient. You could not see it, however that’s horrible! If we wish to present every merchandise’s weight inside data-percentage — making the whole lot prettier — then we would want a approach to entry all these percentages from the mother or father ingredient. That’s not possible!
The one manner 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, nevertheless, that we are able to’t use conic-gradient(), however moderately we’ll have to make use of a couple of.
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 high 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 come in useful later for preserving 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 at the moment restricted to Chromium as I’m penning 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 models. So we’ll parse the data-percentage attribute as a after which divide it by 100 to get our share 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 share, after which turn out 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 assist for the brand new attr() syntax:
Now that each one the slices are executed, you’ll discover every of them begins from the highest and goes in a clockwise route. We have to place these, , 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 is determined by the variety of objects that precede it. We’ll need to rotate an merchandise by no matter measurement the slice earlier than it’s. It might be perfect to have an accumulator variable (like --accum) that holds the sum of the chances earlier than every merchandise. Nevertheless, as a result of manner 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. Nevertheless it appears we’re pressured into two choices:
Hardcode the --accum variable on every ingredient.
Use JavaScript to calculate the --accum variable.
The selection isn’t that onerous if we revisit our targets: hardcoding --accum would negate versatile HTML since transferring an merchandise or altering percentages would drive us to manually calculate the --accum variable once more.
JavaScript, nevertheless, makes this a trivial effort:
With --accum out of the way in which, we are able to rotate every conic-gradient() utilizing the from syntax, that tells the conic gradient the rotation’s place to begin. The factor is that it solely takes an angle, not a share. (I really feel like a share also needs to work wonderful, however that’s a subject for an additional time).
To work round this, we’ll need to create yet one more variable — let’s name it --offset — that is the same as --accum transformed to an angle. That manner, we are able to plug the worth into every conic-gradient():
This little little bit of CSS arranges all the slices within the lifeless middle of the .pie-chart container, the place every slice covers the container’s solely row and column. They slices received’t collide as a result of they’re correctly rotated!
Aside from these overlapping labels, we’re in actually, actually good condition! Let’s clear that stuff up.
Positioning labels
Proper now, the identify and share labels contained in the are splattered on high 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 how one can lay issues out in a circle utilizing the newer CSS cos() and sin(). Give these hyperlinks a learn as a result of there’s quite a lot 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 system:
360deg * var(--weighing)) / 2: Will get the share as an angle then divides it by two to seek out the center level.
+ var(--offset): Strikes the angle to match the present offset.
- 90deg. cos() and sin(): The angles are measured from the proper, 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 share for every merchandise are nonetheless stacked on high of one another. Fortunately, fixing it’s as straightforward as translating the share a bit of extra on the Y-axis:
Let’s make sure that that is screenreader-friendly:
That’s about it… for now…
I’d name this a very good begin towards a “good” 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 okay, but when it isn’t offered, we must always nonetheless present a approach to let CSS generate the colours. Maybe a very good job for color-mix()?
What about several types of charts? Bar charts, anybody?
That is sorta screaming for a pleasant hover impact, like perhaps 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 comply with up with one other piece (get it?!). Additionally, nothing is ideal with out numerous suggestions, so let me know what you’ll change or add to this pie chart so it may be really good!
1 They’re nice individuals serving to youngsters via extraordinarily troublesome occasions, so in case you are inquisitive about donating, you could find extra on their socials. ↪️