An Unfinished Symphony

It's about the internet and stuff.

Tables in the CSS Age

As CSS began to gain popularity the use of tables for layouts, quite rightly, began to lessen. However one of the side effects of this was that less developers were learning how to use and markup a table, for any purpose. Subsequently we’d often see examples of developers using nested <div>s, painstakingly positioned with CSS, being used to layout data tables which as a result fails to provide any semantic information about what is being presented. Or in some cases actual table markup used with no idea or consideration about the semantics of the elements used, which is something that I saw again only today. Given the prevalence of those scenarios I think a simple guide to laying out a data table might be helpful to some.

Before starting with the code to use we need to consider the components of a data table and why they’re used:

  1. Table Summary

    This component is used to provide a summary of the structure and organisation of a data table.

  2. Caption

    The caption sits outside of the main table when viewed in the browser and provides a contextual heading describing what follows.

  3. Column Groups and Columns

    Data tables often have structurally related columns which can be defined within colgroup elements. The colgroup element can be used to define the width of the columns in the group and also, via the span attribute, the number of adjacent columns that is being defined within the group. If all columns in the table are structurally related then you can use the span to state this. If none are related you would just omit the colgroup element from your markup. The col element is used to define the properties of non-structurally related groups of columns (when used outside of the colgroup element), or to define changes in the properties of a subset of columns within a colgroup.

  4. The Table Header

    The table header can contain the headings for simple tables where the top row(s) of cells contain the column headings for the cells that follow. For more complex tables, the table header can still be used to define headings for the entire table, but as you can only have one table header section you can’t define separate ones for subsection headings. The table header must contain at least one row, and the row must contain at least one cell, however the table header is optional rather than compulsory.

  5. The Table Footer

    The table footer can contain the footer data for simple tables, such as footnotes used to define or describe essential information. As with the table header, you can only have one footer section and so you can’t define separate footers for subsections, only a single overall footer for the entire table. As with the table header, the footer is optional but if used it must contain at least one row, with the row containing at least one cell.

  6. The Table Body

    The title for this section shouldn’t really read “the” table body as, unlike the table header and table footer, there can be more than one table body. This is useful for separating sections of a complex table into distinct groups of data rows. Each table body that is defined must, as with the table header and table footer, contain at least one row with at least one cell inside it. The table header, table footer and all table bodies must contain the same number of columns, however columns can be merged by using the colspan attribute should you need a different number of cells in a given row.

  7. Table Rows

    Table rows are used as structural containers for rows of data cells.

  8. Table Cells

    These are defined by <th> and <td> tags, where they create heading cells and data cells respectively. Headings are used to contain heading data that defines or describes the data that follows, while the data cells contain that data.

Now that we know and understand the components of a data table we can begin to structure one in a meaningful and accessible way:

The opening code for a simple data table:
  1. <table summary="This table contains information about people with the rows containing the information relating to individual people and the types of information contained in the columns.">
  2. <caption>A dummy data table containing information about pretend people</caption>
  3. <colgroup span="4" width="100"></colgroup>

