Digital Web Magazine

The web professional's online magazine of choice.

Everything You Know About CSS Is Wrong

Got something to say?

Share your comments on this topic with other web professionals

In: Articles

By Rachel Andrew

Published on October 21, 2008

When released, Internet Explorer 8 will support many new values for the CSS display property, including the table-related values: table, table-row, and table-cell—and it’s the last major browser to come on board with this support. This event will mark the end of complex CSS layout techniques, and will be the final nail in the coffin of using HTML tables for layout. Finally, producing table-like grid layouts using CSS will be quick and easy.

Applying table-related display property values to web page elements causes the elements to mimic the display characteristics of their HTML table equivalents. In this article, I’ll demonstrate how this will have a huge impact on the way we use CSS for web page layouts.

Using CSS Tables

CSS tables solve all the problems encountered when using absolute positioning or floats to create multi-column layouts in modern browsers. Specifying the value table for the display property of an element allows you to display the element and its descendants as though they’re table elements. The main benefit of CSS table-based layouts is the ability to easily define the boundaries of a cell so that we can add backgrounds and so on to it—without the semantic problems of marking up non-tabular content as a HTML table in the document.

Before we dive in and discover how this works, let’s create an instant demonstration:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US"> 
  <head> 
    ⋮ HTML head content… 
  </head> 
  <body> 
    <div id="wrapper"> 
      <div id="header"></div> 
      <div id="main"> 
        <div id="nav"> 
          ⋮ navigation column content… 
        </div>
        <div id="extras">
          ⋮ news headlines column content… 
        </div>
        <div id="content">
          ⋮ main article content… 
        </div>
      </div>
    </div>
  </body>
</html>

The HTML source matches the desired content display order. The nav column comes first, followed by the extras column, and then the content column.

We also need to apply the following CSS:

#main { 
  display: table; 
  border-collapse: collapse; 
} 

#nav { 
  display: table-cell; 
  width: 180px; 
  background-color: #e7dbcd; 
} 

#extras { 
  display: table-cell; 
  width: 180px; 
  padding-left: 10px; 
  border-right: 1px dotted #d7ad7b; 
} 

#content { 
  display: table-cell; 
  width: 380px; 
  padding-left: 10px; 
} 

The fresh CSS table-based layout that we’ve just created will display correctly in Internet Explorer 8 as well as in Firefox, Safari, and Opera; the image below shows how it looks in IE8.

Our three-column equal-height layout is achieved without having to resort to tricks like faux columns using background images, worrying about positioning, or having to clear floats—revolutionary!

How Does This Work?

The display property allows you to specify a range of table-related values in order to make elements display as though they were table elements. The available display values are:

Hang on… Aren’t Tables for Layout Wrong?

Perhaps you’re feeling slightly uncomfortable about the example we’ve just seen—after all, haven’t web standards advocates like myself been insisting for years that you shouldn’t be using tables for layout?

The table element in HTML is a semantic structure: it describes what data is. Therefore, you should only use the table element if the data you are marking up is tabular—for example, a table of financial information. If it would normally be stored in a spreadsheet on your computer, it probably needs marking up as a table in HTML.

The table value of the display property, on the other hand, is simply an indi­cation of how something should look in the browser—it has no semantic meaning. Using a table element for your layout tells a user-agent, “This data is tabular.” Using a bunch of divs that have the display property set to table and table-cell says nothing to that user-agent other than asking it to render them visually in a certain way, if it’s capable of doing so.

Of course, we should also take care not to use display: table; on a bunch of div elements when what we really have is tabular data!

Our simple example above makes our layout behave as if it were a single row table with three cells; it doesn’t take much imagination to realize the potential of this technique for creating complex grid layouts with ease.

Anonymous Table Elements

CSS tables happily abide by the normal rules of table layout, which enables an ex­tremely powerful feature of CSS table layouts: missing table elements are created anonymously by the browser. The CSS2.1 specification states:

