Styling tables with CSS

Thanks to Matt Doyle reference[][26-Nov-2010 05:39PM]

Learn how to use CSS to transform dull HTML tables into beautiful works of art. This article shows how to customize the borders, spacing, padding, background and colours of tables and table cells, as well as how to produce alternate-coloured table rows and create hover effects.

Techniques we’ll cover include:

  • Adjusting the spacing, padding and margin of table cells
  • Styling the borders of the table and the cells within the table
  • Changing text and background colours, and using background images
  • Creating alternately-coloured table rows to aid legibility
  • Adding a mouse hover effect to table rows
  • Styling the table caption

The basic HTML table

Here’s the markup for our basic, unstyled table. It contains:

  • summary attribute, used to give a detailed description of the table’s structure for non-visual browsers, such as screen readers.
  • caption element. This gives a short description of the table’s contents for non-visual browsers. It’s also usually displayed by regular browsers, either above or below the table.
  • header row, providing labels for Product, 1st Quarter, 2nd Quarter, and so on. We use a?th (table header cell) element for each label to indicate that this is header information, rather than actual data. We also use the?scope="col" attributes to indicate that these header cells refer to the columns of data below them.
  • data rows, containing the sales data for each widget. Each row starts off with another header cell (th), containing the widget name. This time, we use?scope="row"to indicate that the header cell refers to the data in the same row. After the header cell, four data cells (td) contain the actual sales data.

Giving the table a class

In order to style our table, we first give it a class, like this:

 This allows us to identify any tables with the?pretty-table class in our CSS, so that we can apply our styling. To do this, we use the?.pretty-tableselector in the CSS.

Controlling cell spacing and padding

In HTML 4 and XHTML 1.0, you can control the spacing between each table cell using the?cellspacing attribute, and the padding between each table cell and the content inside it using the?cellpaddingattribute. However, you can also control these two attributes within CSS:
  • To control cell spacing, use?border-spacing. For example:?table { border-spacing: 5px; }.
  • To control cell padding, use?padding on individual table cells. For example:?td { padding: 5px; }.
Unfortunately, IE6 doesn't recognize?border-spacing. However, all is not lost. We want our styled table to have no gaps between table cells, so we can instead use?border-collapse: collapse;, which IE6 does understand. So here's our CSS to control spacing and padding. We want no gaps between table cells, and we want a padding of 0.5 ems around the content within each table cell:
 .pretty-table { border-collapse: collapse; } .pretty-table th, .pretty-table td { padding: 0.5em; } 

Styling table borders

By default, most modern browsers display tables with no borders around the table cells. Using CSS, you can fine-tune the appearance of all borders within a table, including:

  • The border around individual table cells
  • The border around table rows
  • The border around the whole table

Borders around the table and table cells