The code above demonstrates an example of the opening code for a table and includes the sections that define the table, including aspects of its structure, but none of the data containing aspects. Line 1 contains the opening table tag and the summary attribute which has an explanation of how the table data is organised. Line 2 contains the caption which describes what the data is about. Line 3 contains a single colgroup element which defines the width (of the 4 columns that are a part of that group.

If you wanted to set a column to have a different width you could do so simply by including a couple of col elements nested within the opening and closing tags:

  1. <colgroup width="100">
  2. <col span="3" />
  3. <col span="1" width="250" />
  4. </colgroup>

On line 1 of the above code we have the opening colgroup tag again, however now it only contains a width attribute that defines the width of the columns in the group. Line 2 then defines the number of columns within the group that have that width setting, and line 3 defines a 4th member of the group that has a different width to the others, with line 4 closing the colgroup. Also permissable is to just use the colgroup elements as containers for col elements which are then used to define the numbers of rows, and their widths, within the colgroup, for simplicity this is the method that I most often use. For example:

  1. <colgroup>
  2. <col span="3" width="100" />
  3. <col span="1" width="250" />
  4. </colgroup>

Now that we’ve opened our table and defined a number of its properties we can begin to create the data containing sections:

The table header & table footer sections for a simple data table:
  1. <thead>
  2. <tr>
  3. <th scope="col">Name<sup>1</sup></th>
  4. <th scope="col">Age</th>
  5. <th scope="col">Place of Birth</th>
  6. <th scope="col">Height</th>
  7. </tr>
  8. </thead>
  9. <tfoot>
  10. <tr>
  11. <td colspan="4"><sup>1</sup> Pretend names for pretend people</td>
  12. </tr>
  13. </tfoot>

The above block of code contains the table header (<thead> on line 1, ending on line 8 with the </thead>) and table footer (<tfoot> on line 9, ending on line 13 with the </tfoot>) sections. Lines 2 & 10 and 7 & 12 define the opening and closing table row tags respectively, with lines 3-6 containing heading cells and line 11 a single data cell in the footer. Each of the heading cells contains a scope attribute, this can be set to either col or row depending on which direction it needs to be read in, here the value of col is set to define the data cells in the column below as belonging to it. I’ll discuss using scope=”row” in the section on the tbody later on.

For more complex tables the id and headers attributes can be used to define heading relationships between th and td cells. For information on that check the W3C section on the headers attribute, and also the axis attribute. Also, if our headings had needed to be longer and/or more complex we could have used abbreviations in conjunction with the abbr attribute.

In the table footer a single cell has been created by merging all the columns into one using the colspan attribute with a value of 1. Here a footnote, denoted by the use of a superscript #1 has been used to describe an aspect of the data in one of the heading cells.

Finally we come to the tbody (of which there may be more than one) where the data is presented:

The table body section
  1. <tbody>
  2. <tr>
  3. <th scope="row">Joe Blogs</th>
  4. <td>32</td>
  5. <td>Liverpool</td>
  6. <td>6' 1"</td>
  7. </tr>
  8. </tbody>
  9. </table>

On line 1, above, we open the first tbody section (and the only one for this example table). On line 2 we define the opening of the first table row, closing it on line 7, while on lines 3-6 we define the data cells within that row. On line 3 we have our opening data cell, which falls under the heading of Name (see the header section above) and contains the name of our example person. I’ve also set that data cell to be a heading cell by marking it up with <th> instead of <td>, and as this is a heading that sets a relationship with the other cells in its row it has a scope attribute with the value of row to establish this. Lines 4, 5 and 6 are simple data cells containing data that is headed by the main column headings defined in the header section earlier, but also by the row heading defined here. Finally, on line 8 we close the table body and on line 9 we close the entire table. If we put all of the component parts together we’ll have the full markup for a simple data table:

The full markup for a simple data table:
  1. <table summary="This table contains information about people with the rows containing the information relating to individual people and the types of information contained in the columns.">
  2. <caption>A dummy data table containing information about pretend people</caption>
  3. <colgroup span="4" width="100"></colgroup>
  4. <thead>
  5. <tr>
  6. <th scope="col">Name<sup>1</sup></th>
  7. <th scope="col">Age</th>
  8. <th scope="col">Place of Birth</th>
  9. <th scope="col">Height</th>
  10. </tr>
  11. </thead>
  12. <tfoot>
  13. <tr>
  14. <td colspan="4"><sup>1</sup> Pretend names for pretend people</td>
  15. </tr>
  16. </tfoot>
  17. <tbody>
  18. <tr>
  19. <th scope="row">Joe Blogs</th>
  20. <td>32</td>
  21. <td>Liverpool</td>
  22. <td>6' 1"</td>
  23. </tr>
  24. </tbody>
  25. </table>

This will then be presented in the browser like this:

A dummy data table containing information about pretend people
Name1 Age Place of Birth Height
1 Pretend names for pretend people
Joe Blogs 32 Liverpool 6′ 1″

Note: that table is using the table styling set in my CSS. If you were to use that example elsewhere you would need to create your own CSS to style it otherwise it would use the browser default styling. If anyone needs tips on how to do that then let me know and I can create a follow up tutorial for that. Similarly, if there’s anything that needs clarification please leave a comment and ask, I’ll be happy to help.

Up arrow


Regular visitors to the site should notice quite a radical change of design here which I’m hoping will make it a bit more visually pleasing. When I first started the site it was, almost literally, just thrown together in a hurry and without a finalised design. This was basically because there were a number of people that wanted the blog up and running and, I think, for me to start posting on it. Some of you may still remember the original look of the site, which was far from complete and so a little bit broken to say the least. After a little feedback I decided not to continue working with that design and came up with a working temporary one instead. Even though it provided everything I wanted from it (functionality, flexibility and simplicity) it was a little uninspiring and I was never really happy with it, despite that it managed to survive for a couple of years.

Simplifying the Interface

What I’ve tried to do with the new design is keep the important concepts intact – it’s still, I believe, functional; it’s still flexible, though now within a set limit; and, importantly, it’s still simple. In fact I’ve made an effort to simplify it further. I found certain aspects of the old design were slightly cluttered, notably the links sidebar. In order to de-clutter the link heavy sidebar I separated the archives and external links section into their own pages, with them linked via a new primary menu. I’ve also used a little PHP and hidden the MyBlogLog widget from everyone but myself.

All in all I think the sidebar is cleaner and more useful while still sharing things that I want to share. Also, perhaps the most noticeable change is the dropping of the third column from the front page. My original idea for that was to allow the front page of the site to show the latest post in full whilst also showing the next 5 or 6 most recent posts, but rather than overloading the page with too much content these recent posts would just have excerpts showing on the front. The old design did achieve that fairly well, however the extra column meant that the front page would always seem a little cramped. It’s also not necessary to have a third column in order to provide direct access to that content from the front page, so it got dropped.

New Sections

I’ve added a few extra sections to the site where I can provide supplementary information, such as an accessibility statement and privacy policy, including an about page. At the moment the about page is a bit bare; I do intend to put something in there at some point, hence its inclusion, however I haven’t decided on what exactly I want to say. I’ve also extended the subscription page to include information about my feeds, feed readers and feed aggregators along with the original email subscription option that I’ve carried over from the previous version. There’s also a contact form, just in case.

Ironing out the Issues

As with any project, there have been issues to resolve (I’m talking specifically about coding issues at this point).

PHP Issues

One of the things I wanted was for the archives page to show each category with all the posts for that category, to be able to reorder the categories, to be able to show the date of each post adjacent to the link to the post and to be able to structure markup how I wanted. One of the great things about WordPress is its ease of templating, and to that end the codex provides details of the variety of template tags available for this, however those available for templating category listings didn’t quite meet all of my requirements. This is where Sarah came in with a bit of help, well a lot of help, providing me with the PHP needed to pull the required information from the database in the way that I wanted. She also helped me write the PHP for my contact form, in the process helping me to add in fields checking for spammers, and also code to check that those fields had been completed appropriately.

The site used to use an old version of the Subscribe2 plugin which I held off from upgrading to the amount of work it would have taken to get a newer version to work with the existing theme. A new theme has meant that was no longer a consideration, and so I upgraded to the latest version of Subscribe2. It was only when I uploaded, and set live, the completed theme and upgraded plugins at the weekend that a few issues came to light. Basically the plugin only partially worked on the live site, with new subscribers receiving an email containing a link to confirm the subscription, but the link going to a blank page and the subscription not being confirmed. After a lot of reading through the plugin code, and with Sarah doing a number of tests, we found that there were three functions responsible for the confirmation process – commenting out the call to one of these functions allowed the confirmation to go through and the appropriate confirmation page to display – just in a very broken state as a number of scripted components failed to work on the page. It had, by that time, occurred to us that it was an issue with using customised permalinks on the site along with the way the plugin takes over an existing page that it rewrites with the confirmation – instead of doing this successfully the page was being redirected to the real permalinked page, causing the confirmation to fail. Once we had tested that this was the case, by resetting permalinks to the default method, it didn’t take much to discover the real cause – a conflict between Subscribe2 and the Permalink Redirect plugin. This issue hadn’t existed with the previous version of Subscribe2 that I’d been using, so something that has changed caused the conflict – the process of taking over an existing page from the database, rather than using its own dedicated one like the old version did. It’s probable that the new method makes it easier for the plugin to be integrated with a theme and so it’s an acceptable change in the grand scheme of things, but unfortunate that it results in a conflict. To fix it I dropped the other plugin.

CSS Issues

While differences in browser rendering continue to exist there are always going to be display issues when switching between those browsers. The star browser was Firefox, getting things right first time and according to standards. Internet Explorer 7 had a few minor issues, and IE 6 a few more, however these were relatively simple to fix with a few changes in margin, width and position settings in their own respective style sheets linked via conditional comments. In Internet Explorer 5.5 it looked like a bomb had hit it; that browser couldn’t handle a fair bit of the CSS and rather than spend too much time on an obsolete browser I changed a number of things and fixed the width. I basically just wanted to make sure the site would be usable rather than trying to precisely reproduce the design.

Quite surprisingly I had a few strange problems in Opera 9.20, surprisingly because Opera is at least as standards compliant as Firefox if not more so. However there were several oddities that didn’t seem to have an easy fix, or at least not a fix that could be achieved simply by tweaking the CSS without those tweaks affecting other browsers. Those issues included:

  • displaying the sponsor links at the correct size until the page finished loading, then the text mysteriously shrinking. As these used the same markup and CSS as the other links in the sidebar there was no easy or logical way to fix the problem without affecting the other links.
  • text marked up as paragraphs within the content area would magically jump to the right once the page loaded, leaving a small fragment of text behind (a few characters from the last line of each paragraph, chopped off at the top). Again, there was no simple fix without it affecting the positioning in other browsers.
  • text link styles were overridden with the browser default ones, again without any logic that I could see. It’s possible that one of the settings in my browser profile caused this issue, but I’m not familiar enough with the browser to track it down – so I needed to find another fix.

It’s likely that the first two issues were caused by the combination of scripts used on the page, for example the sponsor links are generated by scripts. However as it wasn’t feasible to remove those scripts anyway I decided not to test the theory by disabling them on a live site (the issues weren’t apparent in my local testing copy). Due to the prospect of a CSS fix affecting other browsers I had to find a hack or filter for Opera, much as it pains me to use them now that it’s clear we can control the real problem browser (Internet Explorer 6 and under) using conditional comments. However these issues needed an Opera specific fix and, after a bit of searching on Google, I found a way using an @media declaration to target Opera:

  1. @media all and (min-width:0px) {
  2. head~body p {
  3. margin-left : 0;
  4. }
  5. #content a, #content a:link, #content a:visited, #skipper a:link, #skipper a:visited {
  6. color : #00303e !important;
  7. }
  8. #content a:hover, #content a:active, #content a:focus, #skipper a:hover, #skipper a:active, #skipper a:focus {
  9. color : #870000 !important;
  10. }
  11. #sidebar #links55315 a {
  12. font-size : 11px;
  13. }
  14. }