Document languages other than HTML may not contain all the ele­ments in the CSS 2.1 table model. In these cases, the “missing” elements must be assumed in order for the table model to work. Any table element will automatically generate necessary anonymous table objects around itself, consisting of at least three nested objects corresponding to a “table”/“inline-table” element, a “table-row” element, and a “table-cell” element.

What this means is that if we use display: table-cell; without first containing the cell in a block set to display: table-row;, the row will be implied—the browser will act as though the declared row is actually there.

Let’s use a simple example to investigate this feature: the three-cell grid layout shown below. We’ll look at three different HTML markup samples that will result in the same visual layout.

First, here’s a sample of markup that can be used to generate the three-cell layout:

<div class="container">
  <div class="row">
    <div class="cell">CELL A</div>
    <div class="cell">CELL B</div>
    <div class="cell">CELL C</div>
  </div>
</div>

A set of nested div elements may not seem so very exciting, but hang in there, we’re building to something. The CSS is also very simple:

.container {
  display: table;
}

.row {
  display: table-row;
}

.cell {
  display: table-cell;
  width: 100px;
  height: 100px;
  border: 1px solid blue;
  padding: 1em;
}

The CSS above sets the element with a class of ‘container’ to display: table, an element with a class of ‘row’ to display: table-row, and an element with a class of ‘cell’ to display: table-cell, as well as giving it a border and a height and width.

This HTML markup above explicitly creates elements for the table and row surroun­ding the three cells, using all of the CSS classes that we’ve created. However, we can reduce the markup, removing the row div element like so:

<div class="row">
  <div class="cell">CELL A</div>
  <div class="cell">CELL B</div>
  <div class="cell">CELL C</div>
</div>

Even though the above markup is missing the element representing the table row, the row will be created by the browser as an anonymous box. We can reduce the markup even further:

<div class="cell">CELL A</div>
<div class="cell">CELL B</div>
<div class="cell">CELL C</div>

The above markup is missing the elements representing the table row and the table; these are both created as anonymous boxes by the browser. Even with the elements missing in markup, the end product is the same.

Rules for the Creation of Anonymous Table Elements

These anonymous boxes are not created by magic, and they won’t automatically make up for any deficiencies in your HTML code. To be able to take full advantage of anonymous table elements, you’d best become familiar with the rules for their creation. If a layout calls for an implied element, the browser will create an anony­mous box and set its CSS display property to one of table, table-row, or table-cell, depending on the context.

If you have an element that has been set to display: table-cell; but its immediate parent (the containing element) is not set to table-row, an anonymous box set to table-row will be created to enclose the cell and any subsequent adjacent sibling elements that are also set to table-cell, until it encounters an element not set to table-cell, so they’ll all end up in the same row. This is the case with the following markup:

<div class="cell">CELL A</div>
<div class="cell">CELL B</div>
<div class="cell">CELL C</div>
<div>Not a cell</div>

The three div elements above that have a class of ‘cell’ are set to display: table-cell; and will appear side by side as though they’re in a single row table; the last div element won’t be included in the row, because it isn’t set to display: table-cell;.

If an element is set to display: table-row; while its parent element isn’t set to table (or table-row-group), an anonymous box set to display: table; will be created to enclose the row, and any subsequent adjacent sibling elements will be set to display: table-row. Also, if the element with display set to table-row lacks an element set to table-cell directly within it, an anonymous box set to table-cell will be created to enclose all the elements within the table-row element.

Consider the following markup:

<div class="row">ROW A</div>
<div class="row">ROW B</div>
<div>Not a row</div>

The two div elements above with a class of ‘row’ are set to display: table-row; and will appear one under the other as though they’re rows in the same single-column table. The last div element won’t be included in the implied table.

Similarly, if an element is set to any of the other display values that match elements which would naturally exist directly inside a parent table element such as table-row-group, table-header-group, table-footer-group, table-column, table-column-group, and table-caption, but does not have a parent set to display: table;, an anonymous box set to table will be created to enclose the element and any subsequent adjacent sibling elements with suitable display values.

