Jason Vertucio

You know me! ™

I made a 3D bar graph using just HTML and CSS


(tap on the graph to reset the animation)

I’m all proud of myself for making this. It’s actually for work, where I build specialised digital presentations for iPads, so this really only works on browsers like Safari and Chrome. I also threw together some Firefox support, but this only works in the latest and coolest of browsers, so if you have, like, Firefox 4 or almost any version of Internet Explorer, this won’t work.

I had some help. This article at Codrops gave me the initial idea. But, like other “3D CSS Bar Graph” articles I found, all of them create the illusion of 3D by creating triangular borders around the bars.

That is not my desired goal as it’s not built in a 3D space, so I cannot rotate around the view. So, I made my own.

Creating the Graph

A few things to note: This may not be very semantic and HTML5, but I was thinking of this more as a 3D-world exercise and less an HTML exercise.

1. The lines are the easy part

The lines are mostly a graphic I threw together in Photoshop. Nothing special, just a PNG with a transparent background and a horizontal line every 50 pixels.

The vertical lines are all created in CSS. I forgot to take them into account when I made the PNG file, so I just added them separately.

2. The slightly harder part: the graph itself

First, I needed the graph’s dimensions. I decided, arbitrarily, on a 500×230 graph, and for the 3D aspect, I’d make it 100 pixels deep. I knew I’d need three faces for this project: the wall in the back, a wall on the side, and the floor. So, I made three DIVs, #side-wall, #back-wall, and #floor.

The HTML structure looks basically like this:

 
<div id="graph">
<div id="back-wall"><!-- numbers along the back wall --></div>
<div id="side-wall"><!-- numbers along the back wall, as well as a Y-axis indicator --></div>
<div id="floor"><!-- Pubs go here --> <!-- I mean bars --></div>
</div>

And the CSS went like this:

#graph {
	width:500px; height:230px;
	position:relative;

	-webkit-transform:translate3d(0,0,0) rotateX(-20deg) rotateY(-40deg);
	-webkit-perspective:3000px;
	-webkit-transform-style:preserve-3d;
}
#back-wall {
	width:500px; height:230px;
	border-left:2px solid #333333;
	border-right:2px solid #333333;
	background:url('lines.png')repeat-x left top;
	position:absolute;
	bottom:0; left:0;
	-webkit-transform:translate3d(0,0,-100px);
}
#side-wall { 
	width:100px; height:230px;
	border-left:2px solid #333333;	
	background:url('lines.png')repeat-x left top;
	position:absolute;
	bottom:0; left:0;
	-webkit-transform:translate3d(0,0,0) rotateY(90deg);
	-webkit-transform-origin:left center;
}
#floor {
	width:502px; height:100px;
	position:absolute;
	background-image:-webkit-linear-gradient(left,rgba(20,30,40,1) 0%,rgba(80,90,100,0.8) 80%);
	background-image:-moz-linear-gradient(left,rgba(20,30,40,1) 0%,rgba(80,90,100,0.8) 80%);
	bottom:0; left:0;
	-webkit-transform:translate3d(0,0,0) rotateX(90deg);
	-webkit-transform-origin:left bottom;
	-webkit-transform-style:preserve-3d;
}

Note that the back wall is pushed back 100 pixels on the Z-plane. If not, it would be the front wall. Also, the -webkit-transform-style definition is “preserver-3d.” If it were not specified, it would default to “flat,” which would flatten the entire DIV when viewed, like a pancake. Or Wile E. Coyote after slamming into a cliff.

You’ll also notice that I’m playing with the -webkit-transform-origin definition. This is so that when I rotate these walls on their respective axes, they rotate on the proper “hinge.” And the #floor has the same preserve-3d definition as the #graph. That’s because each bar of the bar graph sits on the floor.

3. The hard part: Bars

So, since I’m building a bunch of bars on a bar graph in a 3D realm, I knew I’d have to build every single visible surface. To make things easy, I threw each bar up against the back wall. This essentially meant I don’t have to bother with any bar backs. (Oh, let the puns fly.)

So, I just needed a top, a front, a left, and a right. If I put the left-most bar flush with the side wall, I wouldn’t even need a left-side for that. Since there would be many bars on the chart ( corresponding to each data point), I made classes out of them.

 
<div id="floor">
<div id="bar-1" class="bar">
<div class="front"><span>65%</span></div>
<div class="right"></div>
<div class="left"></div>
<div class="top"></div>
</div>
<!-- etc -->
</div>

The front has the notation for 65% wrapped in a span so I can control its position on the wall. It probably should have been a DIV but that just feel right to me, even though I had to make the SPAN a block-type element.

.bar {
	width:50px;height:50px;
	position:absolute;
	background:rgba(128,0,0,0.3);
	-webkit-transform:translate3d(0,0,0);
	-webkit-transform-origin:left bottom;
	-webkit-transform-style:preserve-3d;
}
.bar div {
	width:50px;height:50px;
	position:absolute;
	left:0; top:0;
	background:rgba(168,0,0,0.5);
	-webkit-transform:translate3d(0,0,0);
}
.bar .front {
	height:100px;
	text-align:center;
	-webkit-transform-origin:left bottom;
	-webkit-transform:translate3d(0,0,0) rotateX(-90deg);
}
.bar .front span {
	display:inline-block;
	-webkit-transform:translate3d(0,20px,0);
}
.bar .right { 
	height:100px;
	-webkit-transform-origin:right top;
	-webkit-transform:rotateX(90deg) rotateY(-90deg);

	-moz-transform-origin:right top;
	-moz-transform:rotateX(90deg) rotateY(-90deg);
}
.bar .left {
	height:100px;
	-webkit-transform-origin:left top;
	-webkit-transform:rotateX(90deg) rotateY(90deg);
}
.bar .top {
	width:50px !important; height:50px !important;
	-webkit-transform:translate3d(0,0,100px);
}

I set a default height for each bar to be 100px, but then defined each #bar-1 or #bar-2 (etc) manually, depending on the height. The 100px part is completely not necessary. But the 50×50 part is. Also, since the bars are on the floor, which was rotated up, I have to remember that the X-Y-Z planes I’m used to dealing with are all jogged up. I couldn’t even tell you which Rotate applies to which plane anymore. I just guessed. (help?)

Easy, right?

So anyway, that’s really it. I invite you to share your techniques on creating a 3D bar graph. As much fun as this was to create, I’m sure there are other ways, some better than others.

Comments are closed.