The combination of the “@media all” and the “and (min-width:0px)” in line 1 of the code above targets Opera fairly specifically – apparently it has also been shown to target Pre-release versions of Safari too. I had no way of testing this, however I felt it was safe to use the method as I was just emphasising already existing styles rather than trying to impose different ones. Line 2 very specifically targets all paragraphs, overriding any other rules that may be conflicting. Line 3 sets the left margin for all paragraphs to zero (even though this had already been done in the main style sheets), while Line 4 closes the rule set opened in line 2. A quick test in Opera showed that the method worked successfully. Lines 5, 6 and 7, and lines 8, 9 and 10 were used to fix the link colour change, while 11, 12 and 13 were used to fix the resizing of the sponsor link text – using a pixel unit instead of ems was what was needed there. The final line closes the whole thing. That discovery came courtesy of the Tanrei Software blog where there’s a useful article on using media selectors for browser targetting.

Other Issues

Rather than use a mass of extra markup and images I chose to use Alessandro Fulciniti’s NiftyCorners Cube script for the curved corners, so users without javascript enabled will see the site with square corners. Users with javascript may also have some issues, there’s a slight lag in the page load time as the script runs through the targeted elements in order to set the curved corners. It’s also possible that the script contributes to the paragraph jumping in Opera, though I’ve never seen this happen in other sites that I’ve used the script on.