Other Useful Table Properties

When using CSS tables, because the elements conform to the normal rules for table layout, you can also apply other table-related CSS properties. Here’s a few that can come in handy:

table-layout

Setting the table-layout to fixed tells the browser that the table should render with the fixed algorithm for formatting the cell widths. This is useful in a fixed-width layout, such as the one we created earlier.

border-collapse

Just as with regular HTML tables, you can use the border-collapse property to specify that your table layout elements use collapsed (with the value collapse) or separated (with the value separate) borders between the cell elements.

border-spacing

If you specify the value separate for the border-collapse property, you can then use the border-spacing property to specify the width of the space between the cell element borders.

Making a Perfect Grid

Making a grid of equal height elements has always been a challenge using traditional CSS layout techniques, but it’s something to which CSS tables are well suited. For example, if we want to create an image gallery comprising a grid of images with captions, such as the one shown below, using a CSS table renders the task simple.

The markup for our gallery is as follows:

<div class="grid"> 
  <div class="row"> 
    <div class="image"> 
      <img src="images/photo1.jpg" alt="A Lily" /> 
      <p>A lily in the gardens of The Vyne Country House</p> 
    </div> 
    <div class="image"> 
      <img src="images/photo3.jpg" alt="A Fuchsia plant" /> 
      <p>Fuchsia plant in my garden</p> 
    </div> 
  </div> 
  <div class="row"> 
    <div class="image"> 
      <img src="images/photo2.jpg" alt="A crazy looking Allium flower" />
      <p>A crazy looking flower</p>
    </div>
    <div class="image">
      <img src="images/photo4.jpg" alt="A Robin sitting on a fence" />
      <p>
        This robin has been visiting our garden over the summer. 
        He is very friendly and doesn't seem to be too worried about sharing the garden with us.
      </p>
    </div>
  </div>
</div>

Each gallery image cell is comprised of an img element and a caption in a p element contained within a div element with a class of ‘image’. Each row is contained within a div element within a class of ‘row’, and the whole gallery is contained within a div with a class of ‘grid’.

The CSS required to lay out our grid is simple:

.grid { 
  display: table; 
  border-spacing: 4px; 
} 
.row { 
  display: table-row; 
} 
.image { 
  display: table-cell; 
  width: 240px; 
  background-color: #000; 
  border: 8px solid #000; 
  vertical-align: top; 
  text-align: center; 
} 
.image p { 
  color: #fff; 
  font-size: 85%; 
  text-align: left;
  padding-top: 8px;
}

The above CSS is fairly straightforward, but you might notice how we’ve made use of the border-spacing property to control the spacing of our gallery image cells. Making a grid layout couldn’t be easier—and we’ve avoided any headaches over equal heights or fragile layouts made with floated elements.

Putting Principles into Practice

This article has presented a basic primer to the usage of the table-related values of the CSS display property—finally, a source of relief for all those struggling to construct reliable grid-based layouts using CSS! We had an introduction to the straightforward approach to layout provided by CSS tables. We explored the various table-related display values available, looked at the nature of anonymous table elements, and discovered some other useful CSS table properties.

The next step is up to you—with any luck, you have realized the potential CSS tables provide for creating grid layouts, and are now bursting with curiosity! Using the knowledge gained in this article, you’re all set up to begin experimenting with your own CSS table layouts and create new techniques.

This chapter is taken from the book Everything You Know About CSS Is Wrong by Rachel Andrew and Kevin Yank, published by Sitepoint and available now.

Got something to say?

Share your comments  with other professionals (107 comments)

Related Topics: XHTML, Web Design, HTML, CSS

 

Rachel Andrew is a director of web-solutions at edgeofmyseat.com. Rachel has written The CSS Anthology as well as HTML Utopia: Designing Without Tables Using CSS.

Media Temple

via Ad Packs