New CSS features can sometimes make it easier and more efficient to code designs we already knew how to create. This efficiency can come from less code or hacks, or improved readability thanks to the new features.
In that spirit, let’s take a look at what’s under the hood of a bar chart.
We begin by laying out a grid.
.chart {
display: grid;
grid-template-rows: repeat(100, 1fr);
/* etc. */
}The graph statistic is based on a percentage, as in 'a number' out of 100.” Let's say we're working with a grid with 100 rows. That should be a stress test, right?
Then we add the bars to the grid with the grid-column And grid-row properties:
.chart-bar {
grid-column: sibling-index();
grid-row: span attr(data-value number);
/* etc. */
}I want to point out a few things right away. First is that sibling-index() function. It is brand new and incomplete browser support at the time of writing this (come on, Firefox!), although it is currently supported in the latest Chrome and Safari (but apparently not on iOS). Second is that attr() function. We've had it for a while, but it was recently upgraded and now accepts data attributes. So if we have one in our markup, like data-value="32" — that's something the function can read.
With these present, that's basically all we need to create a pretty nice bar chart in vanilla CSS! The following demo has fallbacks so you can still see the end result if your browser has not adopted these new features:
Yes, that was easy to do, but it's best to know exactly Why it works. So let's break that down.
Automatically set grid columns
Explaining the sibling-index() function on the grid-column property expressly places the list items in consecutive columns. I say 'explicitly' because we tell the grid exactly where to place each item data-value attribute in the layout. It goes first
in the second column, and so on.That's the power of sibling-index() — the grid intelligently generates the order for us without having to do it manually via CSS variables.
/* First bar: sibling-index() = 1 */
grid-column: sibling-index();
/* ...results in: */
grid-column: 1;
grid-column-start: 1; grid-column-end: auto;
/* Second bar: sibling-index() = 2 */
grid-column: sibling-index();
/* ...results in: */
grid-column: 2;
grid-column-start: 2; grid-column-end: auto;
/* etc. */Automatically set grid rows
It's pretty much the same! But in this case, each bar covers a certain number of rows, based on the percentage it represents. The grid extracts these values from the data-value attribute in the layout, effectively telling the grid how high each bar should be in the chart.
/* First bar: data-value="32" */
grid-row: span attr(data-value number);
/* ...results in: */
grid-row: span 32
/* Second bar: data-value="46" */
grid-row: span attr(data-value number);
/* ...results in: */
grid-row: span 46The attr() function, if provided with one datatype parameter (the parameter value number in our case), cast the value retrieved by attr() in that specific type. In our example the attr() function returns the value of data-value as one type, which is then used to determine the number of rows for each bar.
Let's create different graphs!
Since we've got the basics of this approach down, I thought I'd push things a little further and show how we can apply the same techniques to all kinds of diagrams that only work with CSS.
For example, we can use grid-row values to adjust the vertical direction of the bars:
Or we can skip the bars altogether and use markers instead:
We can also swap the columns and rows for horizontal bar charts:
Packing
Pretty exciting, right? Just look at all the ways we got this stuff done before today sibling-index() and an upgrade attr():
#CSS #Bar #Charts #Modern #Features #CSS #tricks