I use a plugin that dynamically adds classes to various links in order to add an icon to identify the target type (such as an arrow pointing away from a box to indicate external links). One of the icons used was for links to Wikipedia which consisted of a small icon of the Wikipedia logo. However it was hard to distinguish that icon from the surrounding text, so it was removed.

I wanted to use an image of an upwards pointing arrow for my back to top links, but without all of the images being background images (otherwise there would have been no actual content for the links). I achieved it using a combination of foreground and background images with the following markup and CSS:

  1. <div class="up">
  2. <a href="#skipper" title="Back to top">
    <img src="" width="16" height="16" alt="Up arrow" /></a>
  3. </div>

The markup simply creates a div as a container element for the image used as the default-state up arrow and the actual link back to the top of the page.

The CSS – part 1
  1. div.up {
  2. float : right;
  3. margin : 5px 10px 0 0;
  4. padding : 0;
  5. width : 16px;
  6. height : 16px;
  7. }

The first part of the associated CSS sets the dimensions of the container div to match the size of the image it contains and positions it where I want it on the page (relative to its own container).

The CSS – part 2
  1. div.up a, div.up a img {
  2. display : block;
  3. padding : 0;
  4. margin : 0;
  5. }

Part 2 of the CSS sets both the image and the anchor around it to be block level. This corrects Internet Explorer 6’s behaviour of adding a 3px space beneath inline images (which is the space preserved for text descenders). It also allows the full area taken up by the image to be a clickable part of the link.

