Designing Data Part 2: Adding Style

The goal of this two-part article is to demonstrate how to create XHTML-compliant tables and how to style them effectively using CSS.

In Part 2, we apply style to the structure of our document that we created in Part 1.

Part 2: Adding Style

The TABLE element

To make our table look good, I'll touch on a few of the more important properties: border, border-collapse, border-spacing and empty-cells.


The border property is similar to the frame element in HTML. It controls the border that surrounds the table and not the borders of each individual cell.


There are two values to choose from: collapse and separate.

In the collapsed model, there is no spacing between cells. Since borders are shared between cells, there is the possibility that there are two competing styles for a particular border. There is a border conflict resolution guide from the W3C to help determine which style will be displayed. It goes as follows, where the first rule has precedence over the next:

  1. border-style of hidden
  2. border-width
    1. higher values
      1. rules attribute in HTML
      2. stylesheet border-width property
      3. frame attribute in HTML
    2. lower values
      1. rules attribute in HTML
      2. stylesheet border-width property
      3. frame attribute in HTML
  3. border-style
    1. double
    2. solid
    3. dashed
    4. dotted
    5. ridge
    6. outset
    7. groove
    8. inset
  4. element
    1. cell
    2. row
    3. row group
    4. column
    5. column group
    6. table
  5. border-style of none

Firstly, IE doesn't follow these rules exactly. It gives precedence to the table border over all others. It also seems to treat a border-style of hidden like a border-style of none, giving precedence to everything else. The second thing to mention is that there is no rule on how to handle differences in colour between cells. Therefore, you may end up with a different rendering in every browser (one quick test did, in fact, give me three different renderings in the three browsers I tested in).

It's not indicated on the W3C site but I've added extra precendence rules under border-width after behaviour encountered while testing.

With a separate model, there is a space between the borders of cells. This is the default property and is probably what you're used to seeing. To adjust the space between cells, use the border-spacing property.

Setting the border-collapse property to separate makes Firefox and Opera ignore the rules attribute and I can't find any W3C standard that seems to support this behaviour.


The border spacing can be specified using either one or two length values. If specified only with one then the value affects the spacing on all sides. If two values are specified then the first specifies the horizontal spacing (left and right of the cell) and the second specifies the vertical spacing (above and below the cell).

Our nemesis IE, however, does not support the border-spacing property. Which means you're left using the cellspacing property in HTML to achieve the same. Not to worry, cellspacing is still in XHTML 1.0 Strict.

Another thing to note is that the colour that appears in this space between the borders is always the table background. Setting the row or cell background will never change the colour in between the cells.


The empty-cells property has two values: show and hide. It determines whether a border will be visible on an empty cell. This property can also be applied to almost any element within a table such as specific rows or cells.

IE, again, does not support the empty-cells property. This would almost be funny if it weren't so annoying. Luckily, we have somewhat of a workaround. Just set the rules and frames properties in HTML (covered in Part 1). Then, any border applied to the cell using CSS will appear.

Styling Columns: COLGROUP and COL

The column group elements are unique in that cells do not actually inherit anything from them. Therefore, there are only four properties that can be set: border, background, width and visibility. These properties are filled with inconsistencies across the browsers so be prepared!


In Opera, if this property is applied to a colgroup and the right side of the colgroup is the edge of the table then it won't be drawn. Setting the border attribute in HTML to 0 or setting the frame attribute to void will also remove any border on the table frame.

In Firefox, everything appears to work as it should.

In IE, this property isn't supported.


In a wierd exception, Firefox is the one that doesn't support this property. Both Opera and IE support it.

UPDATE: Firefox 0.9 just came out and now supports the background property. Horrah!


The width property almost works perfectly in all browsers tested. The exception is that it's not supported in Opera on the colgroup element.

Keep in mind that when applied to a colgroup, the width affects the size of each column contained within. For example, if you set a width of 200 pixels on a column group which contains two columns then each column is 200 pixels, for a total of 400 pixels for the column group.


When applied to a column or column group, the visibility property can only be set to collapse. Unfortunately, none of the browsers tested supported this property.

Aligning text within a cell

There are two properties in CSS that allow you to set the alignment within a cell. There's text-align (the align attribute in HTML) and the vertical-align (the valign attribute). These properties can be applied to any element except for the table element and the column group elements (colgroup and col) .

To align text within a column, we have to do a couple tricks together in order to get it to work in the majority of browsers (including all the ones I tested). We set the align and valign attributes in HTML on the colgroup or col tag (which, as mentioned in Part 1, is not supported in Mozilla/Firefox). To get things working in Mozilla and Firefox is a little more involved.

  1. Set the id attribute on the column header. Example: <th id="qty">Quantity</th>
  2. Then set the headers attribute to the same as the id for that column. Example: <td headers="qty">123</td>. I didn't cover these attributes in Part 1 as, according to the W3C, are better purposed for addressing accessibility for which I didn't want to get into with this article. However, they'll come in handy here!
  3. Now using CSS, we can use an attribute selector to put it all together. Example: td[headers=qty] { /*put styles here*/ } The attribute selector works in Mozilla, Firefox, and Opera...but not in IE.

