Pure CSS Isometric Bar Chart
Earlier this week, I was given a design to cut up that included an isometric bar chart (or is it graph?). When the design was in process, one of our developers saw it and thought it wouldn’t be possible in CSS alone like the other charts we have done for the same project. Myself and Cameron, one of Hashrocket’s designers, took that as a challenge and came up with a way to create it. As a disclaimer, though, this is not how I would prefer to visualize this manner of data. You’ll see why shortly.
First off, here’s a screenshot of the design I’m talking about:

As you can see, the labels are listed from bottom up, and opposite the usual direction you would see the data ordered if you had just spit out a list in HTML. There’s tricky ways of reversing the order with CSS, but it’s incredibly brittle and requires specific CSS for each bar. With the data set we were using, this wasn’t a problem since it made as much sense going in the other direction.
The problem I have with coding a chart in this way is that you can’t just measure heights in percentages; you have to convert them to pixel heights. This means that you can’t just change the dimensions of the container and have the bars resize to match. To make a large version of this chart, you’d have to cut a larger background image as well as adjust a number of pixel dimensions.
The first step is to determine how the HTML will be structured. I went with an article containing an unordered list because of the fact that they were listed in reverse, and didn’t want to put a reverse order on them improperly. I added the bar containers as divs and the labels as paragraphs. Our basic HTML for this graph looks like this:
<article>
<ul>
<li>
<div class=”bar”>100%</div>
<p>August</p>
</li>
<li>
<div class=”bar”>55%</div>
<p>July</p>
</li>
…
</ul>
</article>
If you’ve created CSS charts before, this should look familiar. If this were a flat bar chart, the bar divs would have a width or height of the same percentage depending on whether the chart’s orientation is vertical or horizontal. I won’t get in to detail on how those types of charts are marked up, but if you want a good example, check out the A List Apart article on the subject.
Focusing on the bars themselves, we have what looks like three parts to them: the top, repeating middle, and bottom. However, to make sure our bar works even if it is a height of 0, we need to use two parts. If we used three, the top and bottom pieces would be a fixed height, and would end up overlapping and ruining the illusion if the size of the bar was too small. Since we’re only using two elements, we need two background images. The first is just the top diamond, the second is the bar with the bottom. If you’re familiar with the sliding doors technique, you’ll see where I’m going with this. I cut the bar image with the bottom at a height greater than the maximum height of each bar, ensuring that there’s always enough background to cover the bar. The addition of the top element is simple enough. I added a top div within the bars and wrapped the measurement in a paragraph, so the bar HTML now looks like this:
<div class=”bar”>
<div class=”top”></div>
<p>100%</p>
</div>
You could substitute adding the top div with an :after pseudo-element, but I was coding this to provide the same visual in IE7 and :after isn’t going to work.
Now it’s down to the fun stuff. To start, I’ve set the maximum height of each bar to be 150px. Since the top piece is a height of 35px and we want to ensure it’s always visible, the minimum height cannot be less than 17px. This will become evident once you see the way the top piece is positioned. With this in mind, to have a maximum height of 150px you need to add 17px to the calculated height, making the actual height measurement 167px. For example, if our bar is 87% of the total, you would take the decimal representation of the percentage (.87) and calculate the height like this:
<div class=”bar” style=”height: #{.87 * 150 + 17}px”>
Styling the CSS bars involves positioning the bar from the bottom of the list item, and letting it expand upwards. We also need to position it off the left edge of the list item by half of the width of the bar to prevent the list item’s background color from bleeding through to the left half of the base. The top element is positioned relatively within the bar, poking out above it by half the height of the top piece. In this case, it’s 17px. You then have the CSS for your bar! The hardest part is over.
article li .bar {
position: absolute;
left: -30px;
bottom: 0;
z-index: 1;
width: 60px;
background: transparent url(“bg_iso-graph_bottom.png”) 0 100% no-repeat; }
article li .bar .top {
position: relative;
top: -18px;
height: 35px;
background: transparent url(“bg_iso-graph_top.png”) 0 0 no-repeat; }
Now to style the list items. This is pretty straightforward with the exception of the fact that each needs a fixed width in order to position the bars while also preventing the background color from bleeding through to the left half of the bottom of each bar. For the demo, I used :nth-of-type pseudo-classes to add a fixed width to each list item, but if you’re going to support most modern browsers you’ll probably need classes to do this.
article li {
position: relative;
clear: both;
float: right;
padding: 2px 20px 2px 0;
margin: 0 0 2px 0;
text-align: right;
text-transform: uppercase;
list-style: none;
text-shadow: 0 1px 1px rgba(0, 0, 0, .2);
background: #888888; }
article li:nth-of-type(1) { width: 149px; }
article li:nth-of-type(2) { width: 183px; }
article li:nth-of-type(3) { width: 217px; }
article li:nth-of-type(4) { width: 251px; }
article li:nth-of-type(5) { width: 285px; }
article li:nth-of-type(6) { width: 319px; }
article li:nth-of-type(7) { width: 353px; }
article li:nth-of-type(8) { width: 387px; }
If you’re using SASS to generate your CSS, you can use a loop to spit these widths out, but that’s beyond the scope of this article. If you’re interested, the SASS reference has a thorough explanation of how to do this.
The last bit is optional, but I think it adds a nice finishing touch. Remember the paragraph containing the measurement of each bar? Normally, you’d just hide that and have it around for accessibility’s sake, but why not display it when you mouse over the bar? A quick positioning of the paragraphs and hiding them initially will do the trick.
article li .bar p {
display: none;
position: absolute;
left: 110%;
bottom: 2px;
z-index: 2;
text-align: left; }
article li .bar:hover p { display: block; }
Other than some padding and margin resets, that’s all you need to make this style of isometric graph. You can check out the finished product here. Some suggestions on customizing it would be to adjust the sizes, use different colors for each bar, have more than one bar per item for a stacked bar chart, or even write some JavaScript to pull out the percentage values and translate that to each bar’s height.