The CSS – part 3
  1. div.up a {
  2. background : url(images/up2.png) no-repeat center bottom;
  3. position : relative;
  4. width : 100%;
  5. height : 100%;
  6. text-decoration : none;
  7. cursor : pointer;
  8. }

This code sets the link to 100% width and height of the container div to complete the process started in the previous rule set, which allows the full area of the image to be clickable. It also sets the positioning to relative to allow the contained image to be absolutely position within it, and the first line sets the background to be the hover state version of the image. The final 2 lines turn off the underline added to links and ensure that the proper cursor style is used.

The CSS – part 4
  1. div.up a img {
  2. position : absolute;
  3. top : -5px;
  4. left : -5px;
  5. border : none !important;
  6. }

Part four positions the foreground image within its containers so that it precisely and completely covers up the background image. It also makes sure that no border is added.

The CSS – part 5
  1. div.up a:hover img, div.up a:active img, div.up a:focus img {
  2. visibility : hidden;
  3. }

The final rule set is responsible for removing the foreground image when the link is hovered over, revealing the hover state background image. It works as intended in Firefox, IE 7 and Opera 9. In IE 5 and 6 the change in state doesn’t work, and IE 5 also needs a slight change in the positioning to cover the hover state image.

No doubt there’ll be other issues to find, and they’ll be fixed when they are. I’m expecting there to be one or two in Mac browsers, but as I haven’t been able to test in them (the Mac testing services I normally use have been broken when I’ve visited recently) I haven’t been able to find them. If you’re a Mac user and do find issues, please let me know and I’ll do my best to resolve them, thanks.

Up arrow

The First Annual CSS Naked Day

Dustin Diaz has announced the first annual CSS naked day to take place on the 5th of April. I had wanted to announce my participation in the event prior to my CSS turning off, but I didn’t get the chance to and now it’s gone.

Some of you may be wondering why I started early – well I haven’t really. You see I used the PHP function provided by Luke Wertz to disable my CSS automatically and, as mentioned by Dustin, this will turn the CSS off as soon as it is the 5th of April anywhere in the World, and keep it turned off until it’s no longer the 5th anywhere. In other words it’s an annual CSS 2 naked days.

To find out what it’s all about have a read of the announcement, however it’s summed up quite well here by Håkon Wium Lie, the creator of CSS:

This is a fun idea, fully in line with the reasons for creating CSS in the first place. While most designers are attracted by the extra presentational capabilities, saving HTML from becoming a presentational language was probably a more important motivation for most people who participated in the beginning.

