Mar 17
A CSS styled table
2005 at 01.48 am posted by Veerle Pieters
Further to my article about the creation of a CSS calendar the thought crossed my mind to show you an example on how you can style a table using CSS. The data of tables can be boring so all the more reason that we need to attract attention to it and make it as pleasant to read as possible. Presentation and design with some basic accessibility rules in mind is the way to go.

Advantages
Styling a table with CSS has mayor advantages since it separates the structural markup from presentation formatting. It offers extra flexibility on the way you present your table. You have the freedom to style each side of a table cell separately, isn't that cool.
Getting started
First I create a simple draft in Photoshop. I experiment by trying out some color combinations, bullets, colors for the alternating rows etc. If you can't decide on colors, then maybe these color calculators can help you out.
In the next step I export these 3 image files to style the background of the headers:
The structural markup
The coding in BBEdit is pretty simple. Here is some part of it:
<table id="mytable" cellspacing="0" summary="The technical specifications of the Apple PowerMac G5 series"> <caption>Table 1: Power Mac G5 tech specs </caption> <tr> <th scope="col" abbr="Configurations" class="nobg">Configurations</th> <th scope="col" abbr="Dual 1.8GHz">Dual 1.8GHz</th> <th scope="col" abbr="Dual 2GHz">Dual 2GHz</th> <th scope="col" abbr="Dual 2.5GHz">Dual 2GHz</th> </tr> <tr> <th scope="row" class="spec">Model</th> <td>M9454LL/A</td> <td>M9455LL/A</td> <td>M9457LL/A</td> </tr> ...
As you can see I've used the scope attribute to make sure that a my table makes sense in non-visual browsers. This attribute defines whether the header cell holds header information for a column (scope="col") or a row (scope="row").
The CSS styles
For the headers at the top I've used a background image to make them extra visible, except for the one on the left. For this one I created a class (.nobg I have the habit to always use 'bg' as an abbreviation of 'background'). Here is how the CSS code looks:
th {
font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica,
sans-serif;
color: #6D929B;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #CAE8EA url(images/bg_header.jpg) no-repeat;
}
th.nobg {
border-top: 0;
border-left: 0;
border-right: 1px solid #C1DAD7;
background: none;
}
The headers on the left that appear as rows (the specification headers) have alternating styles:
th.spec {
border-left: 1px solid #C1DAD7;
border-top: 0;
background: #fff url(images/bullet1.gif) no-repeat;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica,
sans-serif;
}
th.specalt {
border-left: 1px solid #C1DAD7;
border-top: 0;
background: #f5fafa url(images/bullet2.gif) no-repeat;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica,
sans-serif;
color: #B4AA9D;
}
The table cells that hold the technical specifications of each Power Mac G5 also have alternating styles:
td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
padding: 6px 6px 6px 12px;
color: #6D929B;
}
td.alt {
background: #F5FAFA;
color: #B4AA9D;
}
And this is the final result:
Hope you like my table ;-)
52served
1
Beautifully done. This is a very nice example of how the caption element can be used, one of the best I’ve seen yet.
I still have a tip though. You can use the ‘border-collapse’ property to get rid of the ‘cellspacing’ attribute in your HTML code.
And to end with a question, why have you used the abbr element when ‘L2 Cache’ isn’t actually a abbreviate. ‘L2’ on the other hand is, but in that case it would be more useful to use the abbr element in the cell itself.
Again, great job!
2
A quickie because I’m almost off to a meeting, but wouldn’t it be nicer to leave all those explicit classes out of the page code and move them to a script file?
Add
<script type=“text/javascript” src=“alt_rows.js”></script>
to the HTML file, drop the class=“alt” on the tds, change the css to read
tr.alt td {
background: #F5FAFA;
color: #B4AA9D;
}
instead of the td.alt there was there, and put something like this in the alt_rows.js file:
function checkItems() {
var r,l;
if (!document.getElementById) return
var tb=document.getElementsByTagName(“table”);
for (r=0; u<tb.length; r++) {
if (tb[r].className==‘alternate’) {
tr=tb[r].getElementsByTagName(“tr”);
doEven(tr);
}
}
}
function doEven(myRows) {
for (l=0; l<tr.length; l++) {
if (l%2==0) { tr[l].setAttribute(‘class’, ‘alt’);
tr[l].setAttribute(‘className’, ‘alt’); }
}
}
function addLoadEvent(func) {
var onloadBak=window.onload;
if (typeof window.onload!=‘function’) { window.onload=func; }
else {
window.onload=
function() {
onloadBak();
func();
}
}
}
addLoadEvent(checkItems);
...and you’re halfway there. The rest is fiddling with stuff to get the exact same lay-out, but you should be able to leave out almost all those nasty explicite classes and replace them with on ID and/or class on the table.
3
@Maarten, I was actually wondering about the cellspacing attribute. I’ve read somewhere that it couldn’t be ‘replaced’ by CSS… guess the article was wrong then? Haven’t used the ‘border-collapse’ before. Is this property supported in ‘all’ recent browsers btw?
About the ‘abbr’... to be honest here I sometimes am confused when and where to use it. People advice me all different ways that it gets confusing.
Glad you like my table.
@Michel, I know you informed me a while ago about using such script. It is indeed cool and can make things easier, but there is nothing wrong by using classes. I just wanted to show people that are less familiar with CSS how to style a nice looking table. Didn’t want to make it too complex or confusing and dependable on scripting.
4
That’s a very good option and when the table is growing, it can save you a lot of time removing or adding a row.
On the other hand, Javascript must be enabled to achieve this result so the best option would be having access to a server-side script language and generate the classes yourself.
(BTW: Smarty already supports this)
5
@Veerle: I see your point.
It’s just that once you have your tables styled properly, if you’re going to use the styling in a production environment, I think you’re better off moving as much of the non-essential presentation code as possible away from the content.
@Maarten: I don’t necessarily agree that server-side scripting generating the classes is always a better option. The same argument could be taken just a little further to defend adding bgcolors to all tds, but realistically speaking: how many users really have a modern browser with CSS turned on and javascript turned off? Not talking about W2K3 administrators or über-paranoid power users here, mind you…
6
(I will use the @ this time to clearify this thread a bit ;])
@Michel: I was just trying to say that if it’s absolutely necessary to use a so-called ‘cycled’ layout as is shown in the example, it may be a even better option to solve it on the server side than using the client side. I get your point though.
7
To clarify the issue with cellspacing, the CSS spec indicates that if you use “border-collapse: separate” then you would use “border-spacing” to adjust your cellspacing. BUT, Internet Explorer doesn’t support cellspacing—most other browsers do. If you plan to only have a solid border in between cells then use “border-collapse:collapse” and simply apply use “border:1px solid #000” to apply the border colour to all cells. With my example, there would only ever be 1px black border between cells. Check out my site for more info.
8
Very insightful. Just one thing, why repeat CSS, particularly with the th and the classes? Shouldn’t it cascade down into the other classes, allowing you to remove all the code except for the difference between the original and the new class?
For example, couldn’t th.spec and th.specalt be reduced to:
]th.spec {
background: #fff url(images/bullet1.gif) no-repeat;
}
th.specalt {
background: #fff url(images/bullet2.gif) no-repeat;
color: #B4AA9D;
}
I’ll have a fiddle with this a little later after work, but in theory, that will work, and it will save some kb in your CSS :)
9
@Brendan, you are ‘partially’ right ;-) The bottom and right border are already defined in the ‘th’, also text-transform and letter-spacing aren’t necessary. The rest isn’t the same and should remain there. Thanks for pointing this out.
I’ve saved 1/2 kb LOL! :-)
10
@Marrten, Expression Engine has similar possibilities by using certain EE tags, very handy ;-)
@Jonathan Snoock, thanks for the information. I’ll definitely check your article out, sounds very interesting ;-)
11
I hate the coding cops - that’s the reason why I no longer post html + css tutorials on my site. The table looks great in all browsers and works for people with disabilities, that should be more than enough.
The table is very easy to read and very nice looking, and that’s all I care about.
12
@mark, you make me smile :-) On the other as long as I learn from the comments here I have absolutely no problem with those picky ‘cops’ ;-) In fact here in Belgium, there is a saying ‘The cops, your friend’ (it is a kind of branding slogan :-P )
13
Thanks for pointing out how the ‘th’ tag is not just for column headings. I’ve had good results using the ‘thead’ and ‘tbody’ tags in order to style column and row headings differently. Instead of using a class you end up with CSS like this:
table thead th {
}
table tbody th {
}
14
Sheesh. I hope I’m not being branded a coding cop here :)
15
Very nice!
However - and I hope I’m not coming across as an accessibility cop here - the colours aren’t very accessible. I have perfect eyesight and have to strain my eyes a little to read the text.
A couple of good tools to check that the contrast and colour difference between foreground and background is sufficient:
Colour Contrast Check
Colour Contrast Analyser
Maybe you can add them to your list of colour tools :-)
16
@Michel, I feel I’m one myself sometimes ;-) I just found it a funny remark from mark.
@Roger, oh no certainly not, any advice is welcome here. I have to admit that I sometimes make the mistake of not thinking about that ... and working on an Apple Cinema Display has a factor in it too since you almost always see things a bit ‘better’ ;-) I’ve adjusted the text colors a bit. It is much better now I think.
Can I ask you a question? Since you are (I believe) an expert on this, I learned so much from your site. Do you know in this example how I should use the abbr attribute here? I am a bit confused to be honest on how to use it properly. Do you always have to use them anyhow even if the the header cell contains just 1 word? Any advice on this matter? Hope I’m not asking too much here. Thanks in advance.
17
Veerle: The abbr attribute is meant to provide shorter versions of table headers - the opposite of the abbr and acronym elements. The reason is to spare screen readers users from having to listen to very long headers over and over again, if their screen reader is set to announce the header before each data cell.
In most cases, table headers should be pretty short anyway, so the abbr attribute is optional. In the table you’re using for this example I would probably not use it. If I did, I’d just leave out the GHz from each header.
18
Veerle, what is the copyright on your examples? I’d love to use your calendar on my blog. I’d credit you in the source.
19
Cool Tutorial… :) Greetings from Indonesia.
20
Cool use of the table dude. :-)
However, increasing the text size appears to break it. I’m no CSS pro (I’m a newbie), but wouldn’t it be best if the little blue triangles lose the background color in its file (like the gold triangles)? You’d only have to set the bgcolor on it (via CSS) to give it its current appearance. That way, if the visitor increases the text size, the table’s appearance wouldn’t break.
Again, neat article! Keep it up.
21
@Roger Johansson, thanks for this clarification. This makes perfect sense to me now.
@Raymond, if you want to use them (both design and code) I would like a small visible credit link on the page itself. If it is only the code with another design (colors, background etc.) then credit in the source code is preferable, thanks ;-)
@Roshawn, you probably don’t really see it but there is a small gradient in the blue background. I should however use #f5fafa as a background color instead of white like I did so I adjusted this, thanks for pointing this out. If I enlarge now, things still look fine.
22
Like Greg said, thead and tbody are missing, since you are trying to use xhtml 1.0 at least a tbody should be added
(note that in fact you are not using xhtml in any way, by serving it as text/html, resulting in parsing it as html, your html is ok now, but your doctype is wrong)
Furthermore, the use of colgroups and cols could be recommended to, since you seem to be eager on accessibility. (and the first column is clearly distinct from the rest)
23
woops, let me rephrase that, in xhtml the tbody element is not mendatory anymore (in html it was, but the tags could be omitted), so this is in fact ok. Nevertheless, it would be a good idea to add tbody and thead
24
@Rikkert Koppes, Ok I see your point about thead and tbody. But I can’t follow you about my doctype being wrong :-S My page validates and if I look at the code that gurus/experts like Jeffrey Zeldman or Douglas Bowman or others use, I see the same text/html meta tag : <meta http-equiv=“Content-Type” content=“text/html; charset=ISO-8859-1” /> (except for the charset, some use UTF-8). What exactly should I use then? What exactly is wrong in my code? If you say A please say B and help me here :-) Thanks.
25
Ok, here goes:
- meta elements do not decide what type of document you have, nor do doctypes, it’s the mime type which does that.
- your document is served with the mime type of text/html
- this result in browsers parsing the document as being html tag soup.
- xhtml 1.0 recommends your document being served as application/xhtml+xml, xhtml 1.1 demands it.
- serving your document as application/xhtml+xml fails in IE, it simply does not recommend the mime type, in fact IE does not support xhtml at all (parses it as html)
so you document is treated as (malformed) html, in fact one could say it is html, therefore the doctype is wrong
also read my writings or Ians
26
I’m watching this closely…
for the interesting stuff. :)
27
@Rikkert Koppes, I understand what you are saying here, but I made the choice to use XHTML anyway. No offense but I tend to stick to what people who have proven themselves are telling me what is ‘best’ at this current time. Not that I am a blind follower or anything but they know their stuff. That it is wrong is your opinion and I do like to keep the fun part in all this and not become a code cop who breaks down every rule to find something for the purpose of finding :-)
28
@Rikkert Koppes: sending an xhtml 1.0 document as text/html will mean that it will be treated as _well-formed_ html. And there ain’t nothing wrong with well-formed html. Anyways, we’re diverging from the main topic of this whole post which is that tables CAN look good (thanks Veerle!).
29
@Rikkert, Veerle, Jonathan
I am not an expert here, I just happened to have read about this topic in the past week.
Rikkert already provided 2 intresting links. Let me add 2 others to that, one of which actually is defending xhtml used in conjunction with the text/html mime type:
- Case for xHTML
- On the fence
@Veerle
Nice tutorial yet again (as I’ve come to expect). I really like your taste of colours, keep it up!
30
Great timing with the tutorial, and what beautiful tables. I was just in the midst of trying to come up with a way to make a series of cross-tabulation reports for a client look better; and was massively inspired by your CSS. I’ve adapted to fit clients color scheme, multiple table types, etc. But WOW!! before your post, I was headed for delivering just a mildly readable report with a series of uninteresting tables (visually uninteresting, the data itself is useful).
I was actually getting depressed, well relatively uninspired, after making SQL jump through hoops to spit out the data, it was all just laying there on the page, looking as blah, as soot.
But now the first report looks so spiffy, that I’m actually inspired to go back and give a go at automating “summary statements” using thresholds and qualifiers. Now the page just calls to have some spot on text, highlighting trends, and since the tables are so easily read, who am I to deny.
31
I have been playing around with your tables example for a site that I am working on and when viewed in Firefox the borders seem to stay put when using the nobg class. When I set the borders to 0 in the nobg class that are still showing up in firefox. Has anyone else stumbled across this. Works fine in IE. (thats a first)
thanks
32
Congratulations,
I wanted to ask you, If you can allow me (given the credits, of course) to translate this article into spanish, in order to include it in my soon to relaunch blog ?
Please, reply me at juanluis(at)gmail.com
Regards,
Juan Luis
33
Wow, great job on the table. It looks really good
34
Cool tutorial. At least I don’t have to worry about tacky, web-default tables anymore. good job
35
By the way, my web design teacher hates when people use tables for anything other than tabular data. Out of about 120 lessons, about 60 of them mention this. I try not to use them, unless I’m making a template with photoshop, and I’m too lazy to code it in CSS, or it’s to complex to do. To make it a little better I just slice and code with dreamweaver. cuts out some of the extra tables. Then I edit it again with notepad or Html-Kit to get rid of the generated DW tags. Quite a process, but after this and your calendar articles, I now have an alternative to default styling of tables. BTW, does the default styling of tables look different on a Mac than it does on a PC?
36
Very nice table Veerle!
It makes an excellent weather table.
@Zeeru
See for yourself
37
@Zeerus “does the default styling of tables look different on a Mac than it does on a PC?”
No this is the same, there are (as far as I know) no differences.
38
With regard to using <th> for the horizontal headers: as the cells are both headers and containers of data, <td> should actually be used. It would still be possible to neatly select them in CSS using td[scope]. Note that the use of th would be correct if the ‘configurations’ column did not itself have a header.
39
Hi, this is such a beautiful website, I thought I’d take the time to let you know you have some faulty code which is messing up the formatting.
I wanted to send you a screen-shot, but I can’t via this form. In any case, the problem is that you have no content in the pale olive column on the right. All conent is starting under the menu from the darker olive column on the left.
Just thought you might want to know. My browser is IE version 6, with SP2.
40
@Gina: This was because I changed my fonts to ems instead of pixels beause IE can’t enlarge fonts. The behaviour you noticed was only in IE but it is fixed now and due to IE showing fonts a bit larger then Firefox. Thanks for letting me know.
41
Using this excellent table i have modified a few things for use on my site and i created a php script that allows me to upload a file and it creates the table attributes as well as a counter to add to the table. To see this in action go to http://bioxide.macaronisresort.com/login and register. The table is in the downloads section
42
Hi
Please can any one help me to manage a stick up footer on the website for IE 5.0 +.
As you can see 1 on http://www.bmw.co.uk
thanks
Imran Hashmi
43
I have two tables how can I apply this css style to only one of them?
44
@Robert, you can either use another ID for this table or use 2 different classes (in case you want to use this style again within the same webpage). Then you have #idname th and #idname td etc. or with a class you have .classname th and .classname td.
45
good
46
Very nice table, I love it!
47
Hi
Really good stuff here, a really informative article.
I see where Michel Vuijlsteke was coming from with his comment about the class selectors so i had a look to see if there was away of doing it them using only pure CSS, turns out there is.
So I thought I’d take your idea and run with it a bit. I removed the class selectors from the table and added more information about the structure of the table by using the thead and tbody tags.
The resulting table looks like so:
<table id=“mytable” summary=“The technical specifications of the Apple PowerMac G5 series”>
<caption>Table 1: Power Mac G5 tech specs </caption>
<thead>
<tr>
<th scope=“col”>Configurations</th>
<th scope=“col”>Dual 1.8GHz</th>
<th scope=“col”>Dual 2GHz</th>
<th scope=“col”>Dual 2.5GHz</th>
</tr>
</thead>
<tbody>
<tr>
<th scope=“row”>Model</th>
<td>M9454LL/A</td>
<td>M9455LL/A</td>
<td>M9457LL/A</td>
</tr>
<tr>
<th scope=“row”>G5 Processor</th>
<td abbr=“Dual 1.8”>Dual 1.8GHz PowerPC G5</td>
<td abbr=“Dual 2”>Dual 2GHz PowerPC G5</td>
<td abbr=“Dual 2.5”>Dual 2.5GHz PowerPC G5</td>
</tr>
<tr>
<th scope=“row”>Frontside bus</th>
<td>900MHz per processor</td>
<td>1GHz per processor</td>
<td>1.25GHz per processor</td>
</tr>
<tr>
<th scope=“row”>Level2 Cache</th>
<td>512K per processor</td>
<td>512K per processor</td>
<td>512K per processor</td>
</tr>
</tbody>
</table>
Now to style it in the same way using only the structure of the mark up.
body {
font: normal 11px auto “Trebuchet MS”, Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
background: #E6EAE9;
}
a {
color: #c75f3e;
}
#mytable {
width: 700px;
padding: 0;
margin: 0;
border-collapse: collapse;
}
caption {
padding: 0 0 5px 0;
width: 700px;
font: italic 11px “Trebuchet MS”, Verdana, Arial, Helvetica, sans-serif;
text-align: right;
font-style: italic;
}
th {
font: bold 11px “Trebuchet MS”, Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #CAE8EA url(images/bg_header.jpg) no-repeat;
}
thead th:first-child {
border-top: 0;
border-left: 0;
border-right: 1px solid #C1DAD7;
background: none;
}
tbody td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
padding: 6px 6px 6px 12px;
color: #4f6b72;
}
tbody tr:nth-child(odd) td {
background: #F5FAFA;
color: #797268;
}
tbody th{
font: bold 10px “Trebuchet MS”, Verdana, Arial, Helvetica, sans-serif;
border-left: 1px solid #C1DAD7;
border-top: 0;
font: bold;
background: #fff url(images/bullet1.gif) no-repeat;
}
tbody tr:nth-child(odd) th {
background: #f5fafa url(images/bullet2.gif) no-repeat;
color: #797268;
}
Only trouble is the selector nth-child(odd) is CSS level 3 and so won’t work in most browsers for a while yet!
Ah well, guess well have to wait for ff 2.0 or ie 8!
48
@Tom, thanks for sharing your approach on this. Much appreciated.
49
great article. I’ll try this css table.
50
You did a great job with this. I’ll be using this or a variant on upcoming pages I build. Thank you!
51
Please avoid the red text on black background. There are a lot more color blind people than people think—especially men.
Red on black is not readable.
52
Stunning. One of the nicest examples I have come across, and i thought your tutorial was excellent. LOL, there will always be more than one way to code up a page, however, you have provided an excellent example, and for those out there, an excellent base to extend on it and apply their own twist.
Magnificent.