Twitter RSS

CSS Stacked Bar Graphs

by Stephen on December 9th, 2008

To design the stats feature of Backbone, our Ruby on Rails CMS, we needed to show a stacked bar graph of page views vs unique visitors. I looked around for a sample of how others did stacked bar graphs and came up empty handed. There are plenty of CSS bar graph interpretations, but none of them did stacked bar graphs. So I’ve done it here. Based off Alen Grakalic’s Pure CSS Data Chart.

The Markup

The first thing I do anytime I start HTML/CSS work is code the HTML. It’s a personal preference to make sure it’s all going to look good for screen readers and be as semantic as possible, and since it’s my article we’re going to do the HTML first.


<dl id="csschart">
<dt>Mon</dt>
<dd>36</dd>
<dd>30</dd>
</dl>

Like Alen Grakalic’s implementation, I used a definition list. I found it to be the most semantic way of presenting data like this. To explain, each day of the week is in a <dt> tag and the data for that day is in a <dd> tag. There are two <dd> tags for each day, one for our page views and one for our unique visitors.


<dt>Mon</dt>
<dd class="p36"><span><b>36</b></span></dd>
<dd class="sub p30" ><span><b>30</b></span></dd>
</dl>

Each <dd> is given a class that corresponds to the percentage of that bar. p100 is 100% the height of the graph and p0 is 0% the height of the graph. Making 100 classes is a pain and a lot of CSS but it makes it so easy when you’re turning these graphs out dynamically.

The other class to which we’ve added some of the <dd> tags is “sub”. This denotes the stacked bars in the graph that will be put on top of the other <dd>.

Finally, we wrap our data in <b> and <span> tags so we have enough to work with when we’re styling it.

Styling the Graph.


dl#csschart, dl#csschart dt, dl#csschart dd{
margin:0;
padding:0;
}
dl#csschart{
background:url(../images/bg_chart.gif) no-repeat 0 0;
width:454px;
height:360px;
padding-left:11px;
}
dl#csschart dt{
display:none;
}
dl#csschart dd{
position:relative;
float:left;
display:inline;
width:33px;
height:330px;
margin-top:22px;
}
dl#csschart span{
position:absolute;
display:block;
width:33px;
bottom:0;
left:0;
z-index:1;
color:#555;
text-decoration:none;
}
dl#csschart span b{
display:block;
font-weight:bold;
font-style:normal;
float:left;
line-height:200%;
color:#fff;
position:absolute;
top:5px;
left:3px;
text-align:center;
width:23px;
}

/* Styling the Bars. */

dl#csschart span{
height:50%;
background:url(../images/bar.png) repeat-y;
}
dl#csschart .sub{
margin-left:-33px;

}
dl#csschart .sub span{
background:url(../images/subBar.png) repeat-y;
}

dl#csschart .p0 span{height:0%}
dl#csschart .p1 span{height:1%}
dl#csschart .p2 span{height:2%}
dl#csschart .p3 span{height:3%}
dl#csschart .p4 span{height:4%}
dl#csschart .p5 span{height:5%}

/*This continues until 100%*/

The first thing we do is reset all the margins and padding to make it look the same on all browsers. Then we define the width and height of our chart in dl#csschart. We’re using a background image to help with the tick marks on the axes and for some background gridlines. One important thing to note is the padding-left. This moves the first bar over past the tick marks on the left of the background image.

Next, we hide the dt tags. There is really no good way to make them do what we want. Position the dd tags to float left and use margins to push them to the correct place in our background image. Now, just to be clear, the dd tags are not our bars, they are simply containers for our bars. They are all 100% of the height of our graph. We use the span tags inside the dd tags to fill in our bars. Since they’re positioned absolute inside our positioned dd tags we can use bottom:0; to place them on the x-axis and have them grow in height from there. We use the b tag to display the actual data value inside the bar.

At the bottom we define the background image for the bars in dl#csschart span (you can easily just make it a background color instead), then we position our .sub bars overtop by giving them a margin-left of -33px (the exact width of the bars).

Finally, we make our 101 classes for each percentage point from 0% to 100% to give our bars their height.

I’ve made a diagram to explain what each tag is doing.

Add the Axes.

Ok now you’ll notice we don’t have labels on our axes. So we use some simple unordered lists. One before the cssChart:


<ul class="yAxis">
<li>100</li>
<li>90</li>
<li>80</li>
<li>70</li>
<li>60</li>
<li>50</li>
<li>40</li>
<li>30</li>
<li>20</li>
<li>10</li>
</ul>

and one after the cssChart:


<ul class="xAxis">
<li>Mon</li>
<li>Tue</li>
<li>Wed</li>
<li>Thu</li>
<li>Fri</li>
<li>Sat</li>
<li>Sun</li>
</ul>

Styling the Axes.