Let’s start by placing a 1-pixel dark grey border around the whole table, and a 1-pixel lighter grey dotted border around each table cell:

 .pretty-table { border: 1px solid #333; } .pretty-table th, .pretty-table td { border: 1px dotted #666; } 

Two thick borders to separate headers from data

We also want to place a 2-pixel dark grey border to the right of the left hand column, and another below the top row, to separate the header cells from the data cells.

First of all, let’s place the border below and to the right of the top-left cell (“Product”). To select this cell in CSS, we can use an?attribute selector, as follows:

 .pretty-table th[scope=col] { border-bottom: 2px solid #333; border-right: 2px solid #333; } 

The?[scope=col] attribute selector allows us to select just those table header cells that have the attribute?scope="col", i.e. our top table row.

However, we only want that right-hand thick border on the “Product” cell, not the other four cells in the row. So we need to select those other four cells, and reset their right-hand borders to the standard dotted border. To select the cells, we can use anadjacent sibling selector, as follows:

 .pretty-table th+th[scope=col] { border-right: 1px dotted #666; } 

Adjacent sibling selectors allow you to select an element based on the element immediately before it. So in the above code, we’re selecting only those?th elements that are preceded by another?th element – in other words, all header cells except the first one (“Product”). We then reset those selected cells back to the standard dotted border.

Finally, we’ll add the same 2-pixel border to the right hand edge of the left-hand column by selecting all those header cells with the?scope="row" attribute:

 .pretty-table th[scope=row] { border-right: 2px solid #333; } 

Setting table and cell colours and backgrounds

Next, let’s take a look at controlling the background and foreground colours in our table, as well as setting background images.

As with borders, CSS gives you control over the colour and background of:

  • individual table cells
  • table rows
  • the whole table.

The base text colour

We’ll start by setting the basic text colour of our cells. In our example, we’ll use a dark maroon colour:

 .pretty-table th, .pretty-table td { color: #632a39; } 

Colouring the header cells

Now we’d like to use different background and foreground colours for our header cells, as follows:

  • The “Product” cell has black text (#000) and a mid blue background (#8fadcc).
  • The remaining header cells along the top row have white text (#fff) and a dark blue background (#7d98b3).
  • The header cells down the left hand side (apart from “Product”) have a light blue background (#b8cfe5). We’ll leave the text colour at the default dark maroon.

To set colours for these different types of cells, we can use attribute and adjacent sibling selectors again, exactly as we did when setting cell borders above:

 /* Set colours for all header cells in the top row */ .pretty-table th[scope=col] { color: #000; background-color: #8fadcc; } /* Set colours for all header cells in the top row except "Product" */ .pretty-table th+th[scope=col] { color: #fff; background-color: #7d98b3; } /* Set the background colour for all header cells in the left column */ .pretty-table th[scope=row] { background-color: #b8cfe5; } 

Note how the second selector overrides the first, as it is more specific (th+th versusth). This ensures that only the “Product” cell is left with the black text and?#8fadccbackground. (For more on this topic, see?Calculating a selector’s specificity in the CSS 2.1 specification.)

Table background image

Adding a gradient to the table background

As well as changing text and background colours of table elements, you can also give those elements background images. Let’s use this to add a gradient background to the whole table.

To do this, first create a suitable background image. You can see the one we’ve used over on the right. It’s the right height for the table, and 16 pixels wide.

We use this image as the table background as follows:

 .pretty-table { background: #bcd0e4 url("widget-table-bg.jpg") top left repeat-x; } 

This sets our background image on the table, positions it at the top left corner of the table, and makes it repeat horizontally. We’ve also told the browser to use the background colour?#bcd0e4 for any areas of the table not covered by the image. This means that the table retains its blue background if it grows to be longer than the height of the image.

Read more on?controlling backgrounds with CSS.

Creating table rows with alternate colour

A nice trick to aid readability of a table is to colour alternate table rows in a different colour. To do this, we first need to add a class to those alternate rows in the table’s markup:

... ... ... ... Now we can select those rows in CSS, and apply a different colour to the text in the cells within those rows — say, a dark blue:

 .pretty-table tr.alt th, .pretty-table tr.alt td { color: #2a4763; } 

Adding a hover effect to table rows

We can further improve readability of our table by making a table row change colour when the mouse hovers over it. We’ll use white text and a maroon background as the hover colours, making sure to only add the effect to those rows that contain data (i.e. not the top row):

 .pretty-table tr:hover th[scope=row], .pretty-table tr:hover td { background-color: #632a2a; color: #fff; } 

Changing the look of the table caption

By default, the table caption tends to appear centred above the table, though this is very much browser-dependent. We can use the CSS?caption-side property to specify a top or bottom caption, and also apply other standard CSS properties such as?text-align and?padding to achieve a nice-looking caption:

 .pretty-table caption { caption-side: bottom; font-size: 0.9em; font-style: italic; text-align: right; padding: 0.5em 0; } 

The final markup and CSS

Our table now looks much prettier! Here’s the final code. We’ve made a few small additional changes, such as setting a nice font, specifying font sizes, and making the top header row uppercase, to produce a great finish.

The table markup:

Widget sales figures, 2007
Product 1st quarter 2nd quarter 3rd quarter 4th quarter
SupaWidget $9,940 $10,100 $9,490 $11,730
WonderWidget $19,310 $21,140 $20,560 $22,590
MegaWidget $25,110 $26,260 $25,210 $28,370
HyperWidget $27,650 $24,550 $30,040 $31,980

...and the corresponding CSS:


 .pretty-table { padding: 0; margin: 0; border-collapse: collapse; border: 1px solid #333; font-family: "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; font-size: 0.9em; color: #000; background: #bcd0e4 url("widget-table-bg.jpg") top left repeat-x; } .pretty-table caption { caption-side: bottom; font-size: 0.9em; font-style: italic; text-align: right; padding: 0.5em 0; } .pretty-table th, .pretty-table td { border: 1px dotted #666; padding: 0.5em; text-align: left; color: #632a39; } .pretty-table th[scope=col] { color: #000; background-color: #8fadcc; text-transform: uppercase; font-size: 0.9em; border-bottom: 2px solid #333; border-right: 2px solid #333; } .pretty-table th+th[scope=col] { color: #fff; background-color: #7d98b3; border-right: 1px dotted #666; } .pretty-table th[scope=row] { background-color: #b8cfe5; border-right: 2px solid #333; } .pretty-table tr.alt th, .pretty-table tr.alt td { color: #2a4763; } .pretty-table tr:hover th[scope=row], .pretty-table tr:hover td { background-color: #632a2a; color: #fff; } 

Internet Explorer 6 notes

If you’re viewing the example table in IE6, you’re probably wondering what all the fuss is about. Although the table looks decent enough in IE6, it’s missing some features:

  • The header cells are the same style as the data cells. This is because IE6 doesn’t understand attribute selectors or adjacent sibling selectors. To get the same effect in IE6, add classes to the topmost table row, the leftmost table column, and the “Product” header cell. You can then select these cells using classes, rather than attribute and adjacent sibling selectors. (It’s messy markup though, and I wanted to keep the markup clean for this example!)
  • The table caption is along the top of the table. IE6 doesn’t recognize thecaption-side property, so there’s not a lot you can do about this!
  • The hover effect doesn’t work. Sadly, IE6 only recognizes the?:hover pseudo-class when applied to links; you can’t use it with any other element types. Possible workarounds — if you’re desperate to have the hover in IE6 — include wrapping a link around the text in each table cell (using?display:block to ensure the link fills the cell); using JavaScript’s?onmouseover/onmouseout events to achieve the hover effect; or using this?clever behaviour trick to allow?:hover to work with any element type in IE6.


Download the saved HTML