IE allows you to specify font and alignment properties on column group elements but this is specifically not allowed as part of the current CSS spec.

The Caption

Captions can be formatted like most other block elements including properties like text-align and font-weight. There is an additional CSS property that can come in handy: caption-side. It can be set to either top or bottom and will allow the caption to appear either above or below the table respectively. Firefox takes it a step further and supports values of left or right. I'm sounding like a broken record but IE does not support caption-side.

Margins and Padding

Cells do not have margins but have padding. You can apply a margin to the table which will affect the amount of space around the table. Firefox correcly applied the margin between the caption and the table whereas IE and Opera apply the margin on the outside of the caption.


When in separate border mode, you can only set border styles on the table or cells. Border styles on row and column elements will be ignored. In collapsed border mode, both Firefox and Opera allow you to set border style on row and column elements. IE treats collapsed mode like separate mode and will not let you set border style on row and column elements either way.

With a bug in Opera, if you have a table with a thead, tbody, and tfoot and you've applied a border style to the thead then it will also appear on the top side of the tfoot. Vice versa, if a border style is applied to the tfoot then it will appear on the bottom site of the tbody.

Time for some action

I hope I was able to be informative without being too dry. I haven't touched on a number of CSS properties such as text properties or selectors such as :hover. Maybe I'll extend this into a running series!

I will leave you with a coupleexamples that will hopefully inspire you to try new things. For the examples, I've searched the web for some data tables and formatted them to varying degrees.

  • Check out the original and then check out the example. For this example, I've kept the formatting essentially the same. The original was coded using css-styled font tags which certainly seemed redundant. I cleaned the table structure and removed any of the formatting tags that were used. I moved the table title into a caption tag and used CSS to apply the border. This alone dropped the file size of the table from about 4k down to less than 1.5k.
  • I grabbed another sample table off the web and gave it my own styling. This time I was able to reduce the page size from 9k to 4 mostly from just removing the font tags. But I was also able to trim space by removing the valign="top" on all the cells. From a style point of view, I kept it pretty simple (I like simple). I gave the header cells a background colour and applied padding to all the cells. I used the rules and frame attributes to create a rule between each row of the table. This same look could also have been achieved by applying a border on the cells instead. The last thing I did was set a background colour on the first column. To do this, I made sure my table had some col tags to define my columns. Then I set a class on the first column and set the background colour in my class.


For more information:

Browsers tested:

Published June 13, 2004 · Updated September 17, 2005
Categorized as HTML and CSS
Short URL:


13 Comments · RSS feed
Fred said on July 17, 2004

Great article , I hope this will help some people using table layout to start with css , this is exactly I was searching for , thanks , I do not have to write about it myself . I was bit angry , because everyone say no table , but I think its not a way too change some developers thinking , this is right step too the future . Sorry for my english

Eugene T.S. Wong said on August 02, 2004

I'd appreciate reading any techniques you have on making use of :hover.

I personally think that table has got to be the most useful element around. If we can master it, then we won't need CSS anymore. Just kidding. Just kidding. Seriously, though, I wish that we could get more use out of our tables. For example, 1 of my web pages, , has a table where the user can hover over the times in the schedule. The highlighting of the row acts as a dot leader. I can't seem to get it to work for the column for obvious reasons. However, I'm sure that there is a way to do it with standards compliant CSS.

As browsers get more compliant, people are going to want to get more out of their web pages. I also want to combine those 2 tables to make 1 big table, which would be harder for people to read across & down, especially when they want to compare alternate routes. That is why I'm hoping for a :hover technique for producing some kind of highlighting dot leader. Imagine trying to read a table with 33x33 cells. Ouch!

I speculate that the technique will involve using the background of table, col, row, & cell, all in the same rule or same set of rules. I'll be sure to get back to you, if I can figure it out.

Also, mastering tables will allow us to extract data more readily. Companies can print accounting reports using table & friends.

Jonathan Snook said on August 02, 2004

