CSS3 selectors

Slick tables with CSS3 selectors

This is the article that accompagnies the demo below, showing the use of CSS3 selectors implemented in Firefox 3.5 for easy and stylish tables.

The final result is:
A table stylized with CSS3 selectors

View the demonstration step by step.

Basic HTML table

First, we start by writing a simple HTML table .Note: we do not have any classes or ids, which is what makes this all so fantastic):

<table>
  <thead>
    <tr>
      <th>...</th>
      ...
      <th>...</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>...</td>
      ...
      <td>...</td>
    <tr>
    ...
    <tr>
      <td>...</td>
      ...
      <td>...</td>
    <tr>
  </tbody>
</table>

And now, let us write some CSS to make the plain old table look a bit more stylish:

table {
    font: 90%/1.5em "Lucida Grande", Geneva,
                    "DejaVu Sans", "Bitstream Vera Sans", AnjaliOldLipi,
                    "Lucida sans", "Trebuchet MS", Arial, Verdana;
    text-align: center;
    border: 4px black double;
    border-spacing: 0;
    -moz-border-radius: 12px;
    -moz-box-shadow: #6a3d37 5px 5px 6px;
    -webkit-border-radius: 12px;
    -webkit-box-shadow: #6a3d37 5px 5px 6px;
    border-radius: 12px;
    box-shadow: #6a3d37 5px 5px 6px;
    background: #b59d5c
}

The use of border-spacing, border-radius and box-shadow properties make for a quick and easy way to inject a little beauty into the table.

First selection

Now, we would like to style all th tags.

This is easy with CSS selectors.

th {
    color: #fff;
    font-size: 110%;
    text-shadow: #6a3d37 2px 2px 2px
}

Reminder, if we write table th, we intend to select all the th elements which are child elements of the table element, and if we write thead > th, we intend to select all the th elements which are direct children of the thead element. Well, it was just a reminder :-).

More nuanced selection

The th tag represents a table header. We would like to select the first table header. Hmm… maybe we should use the first-of-type pseudo-class. It represents an element that is the first sibling of its type in the list of children of its parent element. So now we have:

    th:first-of-type {
        font-weight: bold;
        font-style: italic
    }

Even and odd rows

A recurrent problem with tables is: how to select even and odd rows? The solution is the nth-child() pseudo-class. All these pseudo-classes understand the an+b syntax. It means, to select all the even elements, we use 2n; to select all the odd elements, we use the 2n+1 elements; to select all third elements, we use 3n. In other words, this matches the bth child of an element after all the children have been split into groups of a elements each.

So, let's style the even and odd rows:

tbody tr:nth-child(odd) {
    color: #e0d8cb;
    background: #474644
}

tbody tr:nth-child(even) {
    color: #6a3d37
}

Padding on first and last columns

Now, we want to add a padding to the first and the last column. I remind you again that we do not have any classes or IDs, and that the number of columns is unknown.

The solution is the first-of-type and the last-of-type pseudo-classes. We select all the first and last th and td like this:

th:first-of-type,
td:first-of-type {
    padding: 0 0 0 4em
}

th:last-of-type,
td:last-of-type {
    padding: 0 4em 0 0
}

Two last rows are not very interesting

We considere that the two last rows are not very important. How to select them to set their opacity to 0.75?

Let use the nth-last-child with tricky an+b expression. We would like to count from the end and not from the beginning, that's we use the nth-last-child pseudo-class. But, to select only two rows, we do not split rows in groups, so a is set to -1. And, because of we want two rows, b is set to 2.

Thus:

tbody tr:nth-last-child(-n+2) {
    opacity: .75
}

Headache ensured

Now, we would like to combined many pseudo-classes (and introduced a new one).

Ok. We would like to select the row number 1, number 4 and number 7. The “Mathematical” expression is not so simple as we hoped. The tips is as follow: we split our rows in groups of 3, like this: 3n. But it will select the rows number 3, 6 and 9. No problem. In these groups, we will select the first element, so 3n+1 (or 3n-2 if you like to complicate things).

Yes, it's good, but it will select a row in our two last non-opaque rows. Hmm… Ideally, we should say: select the first row of each groups of three rows, but not the last row (which will be selected, because the last is the 7th row). Simple. We are going to use the not pseudo-class, combined with the last-child pseudo-class (or last-of-type, it also works here).

Thus:

tbody tr:nth-child(3n+1):not(:last-child) td {
    text-shadow: red 0 0 8px
}

Final

The final source is:

table {
    display: table;
    font: 90%/1.5em "Lucida Grande", Geneva,
                    "DejaVu Sans", "Bitstream Vera Sans", AnjaliOldLipi,
                    "Lucida sans", "Trebuchet MS", Arial, Verdana;
    text-align: center;
    border: 4px black double;
    border-spacing: 0;
    -moz-border-radius: 12px;
    -moz-box-shadow: #6a3d37 5px 5px 6px;
    -webkit-border-radius: 12px;
    -webkit-box-shadow: #6a3d37 5px 5px 6px;
    border-radius: 12px;
    box-shadow: #6a3d37 5px 5px 6px;
    background: #b59d5c
}

th {
    color: #fff;
    font-size: 110%;
    text-shadow: #6a3d37 2px 2px 2px
}

    th:first-of-type {
        font-weight: bold;
        font-style: italic
    }

tbody tr:nth-child(odd) {
    color: #e0d8cb;
    background: #474644
}

tbody tr:nth-child(even) {
    color: #6a3d37
}

th:first-of-type,
td:first-of-type {
    padding: 0 0 0 4em
}

th:last-of-type,
td:last-of-type {
    padding: 0 4em 0 0
}

tbody tr:nth-last-child(-n+2) {
    opacity: .75
}

tbody tr:nth-child(3n+1):not(:last-child) td {
    text-shadow: red 0 0 8px
}

That's all folks! So simple…

Author

Created by Ivan Enderlin, Hoa project, june 2009.