Geoff Chappell - Software Analyst
CURRENT WORK ITEM - PREVIEW ONLY
How does anyone involved with the CSS standard hold their head high about the mess that CSS makes of styling columns in tables!
Having opened with a provocation, it’s as well to get straight to an example of a column styling that is obviously desirable and is just as obviously made into a mess by CSS. But to lighten your load from my sense of outrage, let’s have a theme on the side. Lately, I have been collecting boxed sets that reissue on shiny plastic a lot of music that I bought as vinyl in my teens, but now the music comes with bonus tracks whose official release was for decades only the stuff of dreams. A tabular summary of an imminent release might look like:
Product | Price |
---|---|
6-disc super-deluxe book | $129.99 |
2-CD deluxe edition | $24.99 |
1-CD sampler | $9.99 |
Just about anyone in the English-speaking world, if not more widely, will want that the prices are right-aligned. Many would say that it’s tidier if the heading above them is right-aligned too (and that the heading above the products would better be left-aligned). Some would go further and want a very particular alignment that has the decimal points in a neatly vertical line and, being fussy, they would want this made explicit even though it’s visually indistinguishable from ordinary right alignment, given that these prices all end the same with the age-old 99 cents. This, however, runs much too far ahead, not for being overly fussy, but because even the most basic right-alignment of a column of prices in a table has been problematic for CSS for more than two decades. How can this be so?
Just about any HTML author, as a preparer of content marked up with meaningful semantics, will want to describe the table above as having a column of products (editions, packagings or whatever) and a column of prices. At least as far back as December 1997, if dating by formally published standards (in this case the HTML 4.0 Specification), the natural expression of this will go something like:
<table> <colgroup> <col class="Products"><col class="Prices"> </colgroup> <tr> <th>Product</th> <th>Price</th> </tr> <tr> <td>6-disc super-deluxe book</td> <td>$129.99</td> </tr> <tr> <td>2-CD deluxe edition</td> <td>$24.99</td> </tr> <tr> <td>1-CD sampler</td> <td>$9.99</td> </tr> </table>
You might elaborate this with a caption (to name the artist and the album) and be explicit with a thead and a tbody, and give the table an id (perhaps again naming the artist and album) and a class too, and so on and so on, but the preceding suffices for the essential point of separating structure from presentation within the table. The HTML author marks up the content just enough to organise it as structural elements but goes no further with presentation than to support it by classifying the elements according to their differing purposes. Here, I think, this classification is done with both generality and economy.
For generality, the HTML author labels the column of Products as having a distinct purpose even though the HTML author has no distinct styling in mind. One may be wanted, if not now then perhaps if the table is ever expanded, and it is better to classify now than later. Perhaps the CSS stylist’s different concern, for presentation rather than content, will anyway find some merit in a background colour or a different font. Such particulars are not the HTML author’s business to specify, just to provide for. The cells in one column are semantically different from the cells in another, and the good HTML author conveys this to the CSS stylist (even if, as often, the one is just the other wearing a different hat).
The economy is that if we trust that the HTML author does good work, then much is conveyed by what is not marked up. One row is distinguished as containing headings but the other rows have nothing semantically distinctive about them and there’s also nothing to say about the semantics of their cells that isn’t already conveyed by which column they’re in. The rows and cells differ in their content, of course, but the author leaves this difference as being already clear enough to the reader. After all, if any of the prices were made too prominent, the reader might put the purchase aside as expensive nostalgia (instead of being overjoyed that the music industry has finally realised there’s an obvious market for one-time teenagers to spend their baby-boomer affluence on hours of session out-takes).
Of course, the point to making an example of such simple markup is that the HTML author’s good intentions of separating structure from presentation are about to hit a wall. So let’s press the pause button on the handover from HTML author to CSS stylist, and ask what might an HTML author reasonably imagine the CSS stylist can do with this markup.
Again dating by the formal publication of the standard, the markup above would have been cutting-edge in late 1997. The colgroup and col elements were new for HTML 4.0. So too was the class attribute. Also new, but not used above, is that the col element is allowed an align attribute for exactly the desired column-based alignment. Not only is its use shown by the Specification’s sample table but an HTML author at the time might already have seen it in real-world use. With the line of <col> tags above changed to
<col align="left"><col align="right">
the HTML author could have loaded the markup into Internet Explorer 4.0 (with executables dated September 1997) and got left and right alignment exactly as wanted and exactly as assured by (drafts of) the standard. But this understates the likely familiarity. The standard at this time was mostly catching up on nearly two years of browsers running ahead in hot competition to lock in authors by giving them tools for the presentation that they and their readers seemed likely to want. This same markup with col align works for a column’s data cells even in Internet Explorer 3.0 (meaning 3.0 exactly, not 3.01 or 3.02, but 3.0, with executables dated March 1996). A simple specification of alignment for a whole column might not yet have been supported by all browsers, but nobody looking at HTML 4.0 as the new standard will have had the slightest reason to imagine even briefly that column-based alignment is beyond the capability of browsers.
The great detraction of align for col is that it is a presentation attribute. It wasn’t born deprecated, but its use was indirectly discouraged from the start. The HTML 4.0 Specification repeats over and over that best practice is to avoid presentation attributes, which future specifications expect to “phase out as support for style sheets matures”. The example above heeds the good advice and leaves the presentation to a style element or to an external style sheet, having provided class attributes for selecting what are in effect named styles whose details of presentation are thus removed from the markup. Cascading Style Sheets, level 1, formally published in December 1996, says nothing specifically about tables—which weren’t standard HTML until the HTML 3.2 Reference Specification in January 1997—but does have a text-align property which applies to all block elements. If our HTML author wondered what CSS rules do the styling that is otherwise doable in the HTML by the presentation attribute align on each col, it would have been not the slightest bit fanciful to expect something like
col.Products { text-align:left; } col.Prices { text-align:right; }
Indeed, this won’t have been mere speculation. Remember that the standards of the time were playing catch-up. Exactly this CSS already had real-world support from Internet Explorer 4.0. Though even the most naive HTML author will have anticipated that the CSS replacement of col align might be browser-specific and take some time to settle, none will have thought for a moment that there can be none or that there never would be any. Even in the dark ages of web design, CSS for column-based alignment was demonstrably not fundamentally too hard to design and implement.
Now press the play button on this idealised handover from HTML author to CSS stylist. What does the CSS stylist say when given the HTML markup from above with its economical classification of the different columns’ different purposes? Believe it or not, but ever since Cascading Style Sheets, level 2 (formally published in May 1998), the response from a frank CSS stylist must be that the standards leave a choice along the following lines:
I’m sorry but I can’t work with this HTML. I can’t right-align the prices by column, though I could give them a background colour, if that would help. Please mark up each of the cells that contain prices that you want right-aligned. Please always remember to do this going forward.
Because the table is simple, I can fudge the right alignment without changing your HTML, but if you ever change the table too much, even just by swapping the columns or inserting one, you’ll have to come back to me for a change to the CSS.
This choice plainly falls far short of reasonable expectations, yet I defy any CSS expert to say this is not accurately the state of things, not just in 1998 but even now in 2021, after nearly a quarter-century.
How this has been allowed to stand is that everyone involved seems content to brush it aside. How this ever got to stand, I have no idea except that even as early as 1998 a single-minded insistence on single inheritance as ideally the only way that one element’s style is affected by another seems to have become established as so fundamental to CSS that a glaring disparity between the HTML and CSS table models has ever since been put too far out of bounds even for frank acknowledgement.
The immediate cause of the poor choice that faces the CSS stylist of table columns is that HTML4 defines the col element for classifying cells as a column but CSS2 greatly constrains what properties are allowed for a col selector.
There are just four: border, background, width and visibility (here in the same order as in the standard). In one sense, saying there are just four is unfair: two are shorthands for collections that are not insubstantial and one of these adds significantly to what little is otherwise available through presentation attributes in the HTML. In another sense, though, to count four is to overstate what styling is allowed, since none apply with full generality: border doesn’t apply at all unless the containing table has the border-collapse property set to collapse; background can show only if cells, rows and row groups have their background evaluate as transparent; width has a very particular interpretation; and anything other than collapsed for visibility is ignored.
Conspicuously, text-align is not among the four. Neither is vertical-align and perhaps more inconveniently in real-world practice, nor is color or any font property. If you imagine separate columns of income in green text and expenses in red, then tough, you’re put to essentially the same difficulty as sketched above for right-aligned prices. If you intend that one column is for snippets of technical material that should always be distinguished by some monotype font, then again, tough, you get no help from the standard. Curiously though, text-align, vertical-align, color and font all work for col, albeit with varying reliability, in Internet Explorer up to and including its version 7.0 even in what Microsoft’s documentation of the time rather fancifully keeps referring to as its “standards-compliant mode”. Especially curious is that when the question of styling columns without marking up every cell comes up on the Internet and commentators propose work-arounds, such as I will get to soon, they tend to grumble that these work-arounds aren’t usable for Internet Explorer 6.0 (and earlier), but they less often mention that there’s no need. These versions of Internet Explorer let you do the obvious thing that you likely want from all browsers: just style the column box!
Of the many properties that are not among the four that CSS2 blesses for styling the column box, text-align and vertical-align are special in that they already had explicit support from HTML4 as presentation attributes (align and valign, respectively) on colgroup and col elements. The new CSS2 could not in good faith write that “the non-CSS presentational hints must be translated to the corresponding CSS rules with specificity equal to zero” (see 6.4.4 Precedence of non-CSS presentational hints), having just fingered “the align attribute in HTML” as one such hint, and then not provide the corresponding CSS rules. Whatever else might for whatever reason be put aside about styling table columns, alignment certainly could not be.
For 17.1 Introduction to tables, CSS2 quickly assures authors that they “may align data vertically or horizontally within a cell and align data in all cells of a row or column.” This last capability, of aligning all cells in a column, is exactly what’s sought for this article’s Problem Example. But it’s also exactly what 17.3 Column selectors makes clear will not be as simple as replacing align on col in the HTML by any CSS rule for any col selector. What CSS2 provides instead for horizontal alignment is that
The horizontal alignment of a cell’s content within a cell box is specified with the 'text-align' property.
This is the first sentence of 17.5.4. Horizontal alignment in a column in CSS2 (and is the whole of this section in CSS2.1, but let’s leave that for later). It confirms that text-align is the intended CSS property for any CSS rule that would do the same work of the HTML attribute align, but it leaves as understood that since this property is not valid for col it must be specified (including by inheritance) for each td and th in the column.
What the CSS2 specification obliquely suggests is that if you want right alignment in columns, then your HTML should repeat a styling for each cell:
<table> <colgroup> <col class="Products"><col class="Prices"> </colgroup> <tr> <th class="Products">Product</th> <th class="Prices">Price</th> </tr> <tr> <td class="Product">6-disc super-deluxe book</td> <td class="Price">$129.99</td> </tr> <tr> <td class="Product">2-CD deluxe edition</td> <td class="Price">$24.99</td> </tr> <tr> <td class="Product">1-CD sampler</td> <td class="Price">$9.99</td> </tr> </table>
Now you can have right-aligned prices for as little trouble as
td.Price { text-align:right; }
(Here, and throughout for brevity, I write CSS rules as if there is only the one table in question.)
There is no small merit to marking up cell by cell, given that CSS does not provide for the obviously easier marking up of just the column. If we want not just right-aligned prices in the data cells but a right-aligned header above them and a left-aligned header for the products, then we can be confident that the following CSS
th.Products, td.Product { text-align:left; } th.Prices, td.Price { text-align:right; }
will get our desired presentation in plausibly all browsers from this century and from many (if not all) since late 1997. Even better, this styling is not only robust against browser variations but it is also robust against the future development of complexity in the table. As long as the HTML author is disciplined to assign classes to each cell even when thinking of them collectively as a column, then everyone should be happy.
Or not. For our particular case of right-alignment but also for many others that arise in practice, marking up a class on each cell plainly does not model either the author’s thinking or the stylist’s or the reader’s. Whose thinking it might model, I would leave for another time except that there’s surely just the one possibility: some browser manufacturer so feared the implementation of text-align and other properties for columns that they insisted the burden be shifted to HTML authors (if not to everyone else) and had sufficient sway over the standards process to get this arranged.
And quite some sway it must have been! How else can this delegation to the markup have got past the purists—or past anyone who had even half a thought for taming what was then the Wild West of HTML layout by educating authors to separate structure and presentation? Each class is in effect half a presentation attribute by stealth. Unlike the explicit presentation attribute align on col, it does at least leave the precise style of presentation to the CSS. But it is implicitly a presentation attribute because hardly any HTML author would think to add it just for the purpose of classifying content or modelling structure.
For some measure of “hardly any”, suppose the promotional page for our boxed-set of music has the following sentence somewhere in its surrounding text:
Get the super-deluxe book for just $99.99 if you order by 1st May.
If you’re an HTML author who would mark this up, habitually, as
Get the super-deluxe book for just <span class="Price">$99.99</span> if you order by 1st May.
then, yes, I will accept that styling the prices in each cell is natural for you. I will even admire your appreciation of good order—and I’ll sympathise too, as someone who compulsively lines up all the items on my dinner table. I’ll also be a little mystified, since if you are so thorough, then your marking up of the price will be first as a span around the price and second for the td whose whole content is the price, e.g.,
<td class="Price"><span class="Price">$9.99</span></td>
I don’t discount that someone somewhere would do this. After all, I don’t just line up the items on my dinner table, one relative to another: all labels must face the front.
In reality, of course, not only do hardly any HTML authors stop and mark up every price’s appearance even in plain text, but probably no commentators on good practice recommend it either. And why mark up just that the price is a price? Why not have a nested span for the currency symbol within the price, for the decimal point, and even for each group of three digits (as for a super-super deluxe edition with a faithful mix of the complete master tapes for thousands of dollars)? Why stop at prices? Mark up dates and times too.
I don’t dispute that there’s a good case for such closely attentive markup, if only as impractical theory, but neither do I see anyone making the case, and I say that unless you are prepared to make this case for prices in plain text, you are on unsafe ground making it for prices in tables except if you acknowledge readily that styling the price in each cell is only a hack to get past a deficiency of the CSS table model.
WRITING IN PROGRESS (but suspended for other work)