If you’re a web developer or a designer working on the web you’ve probably had to type:

if($i MOD 2){
	$class = "odd";
	$class = "even";

Or something similar nigh on a million times.

Frustration with Tables

One of the things I do at work is develop internal tools.  And one particular tool on which I’ve worked extensively contains approximately eighty kajillion tables (lots of data and reports).  Over the years the tables had lost real consistency of appearance due to different developers with different missions and different skill-sets working on the tool.

I’ve tried to enhance those tables a little as I worked — I’m not able to devote a lot of time and attention right now to design, but at minimum I want to make them a little more predictable from page to page and improve readability.  This has been the first time I’ve had cause to deal extensively with styling tables using CSS.  Most of the things I’ve worked on since abandoning tables for layout myself didn’t have any legitimate tabular data!

One of the things I’ve struggled with most has been table borders.  The border conflict resolution model as specified has made it difficult to get the borders I personally find pleasing.  Then there are column groups.  Of course the browsers all adhere correctly to the CSS specification that says background-color and text-align don’t apply to columns & column groups–which is a really nonsensical decision in the spec (one time I’d have preferred the browsers to go rogue).  Then there’s that classic row striping example I opened with.

CSS3 solutions

Styling tables with CSS is one of the more complicated and frustrating experiences I’ve had.  Yes, more than floats.  But there are a few things CSS3 offers that are proving incredibly helpful.  Yay for CSS3!  It’s now possible to set up row and column striping defaults, without needing other developers to remember to use your classes, just by using the CSS3 nth-child selector, and rgba.


CSS3 has this nth-child selector that can take the keywords “odd” and “even” as an argument, like so:

/* Rows */
table tr:nth-child(odd){}
table tr:nth-child(even){}
/* Columns */
table td:nth-child(odd){}
table td:nth-child(even){}


If you combine nth-child with rgba, you can set up row and column striping that works no matter your table background. That is, assuming you don’t overwrite the striping as defaulted.

/* Rows */
table tr:nth-child(odd){background-color:rgba(255,255,255,0.06);}
table tr:nth-child(even){background-color:rgba(0,0,0,0.06);}
/* Columns */
table th:nth-child(odd),
table td:nth-child(odd){background-color:rgba(255,255,255,0.06);}
table th:nth-child(even),
table td:nth-child(even){background-color:rgba(0,0,0,0.06);}

And Table Borders

It’s also possible to get the border defaults I like if you use rigorously structured tables and use your imagination with the selectors.

The border conflict resolution model states,

  1. Borders with the ‘border-style’ of ‘hidden’ take precedence over all other conflicting borders. Any border with this value suppresses all borders at this location.
  2. Borders with a style of ‘none’ have the lowest priority. Only if the border properties of all the elements meeting at this edge are ‘none’ will the border be omitted (but note that ‘none’ is the default value for the border style.)
  3. If none of the styles are ‘hidden’ and at least one of them is not ‘none’, then narrow borders are discarded in favor of wider ones. If several have the same ‘border-width’ then styles are preferred in this order: ‘double’, ‘solid’, ‘dashed’, ‘dotted’, ‘ridge’, ‘outset’, ‘groove’, and the lowest: ‘inset’.
  4. If border styles differ only in color, then a style set on a cell wins over one on a row, which wins over a row group, column, column group and, lastly, table. When two elements of the same type conflict, then the one further to the left (if the table’s ‘direction’ is ‘ltr’; right, if it is ‘rtl’) and further to the top wins.

Even though some of the rules seem backward to me, it’s possible to use them to your advantage.  But when dealing with a complex and highly structured table, it can be rather onerous to get just the right defaults set up in anything approaching a “robust” manner.  I was pleased to discover that most browsers support borders on rowgroups (thead, tbody, tfoot).   It’s that last rule that starts to give me the most grief, however.  When I place a border between thead and tbody I don’t want the tds in the tbody to “win” the border conflict.  Same with tfoot tds!

This can be solved by not collapsing (border-collapse) borders.  But trying that has its own set of frustrations and I rarely like the results.   Here’s where I make that conflict resolution model work for me.  In my case I find a suitable default to be a solid border around the table, thead, and tfoot and a dotted border between td and th.


  • Obviously IE6, IE7, and IE8 don’t support rgba or nth-child.
  • Tables in Firefox have shown a variety of oddness too myriad to log.
  • Up-to-date versions of Chrome, Safari, Opera, and Firefox support the examples equally well.