Back

Creating dynamic column graphs using CSS

An article on producing dynamic, graphically rich column graphs, using structural xHTML and CSS.

Many companies like to put graphs on their websites. Quite often, these graphs can roughly be divided into two groups. Group one: graphically rich graphs, usually created by a graphics agency against a price per hour, that need to be removed from the website weeks after publication, because the data displayed is not accurate any longer and the image of the graph itself cannot be altered. Group two: graphs generated by server applications that show accurate information, but sometimes look jaggy and not too exciting, nor reflect the corporate style of the company they were created for.

The goal is to create a column graph in which, for instance, the income development of this year of a certain company may be visualised against the year before.

The graph

I believe it would be nice to create a three dimensional tube, positioned in another tube that is cut half through. The inner tube represents the company’s turnover development of a certain month this year and the the outer tube represents the turnover of exactly the same month, yet in the year before.

The illustration below demonstrates the graph that I would like to generate dynamically and completely drawn by the browser’s render-engine using CSS.

Column charts

Difficult, you may think? Dread not, nor be dismayed. The good news is: it’s not that hard.

The structure

The graph itself is positioned inside one large container:

To make life easier for the end user, stripes were added to this division, by means of a background image.

Within the division mentioned above, there’s a subdivision which is intended to be used for the graph’s scale. It’s named ‘columnGraphScale’ and is followed by the divisions for the months.

The scale

The markup containing the scale only would look like this:

  700
  680
  660
  640
  620
  600
  580
  560
  540
  520

I choose to make every number dividable by hundred black, corresponding every fifth, darker line in the background.

The CSS for the scale looks like this:

div#columnGraph p { 
font-size: 9px; 
margin: 0px; 
height: 10px; 
width: 25px; 
text-align: right; 
display: block; 
padding: 0px 10px 0px 0px; 
overflow: hidden;  
color: gray;  
line-height: 9px;  
background: url(images/body.gif) no-repeat left center;  
}  

div#columnGraph p b {  
color: black;  
}

Each paragraph has a fixed height of 10 pixels. This equals the interval distance of the background lines. It would harm the legibility of the numbers that are super imposed on the lines if the lines were visible through the numbers. Therefore do the paragraphs get an opaque background. As the background in this case contains a ramp, the background image of the parent division may be reused in order not to destroy the layout for that matter.

The columns

Each column consists of two major parts: the inner tube and the outer tube. Each of these elements will be translated from an <a href> in the markup. Of course an other element might have been used for this purpose, such as span or div, though by using an a-element the possibility is kept open to add a links to pages containing detailed information on the month concerned at any given time.

Naturally, all style elements of the tubes will be declared in the CSS, except for the height of the tubes and the distance between the different columns. These must be written right into the markup as this information is content, not style and is thus due to change.

The markup looks like this:

<div>
  <a style="height: 270px" href="#"></a>
  <a style="height: 354px" href="#"></a>
</div>

For each of those two elements a background image, a width and a z-axis position are set in the CSS.

a.oldResult {  
  width: 38px;
  display: block;
  float: left;
  position: absolute;  
  z-index: 1;
  font-size: 9px;
  text-decoration: none;  
  background: url(images/columGraphColumSkin.gif) repeat-y center top;  
  text-align: center;  
  padding: 0px;  
  margin: 0px 11px 0px 11px;  
  bottom: 0px;  
  color: black;      
  }

An absolute positioning, a z-axis of 1 and the positioning set bottom will make sure that the tubes will grow upwards instead of down. For the background the image columnGraphColumSkin.gif was used, set vertically aligned top.

Antialiasing

  • Left: Microsoft Internet Explorer 6.0 without PNG-24 transparency support and with GIF-replacement images.
  • Right:Other browsers, such as Mozilla FireFox, Safari, Opera and other browser with PNG-24 transparancy support.

The GIF-format was used to keep all of the above compatible with Microsoft Internet Explorer 6-. In order not to waste most other browsers’ precious PNG-24 transparency support, nice, semi transparent and anti aliased PNG images were applied by means of a child selector hack.

html>body a.oldResult {
background: url(images/columGraphColumSkin.png)
            repeat-y center top;  

}

About the same was applied on the elements ‘newResult’ as on the elements ‘oldResult’, but with a z-index of 2, to make sure the inner tube will be drawn in front of the outer tube.

 a.newResult { 
   width: 26px;
   display: block;
   float: left;
   background: url(images/columnGraphColumnCentre-new.gif) no-repeat center top;  
   position: absolute;
   z-index: 2;
   font-size: 9px;
   color: black;
   text-decoration: none;
   text-align: center;
   margin: 0px 17px 0px 17px;
   padding: 0px;
   bottom: 0px;
   font-weight: bold;
 }

Both elements are pushed into position within the division that contains them: <div class="columnGraphColumn">. This division is used for displaying the column’s base. We need this image only in case there is no desire to use the outer tube in the graph.

The images of the inner tube and the outer tube contain a certain top white space and the roundness causes the readout height (the horizontal cut through of the tubes’ top surface) to differ from the absolute height of the image. This value, including a few pixels that we need at the bottom to finish the bases, say twenty-four pixels, may be added to make up for the difference. The nicest way to to fix this would be to add a bottom margin of twenty-four pixels to the a-element in the CSS. An elegant solution yes, however, MSIE’s broken box model redirects this elegant solution straight to the dustbin. Therefore 24 pixels need to be added to each value. On my scale every five pixels equals a difference of ten. In this case the formula is:

a-element height = 0.5 ∙ desired value %2B 24

The month januari was no bad month, as the turnover increased from a measly 492 to a good 660. This gives us, using the formula above, the values 270px and 354px and a matching graph.

In order to make the base of the columns just as neat as the upper part, it has to be covered by an extra division with a background image. Another another div, containing the column titles is also added and the actual statistic data may be written down the a href of each tube to make sure there can be no misunderstanding about the readout of the results of this year. Problems could occur reading out last year’s result, when it sinks behind the inner tube. The outer tube however, is only used as a quick, visual indication to show whether turnovers went up or down and it’s value could be left out. I will add this value to markup however in this case the markup below is the result:

<div>
  <a style="height: 270px" href="#">492</a>
  <a style="height: 354px" href="#">660</a>
  <div />
  <div>januari</div>
</div>

The desired result is now there and adding the other eleven months will make the graph complete.

The legend

A graph like this, wherein multiple kinds of information are combined, needs a legend. As a legend is basically a list of all colours (or shapes) used in the graph and what they mean, it seems logical to use an unordered list for this in the markup. Each list element gets a class to differentiate them from each other.

<div id="legend">
  <ul>
    <li>omzet 2003</li>
    <li>omzet 2004</li>
  </ul>
  <p>x1.000.000 €</p>
</div>

Last but not least adding a scale proportion of 1 : 1.000.000 will not only make this company’s results look much better, but will also finish the graph.

comments powered by Disqus