Use CSS display:table for Layout

I had this post in draft since October 2008. I thought I’d redesign this blog site using display:table and explain that in a series of posts, starting with this one. But I never found the time for the redesign!

Still, I have been using display:table on a much larger site for about 6 months now, so I thought I might as well post this as it is, and perhaps follow up with more examples later.

Also, about 3 weeks after I started this draft, Rachel Andrew and Kevin Yank wrote a book on CSS display:table for layout, as well as a useful summary article! I do recommend looking at that article for finer details.

No need for css float for layout in modern browsers

For a few years now, web developers doing CSS-based layouts have used floats or absolute positioning for layout web sites to avoid using non-semantic HTML <table>s.

While doable, extra hoops often have to be jumped through (mostly for IE) and some seemingly simple things can be harder than necessary (like equal height columns).

However, for a simpler solution, CSS-based display:table, display:table-row, display:table-cell etc are all usable today across Firefox 2+, Safari 3+, Opera 9+ and IE8.

Example:

Consider the following HTML:

<body>
	<div id="header">
		<!-- header -->
	</div>

	<div id="content-body-wrapper">
		<div id="content-body">
			<div id="primary-nav">
				<!-- some navigation column here -->
			</div>
			<div id="secondary-nav">
				<!-- some additional column here -->
			</div>
			<div id="content">
				<!-- main content here -->
			</div>
		</div>
	</div>
	
	<div id="footer">
		<!-- footer -->
	</div>
</body>

And the CSS to style it to get equal height columns:

#content-body-wrapper {
    display:table;
    border-collapse:collapse;
}
		
#content-body {
    display:table-row;
}
		
#primary-nav, #secondary-nav, #content {
    display:table-cell;
}
		
#primary-nav, #secondary-nav {
    width:20%;
}
		
#content {
    width:60%;
}

And that’s it!

The above is just the layout bit of the CSS. Here are some screenshots (click for full size) with content and very basic styling just to see the equal height column effect:

The first is with Firefox 3 and the second with IE8.

You can actually omit extra divs, even the one that gets display:table and the browser is required to create an anonymous table for you.

Isn’t using table for layout wrong?

This is not the same as using the structural html table elements for layout purposes — that indeed is an inappropriate use for tables.

This is using CSS to give table-like display, which is fine as it leaves the HTML (and document structure) in tact.

What about IE 7 and 6?

IE7 and 6 of course remain problems, but you can use conditional comments and give them older techniques that attempt to achieve this.

Some limitations or issues

Some limitations of css display:table I have come across, however, include these:

  • Lack of colspan/rowspan equivalents
  • Like HTML tables, a CSS cell can expand in width based on content (as well as height).

On the last one I noticed this when using things like <pre> even with overflow:auto with the thought that just like inside floated columns with widths assigned this would result in those <pre> blocks getting horizontal scrolls if they became too wide.

Instead, as with HTML tables, they push out the cell they are in. The only workaround I knew to this was to give a px or em width to such elements. (It also applies to large images in a cell.)

That being said, display:table seems a lot cleaner!

CSS3 has an advanced layout module in the works but there are not any browser implementations of it (that I am aware of), so in the interim this could be a useful approach.

More info

Translations

Update: May 2010. Translated into Belorussian, by Ucallweconn