ul.xAxis{
margin:0 0 0 27px;
padding:0;
float:left;
clear:left;
display:inline;
width:454px;
}
ul.yAxis{
margin:14px 0 0 0;
padding:0;
display:inline;
float:left;
}
ul.xAxis li{
float:left;
list-style:none;
width:33px;
text-align:center;
}
ul.yAxis li{
list-style:none;
height:33px;
text-align:right;
float:left;
clear:left;
}

Styling the axes is really just floating the the y-axis left and using some margins to push it in place. Then you can use some margins to push the li tags to line up with the tick marks of your background image.


Tags:
Posted in Design 19 Comments »


19 Responses to CSS Stacked Bar Graphs

Leave a Comment
  1. Can you describe what advantages something like this has over simply using an off-the-shelf graphing solution (e.g. Google Charts or http://www.filamentgroup.com/lab/creating_accessible_charts_using_canvas_and_jquery/ )

    I suppose there are some advantages (highly customizable, can add links/actions to chart elements), but the downsides would seem to be considerable. Most notably you’re restricted to bar-graph style charts, and even then if you want to vary the style (e.g. horizontal .vs. vertical bars), you’ve got to invent a whole new set of CSS.

    In the past when I’ve developed HTML/CSS graph solutions, the result ends up being hard to work with and maintain. I invariably end up needing it to do something other than what I originally intended, and spending hours trying to figure out some CSS “hack” to get it to all render properly.

  2. Well Broofa, I don’t disagree with you that going with Google Charts or some sort of Canvas solution is by far more powerful than doing something with plain CSS/HTML. This however is pretty quick to set up if you know what you’re doing, doesn’t require a learning curve of some other language or framework, and its also very lightweight. If you need to do some pretty hardcore graphing, than by all means add a framework or hook up to Google’s API, but if you need to do just one bar graph, or just bar graphs alone I think its overkill to make extra HTTP requests with Google or add a framework of JS just to make a bar graph.

  3. Alex Shevlyakov  December 11, 2008

    Very well code. Look at this http://forexwebtools.com/activewebcharts.php

  4. Using a DL tag for this is improper. If you dont use tags for what they are meant for, you might as well just use tables.

    I would make these either all LIs or DIVs.

  5. Michael Fasani  December 12, 2008

    Nice tutorial, thanks

  6. This sucks. Why does every newbie have to publish his rubbish experiments on the web?

  7. Sorry, but turn off css and you will see that nonsense markup.

  8. osterello  December 14, 2008

    Very interesting! I use it on my blog http://www.ecogiochi.it with games chart

  9. Pepa Baros  December 15, 2008

    Hi Steve, it does not matter if somebody turn off his css, or even if he turn off his browser. What matters is your users, if they find your charts usefull.
    I use this type of graphs for many years on intranet for visualization of production plan, production results, downtime and many others. If you want to see some screenshots and description of intranet applications where bar charts are used, please visit http://swatelier.info site.

  10. This is great, thanks!

  11. J.B. Nicholson-Owens  December 18, 2008

    A relatively small quibble: “dl#csschart” is worse than “#csschart” and no more meaningful.

    Firefox’s CSS parser will actually slow down on “dl#csschart” versus “#csschart”.

    “#id-name” can only refer to one element in the page, so denoting what kind of element #id-name isn’t necessary and such changes also require updating should you decide to use another kind of element at that ID.

  12. Ross Bruniges  December 19, 2008

    The element, man I’ve not seen that for ages and considering you spoke about wanting to use semantic mark-up I found that a big surprise!

    Also as you mention screen-readers during the article too and wanting to keep content accessible to them you really shouldn’t be using display: none on content. The majority of screen-readers will not read content set to display: none out. Use position: absolute, left: -99999em instead

  13. Ross Bruniges  December 19, 2008

    Argh – mark-up removed from comments. Of course I was refering to the use of b tags ;>

  14. Beejamin  January 1, 2009

    I’d be inclined to use an ordered list for the axis labels – the order of the list items is critical after all.

    Not sure about the use of the DL’s – I’d like to see a scheme which uses a table as the underlying structure – it’d no doubt be a pain to get working in older browsers though. It would solve the problem of ‘CSS off’ though.

  15. Simon Sigurdhsson  January 2, 2009

    <b>? Really, you should be using strong elements instead, as they have a semantical meaning as opposed to the pure-style purpose of b elements.

    However, the idea of creating stacked bar graphs using HTML and CSS is a bit redundant – graphs are much better represented using images, and with correct use of alt attributes screen readers will benefit more than they do from this example.

  16. Rene Fournier  February 5, 2009

    Can someone help me figure out why my cart doesn’t simply items in IE 6/7, but does in Safari and Firefox? (And how to fix it). Thanks.

    …Rene

  17. It’s cool

  18. Fantastic piece of tutorial! I am going to add it to my CSS aggregator website.Hope you dont mind!

Join the Discussion