The Shadow World of CSS Tables

The future of web layouts is promising, especially for the wizened web dev whose deeply nested tables still haunt the internet. But, until flexbox is ready for prime time, CSS floats—the core layout mechanism for CSS frameworks like Twitter Bootstrap and Zurb Foundation—will have to suffice.

Or will they?

Some of today's brilliant young web cats might not be aware that there was a vacuum created when TABLE-based layouts were dethroned. CSS Zen Garden, which showcased the promise of CSS layouts and put a dagger in the heart of TABLE-based ones, didn't necessarily coronate a successor.

There was the Holy Grail technique, Faux Absolute Positioning, and many other slices of PIE. It was a time of experimentation, the spirit of which has dimmed a bit in light of all the lovely frameworks out there (though some folks still experiment from time to time).

One technique that failed to gain traction was CSS table layouts. A number of things conspired against it:

  1. the title of the book advocating for it, Everything you know about CSS is wrong, was, let's face it, a little alienating
  2. the technique's very name conjured up demons of times past (TABLE layouts!)
  3. it was ahead of its time—before two important browsers of the epoch supported it (IE6/IE7)

But recently, a number of folks have revisited the technique because, in a line or two of code, it enables positioning that other techniques cannot without hacks or JS. Moreover, it offers source-ordering sleights of hand that come in handy in responsive contexts.

Vertical centering

Frontend devs are familiar with the challenge of aligning elements vertically in a row when heights are unknown. Indeed, it's usually the first problem one has to solve in any site's HEADER (logo and navigation).

TABLEs made vertical centering easy with the VALIGN attribute. In float-based layouts, things are much murkier, requiring lots of trickery (positioning, padding, or line-heights usually).

Enter CSS tables, which make vertical centering trivial with the vertical-align: middle property. To illustrate, take the following markup:

    <div class="row-table">
      <div class="cell">
        foo
      </div>
      <div class="cell">
        bar
      </div>
      <div class="cell">
        baz
      </div>
      <div class="cell">
        foo<br>bar<br>baz<br>qux
      </div>
    </div>
  

Combine it with the following CSS:

    .row-table {
      display: table;
      table-layout: fixed;
      width: 100%;
    }

    .cell {
      display: table-cell;
      width: 25%;
      vertical-align: middle;
    }
  

And you get the following vertically centered layout, despite the last column's taller content:

foo
bar
baz
foo
bar
baz
qux

An added advantage of this method is that most backend devs just get TABLEs (I'm looking at you, Ara). So, if you write styles for other people to use, you can create an OOCSS grid system with intuitive classes and sub-classes (".col", ".col-valign-middle", ".col-valign-top", etc) to make their lives easier.

Equal height columns

With the same markup as above, it's quite easy to create equal height columns; we'll just add some background colors to demonstrate:

foo
bar
baz
foo
bar
baz
qux

Sure, there are JS libraries that make floated columns do this, but there's all kinds of window-resizing funny business one has to do in a responsive context to prevent the columns' (JS-calculated) heights from trickling down to smaller viewports.

Instead, with a simple media query, I can make CSS tables transform into blocks (display: block) or table groups (display: table-header-group) depending on what's appropriate for the given context.

Source ordering tricks

CSS table properties can come in handy when you want to re-shuffle the hand you were dealt by the HTML source order. In float-based layouts, you can push and pull elements in larger viewports, but things will stack up the same as the HTML source order on smaller ones.

CSS tables allow you to significantly reorder things, particularly on smaller viewports. Take our previous example, for instance:

0
1
2
3

By changing the display setting to one of the table group properties (display: table-caption, display: table-header-group, display: table-row-group, or display: table-footer-group), we can stack elements quite differently than they appear in the row layout on larger viewports (than the source order).

For instance, if we wanted the columns above to stack in the following order (2,3,1,0), we'd apply the following display properties to the cells (from left to right): display: table-footer-group (0), display: table-row-group (1), display: table-caption (2), display: table-header-group (3). And the cells would then stack like so:

0
1
2
3

Note: I had to add a width: 100% rule to the display: table-caption element because otherwise it would not fill up the available space.

Why the heck would you want to do that?! Well, you might be doing one of those clever footer navigation tricks for small viewports and want all the nav that's in your HEADER to snap down to the bottom of the page.

Or maybe you just have some column in a row that should be viewed first on small viewports but is displayed in the middle of a row on larger viewports.

The possibilities are NOT endless or nearly as robust as flexbox's will be, but it's something to tuck away in the back of your toolbox.

Notes

CSS tables were supported in Internet Explorer starting with IE8. Otherwise, support is quite robust.

Although Everything You Know about CSS Is Wrong might sound like a daunting book title, it's actually a good read—and some of the comments on Amazon are amusing.

If you're entertaining the idea of putting something like a scrollable carousel inside a CSS table cell, study up on the table-layout property, in particular the "fixed" value. This prevents the cell from growing to contain its contents and incidentally can decrease rendering times because browsers don't have to calculate all the various widths of all the various contents of your cells.

I've run across a peculiar bug in Firefox concerning positioning elements within HTML and CSS table cells. The "path of least markup" solution, discovered by a current Mozilla employee, is to give both the table row and cell a height of 100%. Another well-documented workaround is to use an element inside the cell to establish a positioning context.

Ian Yates wrote an excellent Tuts+ article about using CSS tables for ad blocks, which tend to represent fixed components in a liquid layout.

Ben Frain has done some really interesting performance tests comparing flexbox to CSS tables. His caveats about sample size and lack of true specs for flexbox should be noted.

Roger Johansson noted in October, 2011 that some screen-readers treat CSS tables as HTML tables, which is a drag. Maybe that's changed in the past two and a half years?

Like anyone working on the internet, I make lots of mistakes. Please shout via Twitter if I've made any here or if you have interesting insights or if you have free beer.