Be Sociable, Share!

    37 thoughts on “Use CSS display:table for Layout

    1. I’ve been playing with this technique as well, but there’s something about it that bothers me…

      If you take the main portion of your markup, and convert the DIVs into appropriate tags, we end up with this structure:

      That’s the same number of tags, with the same number of CSS hooks. How is this any worse than the same set of DIVs? Especially when you consider that THIS structure is supported by the most popular browsers on the ‘net?

      I just wish there was a way to access the display: table-cell behavior, without needing to nest it inside of display: table and display: table-row… The extra markup overhead is enough to make me question CSS.

    2. Aaand, it ate my markup. Hopefully this shows up as somewhat readable.

      <table id=”content-body-wrapper”>
      <tr id=”content-body”>
      <td id=”primary-nav”>
      <!– some navigation column here –>
      </td>
      <td id=”secondary-nav”>
      <!– some additional column here –>
      </td>
      <td id=”content”>
      <!– main content here –>
      </td>
      </tr>
      </table>

    3. @AaronSieb: You don’t actually need the wrapper cells — CSS 2.1 supports “anonymous tables” (which I think is similar to HTML). This means you can provide just the divs that act as the layout cells and it will generate anonymous table structure around the cell.

      Providing those extra divs might be useful if you need to add hooks for additional CSS or JavaScript, for example. If not, then you can get away with something like this:

      <div class="primary-nav">nav content</div>
      <div class="secondary-nav">secondary nav content</div>
      <div class="content">main content</div>

    4. Hi,

      What is the exact fix for IE 6, I tried it in mozilla and i was really happy by seeing the output. Please can anyone provide the fix for IE

      Sanjeev

    5. @Sanjeev,

      There isn’t one exact fix for IE6, per se. It depends on your surrounding HTML. But for me, I have usually used CSS floats to get the layout I need, while others use absolute positioning. So for IE6, I’d normally try one of the more established layout techniques that have been around for a while that cater for IE6, but target IE6 using conditional CSS comments if you can. Hope that helps.

    6. Thanks Anoop,

      Actually i was really trying very hard to find some table layout without using Table then, i come across some techniques…

      left block
      body block
      right block

      * {margin:0;padding:0;}
      #main{display:table-row; list-style-type:none }
      #left,#body,#right {display:table-cell;}

      Its really worked for me

    7. Thanks Anoop,

      Actually i was really trying very hard to find some table layout without using Table then, i come across some techniques

      <ol id="main">
      <li id="main">left block</li>
      <li id="main">body block</li>
      <li id="main">right block</li>
      </ol>

      <style>
      * {margin:0;padding:0;}
      #main{display:table-row; list-style-type:none }
      #left,#body,#right {display:table-cell;}
      </style>
      Its really worked for me

    8. You can use jQuery to make all of the cells on the same row, the same height… even if there are a dynamic number of cells on each row (meaning all of the rows “Float Left” but the number of DIVs on each row depend on the width of the user’s browser window).

      Here is the jQuery for those of you who are interested (I’m assuming the floating DIVs have the class “floatingDiv” assigned to it):

      $(document).ready(function() {

      var currentTallest = 0;
      var currentRowStart = 0;
      var rowDivs = new Array();

      $(‘div.floatingDiv’).each(function(index) {

      if(currentRowStart != $(this).position().top) {

      // we just came to a new row. Set all the heights on the completed row
      for(currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) rowDivs[currentDiv].height(currentTallest);

      // set the variables for the new row
      rowDivs.length = 0; // empty the array
      currentRowStart = $(this).position().top;
      currentTallest = $(this).height();
      rowDivs.push($(this));

      } else {

      // another div on the current row. Add it to the list and check if it's taller
      rowDivs.push($(this));
      currentTallest = (currentTallest < $(this).height()) ? ($(this).height()) : (currentTallest);

      }
      // do the last row
      for(currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) rowDivs[currentDiv].height(currentTallest);

      });

      });

    9. CSS tables give the best of both worlds. But as usual IE is making front-end developers disappointed, IE6 amazingly has a big market share today and is mostly used in business environment.

    10. I don’t see the meaning of these (yet a lot buggy) properties.

      Seeing that {display:table-cell} doesn’t need a specific parent, it seems not so different than {display:inline-block}.
      Not to mention that {display:table-column} doesn’t work at all …

      Much better and much more interesting the CSS3 {display:box} with the others following box-properties.

    11. Pingback: When learning html - DesignersTalk

    12. @Stephen Akins: using javascript to make columns’ height equal is only good if the content inslde those columns is not dynamic (for example slide down elements or loading content dynamically with AJAX depending on user action).

      Once the heights are set on DomReady you would need a lot more code to recalculate proper height everytime something changes dynamicallly. Even then you could get page flicker when recalculating proper height.

      And what about when the height of some content elements is animated?

    13. @MJ

      That’s a good point, but I don’t think it’s difficult to fix. For instance, I have already modified my code above to work with the onresize event. (see http://stephenakins.blogspot.com/2011/01/uniform-div-heights-for-liquid-css-p.html). It’s not the same thing as what you’re talking abot but if the dynamic content could be made to trigger a reflow function that first got the new height, it should work fine. Maybe I’ll update my code to accommodate dynamic content.

    14. Hi Jake.

      A key aspect in the difference is semantic meaning – in CSS it is to help layout. It could certainly be argued as mimicking table markup by using divs but being in CSS, it is one of the many layout options (or hack — just like floats are a kinda hack to lay things out (and we do use floats everywhere for this purpose when it was originally intended to simply float stuff like images to left or right of content).

      They certainly aren’t the only layout tool though (display inline block, some new box model stuff in the works etc all look good too).

      In HTML however, using table markup semantically mean tabular data.

      So, you could use HTML tables if you want to do layout but using divs instead (or HTML5 markup such as section) help you partition your markup and style choices further.

      From a responsive design perspective, it may be useful that the table markup isn’t in HTML, giving you more flexible layout options for different devices/view ports, etc. For example, on a large enough screen, you might opt for CSS-based table display for a simple multi-column layout, but for narrow screens, you may do nothing; the divs just do their default. If this was HTML tables, it would require more effort for the smaller screens.

    15. i believe you need also a
      #content-body-wrapper { table-layout:fixed; }
      to make the columns behave properly, width-wise

      cause the default table behavior (which gets inherited here on the wrapper div) is that they adjust their width according to the contents… you pretty much write something about it, and that’s the solution i believe.
      check here:
      http://www.w3schools.com/cssref/pr_tab_table-layout.asp

      Also, a bonus: Notice that it specifically says about “width” and “horizontal layout”… so you could also add something like
      #content-body-wrapper { height: 100%; }

      and it actually works like a min-height!!!

      so you could use that just in case you don’t have enough content

    16. i mean… you shouldn’t in any case let the contents affect your layout. if you really want a flexible layout, setting the width (in percentage) of each individual table-cell styled div, should be enough

    17. “Isn’t using table for layout wrong?
      This is not the same as using the structural html table elements for layout purposes — that indeed is an inappropriate use for tables.
      This is using CSS to give table-like display, which is fine as it leaves the HTML (and document structure) in tact.”

      Bull Fucking Shit.

      etc is THE GOD DAMNED SAME as etc.

      FIY translating the first into the second is EXACTLY what webit does (used by safari and chrome)

    18. There are still reasons to stick to floats: CSS3 enhancementes.

      Things like box-shadow and border-radius don’t work in CSS table cells. They can be done with nested divs, but you loose the equal height benefit.

    19. @azr damn trying to make equal height work with border-radius is exactly my problem. It’s driving me nuts, I may have to use the java solution after all…

    20. “Never use tables” is over-generalized advice. Designers should absolutely feel free to use them whenever you want a table-like presentation regardless of the screen dimensions. When this is your pattern, then go right ahead and use markup, there’s no need to use CSS. The display logic behind is very elegant and robust, it handles an enormous number of edge cases regarding wrapping, alignment, flow, width, height, etc. The syntax of markup is simple and intuitive. Furthermore, is backwards-compatible all the way to the very beginning of web browsing, so there are never any compatibility worries.

      Just remember that has a fixed layout model. If you need a flexible layout model, then CSS is the right choice, instead. For example, if you want to progressively enhance the layout based on screen dimensions, then you must use CSS to pull this off.

      CSS has its own baggage to carry, especially its lack of backwards-compatibility and cross-browser support. You have to weigh the benefit of the dynamic layout capabilities against the added complexity and maintenance of dealing with multiple supported versions of CSS.

      So, there is nothing inherently “wrong” about using tables, per se. Sometimes they are exactly what is needed. There’s nothing like whipping up some simple markup and watching it render PERFECTLY on everything from an iPhone to a mega-monitor.

    21. Pingback: Four CSS tips that have changed me as a man | Evan Hahn

    22. Personally, I refuse to accept the argument that because somebody wrote in a book that tables cannot be used for layout, that therefore it doesn’t matter how many hoops we have to jump through and how much we have to struggle, we may not use tables for layout. I frequently use tables for layout because it is easy to write, easy to understand, and it works and works consistently, while using CSS floats simply does not. I have spent many unproductive hours trying to wrestle CSS floats into the desired layout when I could have done the same thing with a table in ten minutes. Sure, tables were not designed to create layouts. Floats were not designed to create multiple columns. So what? Separating content from presentation is a great idea, but the harsh reality is that CSS just isn’t there yet. CSS is great for things like setting fonts and borders. It’s clean and simple for that. But for laying out a page, it just sucks.

      All that said, using “CSS tables” like described here sounds like a wonderful “compromise”. I’ve never tried it, but I think I will now.

    23. Thanks for this posting, I needed a solution for transponding tables and your suggestions helped me a lot to achieve this: Being able to “rotate” tables and contents, therefore I could not use traditional table markup.
      I personally would not recommend completely table based layouts, but there are edge cases and therefore the proposed solution is a very clever one that works down to IE8.

    24. I use the div layout for displaying the tabular information quite often. I am currently working on a site that is responsive, and I have used the divs instead of tables to display the tabular data. Everything looks good until l resize my browser to a smaller width.

      What I want is when my browser is very small lets say 320px wide, i would want to be able to browse through the table by swiping left and right (overflow-x: visible). There is one example given here: – http://exisweb.net/responsive-table-plugins-and-patterns

      This is the example tabular data and layout (using divs):

      Sr. No
      Quantity
      Units
      Ingredient Name
      Calorie Count

      1
      1
      Whole
      Chicken
      1100

      2
      4
      Whole
      Large Onions
      100

      3
      4
      Whole
      Large Tomatoes
      100

      4
      1
      Cups
      Yogurt
      100

      This is the css:

      .ingredient-table {
      display: table;
      width: 100%;
      min-width: 420px;
      margin: 0 0 30px;
      }

      .ingredient-table .row { display: table-row;}

      .ingredient-table .row.table-header { background: #333;}

      .ingredient-table .row.table-header span {
      font-weight: bold;
      text-transform: uppercase;
      color: #fff;
      }

      .ingredient-table .row.even { background: #ededed;}

      .ingredient-table .row span,
      .ingredient-table .row strong {
      display: table-cell;
      border-bottom: 1px solid #ddd;
      line-height: 2;
      text-indent: 10px;
      }

      Here I have given the minimum width of the table (div with the class of .ingredient-table) to 420px. So at the smaller screens like cell phones, the table appears cropped, because the width is set to 420px. and there is no way i can swipe left or right to see the complete row.

      I have even tried giving overflow-x: auto to the div with the class of .ingredient-table. But nothing happens.. Can someone tell me what am I doing wrong. Thanks.

    25. I am sorry… This is the markup:

      Sr. No
      Quantity
      Units
      Ingredient Name
      Calorie Count

      1
      1
      Whole
      Chicken
      1100

      2
      4
      Whole
      Large Onions
      100

      3
      4
      Whole
      Large Tomatoes
      100

      4
      1
      Cups
      Yogurt
      100

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>