Håkon has pledged to take part, along with several hundred other mental people 🙂

In a little under 48 hours my CSS will return in a blaze of glory, in the meantime you get to see a more attractive ap4a.

Up arrow

Style:Phreak’s Standard Form Layout Revisited

In August 2004 Style:Phreak created a demonstration of a standards compliant form marked up with semantic HTML and styled with CSS. I’ve only recently been introduced to it by an acquaintance from a forum I visit occasionally who uses a version based on it and asked how it could be made more semantic following a brief discussion about it. As a result of that discussion I’ve taken it upon myself to update it slightly, a possibility brought about due to better support available in current browsers compared to what was available in 2004.

Variations in this version:

  • The wrapping of label/input pairs within divs has been dropped as this was only used to allow styling effects to be done to them – those effects are possible without needing to add the extra markup.
  • The div surrounding the radio buttons has been replaced with a fieldset, which is more semantic, though it’s responsible for the minor issue in Opera 7.23 outlined below.
  • Although a tabindex could have been added one hasn’t been – as this isn’t an active form I decided against this. In a real form one would possibly have been used. Also the erroneous inclusion of one empty tabindex attribute in the original has been omitted here.
  • The statement, in the style:phreak original, that fields marked in bold are required is reliant on presentation and would mean that screenreader users won’t get the message, this has been replaced with the comment that those marked with a * are required.
  • As * marked fields are required it is important that all users get to see it, so it’s important that it isn’t included via the CSS content property, as per the original.
  • As the contents of the “required fields” paragraph contains essential information relating to the form it’s more meaningful to include it within the form, so that’s done too.
  • The script used to toggle the optional fields on and off has been removed in this version, however a script to correct a win IE bug has been included in order to prevent the resetting of select fields when their label is clicked.

This version has been tested and works as intended in the following current Windows browser versions: Internet Explorer 6, Firefox and Opera 8.52. It has also been tested in Opera 7.23 (the lowest version I care about supporting) and found to work with only one error – where the fieldset surrounding the radio buttons is surrounded by a border despite the CSS rule setting it to none. In IE 5.02 and IE 5.5 for Windows it works as intended apart from a misplacement of the textarea and its label, and the wrapping of the label for the input above it on to two lines – I could have corrected this with an IE 5 only stylesheet or a hack, I didn’t care enough to try to.

This version, as with Style:Phreak’s original is released under a creative commons licence.

View the updated form here.

Up arrow

longdesc, it’s not just alternate text

Most developers are, I would hope, aware of alt attributes, and hopefully how to use them correctly. It seems, however, that longdesc is a bit of a mystery even to some of the more experienced and knowledgeable amongst us.

With an image’s alt attribute we’re able to insert a small segment of text that provides an alternative if, for whatever reason, the image is unable to be displayed. It may be something simple, such as “my company logo” to replace your logo when it doesn’t open up. With more complex images, and images that convey very important information such as charts and diagrams, a more detailed description is required – not in case the image doesn’t display, but to provide an alternative means of providing the detailed information the image contains. In this case the alt attribute just isn’t enough. It isn’t viable to describe a graph of medical results, for example, in the few words that alt attributes allow us. For circumstances such as in our medical chart example we have been provided a more powerful method with the longdesc attribute. However, due to the mysterious nature of longdesc it seems that many of us are incorrectly using it in the same way as we use alt attributes. To illustrate the point, consider the markup for an embedded image with alt attribute:

  1. <img src="image.gif" alt="my company logo" />

As the above example shows, alt attributes contain our alternative text enclosed between quotation marks and within our image tag. Longdesc attributes, however, don’t enclose the long description within the confines of its quotation marks, instead longdesc contains a link to another web page where we would describe in detail the image in question. Nor would our longdesc attribute be used to replace our alt attribute – instead we would use both; for example:

  1. <img src="compleximage.gif" alt="Results graph of medical data" longdesc="/clinicalresults.html" />

Rather than being a more detailed, and direct, replacement for alt attributes the longdesc attribute, and associated descriptive web page, is intended to be used in conjunction with alts in order to enable us to provide our visitors with as much information as they might require about the imagery used.

Up arrow