Eugene: Unfortunately, there is no inheritence from the column elements to the cell elements (hence the reason font properties aren't supported for column elements in the CSS spec). This means there's no way to pull off a [colgroup td:hover {...}] approach.

The best way to accomplish this is going to be through script -- which some might argue is the best place for it since it lends to the seperation of behaviour from presentation and content.

Eugene T.S. Wong said on August 03, 2004

Jonathan, thanks for replying. Now that you say that, it makes sense. I guess that I get too caught up in trying to move everything over to CSS.

I guess I'll go search for some tutorials on scripting. I must admit, though, I'm a bit hesitant, because it seems hard, plus there is the whole problem of consistency across browsers. But then again, CSS has the consistency problem as well.

What scripting language do you advise?

Also, are you telling me that :hover, :active, shouldn't have been implemented in CSS, & that it should have been kept with scripting?

Jonathan Snook said on August 03, 2004

Eugene, JavaScript is the only language you should be using for client-side behaviour. If you haven't done much scripting before then it may certainly be a bit of an uphill battle for your to pull off what you're looking for... I wouldn't be surprised if somebody has already put together something similar.

I'm certainly not saying not to use :hover or :active. I fall into the "do what you need to get the job done" camp. And in this case, JavaScript would be the best way to do it.

Eugene T.S. Wong said on August 06, 2004

Jonathan, thanks for the advice & explanation.

In case anybody is curious, here is an update of what I managed to get done. In between the time that I asked & saw your reply, I managed to try a bit of JavaScript. It works much faster than using :hover. Unfortunately, I still can't highlight the columns. I wanted to use JavaScript to change the style of a col, but for some reason it still doesn't work. Based only on what I know, I would have to write code to highlight each cell as opposed to a whole row & column @ the same time. I'm not going to worry about it much.

I'm sure that as time goes on, I'll learn about it enough to know what to do, if it is doable.

Small Paul said on August 10, 2004

Eugene - I think I see your problem. Both CSS and JavaScript act only on the HTML presented to them. Think of your HTML document as one hierarchy, like your hard drive (with HTML elements instead of folders) - Firefox's DOM Inspector (in Tools) will show you.

In HTML, tables are inherently defined row by row. The colgroup and col elements provide some information about the column arrangement, but in terms of the HTML hierarchy, a column doesn't exist: you've just got table cells inside rows. This is why neither CSS nor JavaScript can style a column in a line of code.

JavaScript is the way to go for your problem *because* you can write code to highlight each cell, which you can't do in CSS. It's definitely more complicated, as scripting is: you've got all the cross-browser incompatibilities of HTML and CSS, with added complexity due to the fact that JavaScript is a fully-fledged programming language. Thus if your only experience is with HTML and CSS, you'll be struggling for a while (as I did for about a year).

However, it's well worth it. JavaScript can add a great layer of functionality to your pages, enhancing usability - just as you're trying to do with your column highlight. is a mine of useful JavaScript information, and O'Reilly's JavaScript: The Definitive Guide is great if you want to really get detailed.

Eugene T.S. Wong said on August 11, 2004

Hi Paul.

Thanks for the explanation. I think that the thing that originally tripped me up was the W3C specs. It basically said that screen readers would read the th contents along the top & the sides. Since CSS allows us to style col elements, I thought that there would be a way to work with it somehow. It really took me a while to come to the understanding that this wasn't going to be easy.

Also, I have to bear in mind that Opera, my browser of choice doesn't seem to implement the DOM completely. I noticed that with some of the tutorial examples, I am unable to see the differences. I'll probably have to download Firefox and/or Mozilla.

I'll give a serious look into the web sites that you suggest.

It's a shame that browsers don't have features for dealing with tables.

Thanks for your reply.

Henrik said on June 16, 2005

" due to the fact that JavaScript is a fully-fledged programming language."

Nope, it's definately not.

John C. said on June 25, 2005

Wow!!! Cool comments bar!!!
Styles is very useful/ Thank you for this article. I will post some advice about CSS in my LS soon/ Check it.

Jonathan Julian said on November 18, 2005

"You can apply a margin to the table which will affect the amount of space around the table. Firefox correcly applied the margin between the caption and the table whereas IE and Opera apply the margin on the outside of the caption."

Actually, it looks as though Firefox incorrectly applies the margin between the caption and the table. See the CSS 2.1 spec. IE 6 and Opera 8 correctly apply margin outside of the caption; Firefox 1.0.7 does not.

Sandy said on October 04, 2006

Thanks for describing border-collapse and border-spacing. This is what I was looking for. Hopefully this tables will be looking good in IE7

codeslinger said on December 06, 2008

I know the above conversation took place about a million years ago, but this web page is still being used as a reference on table styling. So I just thought I'd mention a reasonable approach to solving the table column hi-light problem.

Note that I do not claim it to be the "best" approach, I leave it to somebody else to figure out whatever that is. But I think you will find this a usable approach.

What you do is you create a bunch of classes, one for each column (mytableC1, mytableC2, etc) . Then you define each cell to be a member of the class for it's column

Now all you need is a minimal JavaScript to change the color property for the column class via styles. You need only change one value and the whole column lights up.

You can apply other properties in this way as well which would overcome some of the limitations of table columns not being accessible as a group. Essentially you are creating a column group by using a class.

This won't scale well if you have a lot of columns, but for the 33 column table being discussed above it should work fine.

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.