Practical CSS Layout Tips, Tricks, & Techniques

The Web Standards Project’s (WaSP) Browser Upgrade Initiative (BUI), has spurred many a designer to move towards more standards compliant web design, using CSS rather than tables for layout to save user bandwidth while enhancing underlying semantics, accessibility, and reach.

Article Continues Below

“Tables are dead…”#section2

Several designers have taken Jeffrey Zeldman’s lead in posting tutorials that have helped us get over the initial hump of table-less design. The first efforts have focused on creating two or more columns using CSS positioning instead of tables, thus allowing for a (complete) separation of structure from presentation. These broader techniques have been documented and archived at Eric Costello’s glish and Rob Chandanais’ Blue Robot.

Others have chimed in, including Owen Briggs’ Box lesson and Tantek Çelik’s box model hack/workaround, detailed by Eric Costello, and explained by Tantek.

“…Long live tables”#section3

While these excellent resources address the larger issue of creating a general layout using only CSS positioning, other practical questions arise as we find ourselves, as designers, faced with a problem that is trivially easy to solve with tables, but not so obvious with CSS. Such a question was posed on the Webdesign-L list with the subject line “Tables are dead … long live tables.”

The question#section4

Suppose you want to have a bunch of thumbnail images that link to larger versions of the images – a fairly common type of web page. Suppose further that each image contains a short caption that you would like to keep centered underneath its image. And, in the interest of conserving browser window real estate, you want to line these image/caption pairs up in rows across the screen, but in such a way that they will wrap as necessary depending on the width of the browser window (liquid design). With the last requirement we have stepped out of the realm of tables, and into the realm of CSS.

One Step at a Time#section5

Let’s look at this step-by-step. The first requirement is that the thumbnails have their caption centered beneath them. This is relatively straightforward: in your HTML place your image, followed by a break, and then put your caption in a paragraph that is aligned to the center (via CSS).

Next we want to line up these image/caption pairs across the browser window. Using tables for layout, each of these image/caption pairs would go into a separate TD. Using CSS we need to put them into a separate DIV. To get them to line up horizontally across the window we use CSS to FLOAT each of these DIVs to the left.

Here is what the CSS might look like at this point:

div.float {
  float: left;
  }
  
div.float p {
   text-align: center;
   }

And the HTML:

<div class="float">
  <img src="image1.gif" width="100" height="100"
  alt="image 1" /><br />
  <p>caption 1</p>
</div><div class="float">
  <img src="image2.gif" width="100" height="100"
  alt="image 2" /><br />
  <p>caption 2</p>
</div><div class="float">
  <img src="image3.gif" width="100" height="100"
  alt="image 3" /><br />
  <p>caption 3</p>
</div>

And here is what it looks like in the browser:

image 1

caption 1

image 2

caption 2

image 3

caption 3

 

The next requirement can only be solved using CSS. We want the image/caption pairs to wrap if there are more than will fit in the browser window. FLOATing the DIVs to the left has already solved this problem. If we repeat those sample thumbnails a couple of times, they will wrap in the browser window:

image 1

caption 1

image 2

caption 2

image 3

caption 3

image 1

caption 1

image 2

caption 2

image 3

caption 3



Now, suppose you had more than one category of thumbnails you wished to display on your page, and you want to group them visually, with a background and/or border. Simply enclose them in a container DIV:

div.container {
  border: 2px dashed #333;
  background-color: #ffe;
  }

However when we do this we run into a problem. When you float an element with CSS, it no longer takes up any “space” and the background and border show up above the images instead of surrounding them. We need to put some content other than the floated DIVs into the container DIV. Like a spacer DIV:

div.spacer {
  clear: both;
  }

Now the following HTML (note that there are spacer DIVs at the top and bottom of the container DIV):

<div class="container">
<div class="spacer">
  &nbsp;
</div><div class="float">
  <img src="image1.gif" width="100" height="100"
  alt="image 1" /><br />
  <p>caption 1</p>
</div><div class="float">
  <img src="image2.gif" width="100" height="100"
  alt="image 2" /><br />
  <p>caption 2</p>
</div><div class="float">
  <img src="image3.gif" width="100" height="100"
  alt="image 3" /><br />
  <p>caption 3</p>
</div><div class="spacer">
  &nbsp;
</div></div>

…gives us what is shown next:

 
image 1

caption 1

image 2

caption 2

image 3

caption 3

 
Based on code from Sam Marshall.

Nested DIVs, nested TABLEs, what’s the difference?#section6

So now we have a bunch of nested DIVs. How is this any better than the nested tables they replace? The answer lies in the way the tag was intended to be used. DIVs imply a logical, or structural grouping. Even when they are nested they remain structural markup. In our example we grouped images with their captions (first level), and then grouped these image/caption pairs with similar image/caption pairs (second level). These are both examples of structural grouping that is handled quite well by the DIV tag.

However, tables imply a relationship between column and/or row headers, and the data in the table cells. When we use them for layout, we lose the structural semantics of a table. And we are back to using HTML for layout. Nesting tables only compounds the problem.

FORM(s) and Function#section7

Another common use for table layout is lining up FORM elements with their labels. While it could be argued that this is an appropriate use of TABLEs, the CSS technique that I describe below will prove to be useful for other, similar layout needs, as we will see.

A typical layout for FORMs has the labels on the left, flush right, with the form elements on the right, flush left, with everything meeting in the middle:

Name:
Age:
Shoe size:
Comments:
 
Based on original code concept from Eric Meyer.

The form above was created without the use of TABLEs. Once again we are using FLOAT to accomplish the positioning. The trick is to create a DIV that works like the TABLE row that we are used to using. Then we’ll create two SPANs: one for the labels and the other for the form elements. Float the label SPAN left and the form element SPAN right. For the label SPAN, align the text right, and give the form element SPAN left-aligned text.

The CSS looks like this:

div.row {
  clear: both;
  padding-top: 10px;
  }div.row span.label {
  float: left;
  width: 100px;
  text-align: right;
  }div.row span.formw {
  float: right;
  width: 335px;
  text-align: left;
  } 

The CSS above also gives widths for the SPANs. These can be absolute values like the example, or percentages that add up to 100% or slightly less, depending on padding and borders (and the box model you are designing for). In the example I have wrapped the form in another DIV to give it a border and a background.

The example HTML looks like:

<div style="width: 350px; background-color: #cc9;
border: 1px dotted #333; padding: 5px;
margin: 0px auto";>
  <form>
    <div class="row">
      <span class="label">Name:</span><span
class="formw"><input type="text" size="25" /></span>
    </div>
    <div class="row">
      <span class="label">Age:</span><span
class="formw"><input type="text" size="25" /></span>
    </div>
    <div class="row">
      <span class="label">Shoe size:</span><span
class="formw"><input type="text" size="25" /></span>
    </div>
    <div class="row">
      <span class="label">Comments:</span><span
class="formw">
        <textarea cols="25" rows="8">
        Go ahead - write something...
        </textarea>
      </span>
    </div>
  <div class="spacer">
  &nbsp;
  </div>
 </form></div>

Front and Center#section8

You may have noticed part of the STYLE attribute for the containing DIV above contained the following: margin: 0px auto;. This results in the 400 pixel DIV being centered in standards compliant browsers. Some browsers, like IE5.x for Windows ignore this, but will (incorrectly) center DIVs that have a TEXT-ALIGN of center. To center DIVs in these browsers you can wrap a DIV with TEXT-ALIGN set to center, around another DIV that sets MARGIN to auto (and TEXT-ALIGN to left so text will align correctly). See Rob Chandanais’ Layout Reservoir for this and other centering technniques.

Splitting the Difference#section9

A similar layout that is typically solved with tables is essentially the opposite of the above. Instead of meeting in the middle, you might want to place two elements at opposite sides of the browser window. This might be a case where you have a small logo that you want at the top right corner of your page, and some navigational elements at the top left:

Home > Products[logo]

Here we will use the same DIV.ROW, but different SPANs than we did for aligning the FORM elements with their labels. The SPAN on the left will float left, and contain left-aligned text. The SPAN on the right will float right and contain right-aligned text.

CSS:

div.row span.left {
  float: left;
  text-align: left;
  font-weight: bold;
  color: #fff;
  width: 49%;
  }div.row span.right {
  float: right;
  text-align: right;
  font-weight: bold;
  color: #fff;
  width: 49%;
  }

HTML:

<div style= "width: 90%; background-color: #666;
border: 1px solid #333; padding: 0px; margin: 0px auto;">
<div class="spacer"></div><div class="row"><span class="left">
Home > Products</span>
<span class="right">
[logo]</span></div><div class="spacer"></div></div>

CSS Help#section10

The ACRONYM and ABBR tags are useful, if little used, tags that use the TITLE attribute to expand the acronym or abbreviation. Most current browsers do nothing to alert your site visitors that there is anything behind the words on the page that can help them make sense of the abbreviation or acronym. Enter CSS.

In your style sheet you can add a bottom border to those tags to draw attention to them on the page. You can also use CSS to change the cursor into a “Help” cursor (usually a question mark) for those browsers that support it. And you aren’t limited to HTML tags. Create a class called .help and use SPANs to give more information about words or phrases that your readers might be confused by.

This CSS:

abbr, acronym, .help {
  border-bottom: 1px dotted #333;
  cursor: help;
  }

… is used in conjunction with the TITLE attribute on an ABBR or ACRONYM tag to create an underline effect that is different from the hyperlink underline. Changing the cursor to “help” implies that the word is not clickable, and the TITLE attribute expands the abbreviation or acronym. I first saw this done at a site by Sander Tekelenburg.

Checking it twice…#section11

I first read about changing the display of a list to inline in Bos and Lie’s Cascading Style Sheets. I first saw it put to use in Christopher Schmitt’s Babble List website. The trick takes a list and makes it display inline, or horizontally. So instead of:

  • Item one
  • Item two
  • Item three

…you get:

  • Item one
  • Item two
  • Item three

Adding some padding and a border effect gives:

  • Item one
  • Item two
  • Item three

CSS:

li.inline {
  display: inline;
  padding-left: 3px;
  padding-right: 7px;
  border-right: 1px dotted #066;
  }li.last {
  display: inline;
  padding-left: 3px;
  padding-right: 3px;
  border-right: 0px;
  } 

HTML:

<ul>
<li class="inline">Item one</li><li class="inline">Item two</li><li class="last">Item three</li>
</ul> 

The End? Or just the beginning…#section12

It is my hope that in sharing these tips, tricks and techniques, you will be encouraged to use more CSS Layout in your web work, and that you will continue to discover, and share, new tips, tricks and techniques with others.

22 Reader Comments

  1. Hi Mark,

    Just wanted to thank you for your excellent article about replacing tables with CSS for photo thumbnails. It was exactly what I wanted.

    However, in testing it worked fine in a simple example, but when I moved it into my site it didn’t go so well. (The old table based pages are online).

    It started doing very strange things for example: When I hit refresh the text above the main div blocks gets wiped out. Resizing the window slightly restores is. Hitting f5 kills it again. To be this sounds very much like a browser bug, but I’m using the latest IE on XP.

    The example given seems to work well if you have margins or any other stuff around your page, but if you use a 3 coloumn layout like mine (check my site) the black border of the frame runs at times under the edge of the thumbnails during resize operations. You can see an example of my 3 coloumn layout at my site, and if you go to photos you’ll see the table structure I am trying to replace.

    Anyway, this is an old article, and not sure if you are able to respond to this, but if you are, and you have time to help me out, I’d love to send you an example of what isn’t working just to check if I am not going insane… My backup plan is to go back to tables – I did really want to make a good go at converting my photo skin (the one you see at the site) into pure CSS before I release it.

    Mark

  2. I looked at this demo 2 days ago on the old site…like the new look! But it broke your lovely demostration. Now the images don’t wrap.

  3. Using the sample code for layout out thumbnails in an auto-resizing way, I noticed that if one image’s caption extends to a second (or third) line under the image the wrapping doesn’t handle it well.

    Is there a way to make this technique smarter about the height differences of the DIVs ?

  4. Hi,

    I’ve been trying use this example to display photo thumbnails in 3 columns and 4 rows. It works, but in IE6 and NS6 there is extra space under each row. Even if I set all margins and padding to zero there is space under each row. In Opera5 and Mozilla Firebird 0.7 everything displays perfectly. You can view a test page I’ve been working on at the url below:

    http://www.xcelco.on.ca/~aross/demo/css_test2.html

    It may be a small thing, but it still bothers me and you really notice it with smaller thumbnails. I never had this problem with tables and I’d really appreciate any advice you can offer.

    Thanks,

    Andrew

  5. Based on my experiments the photo album divs require having an explicit width set in order to work properly.

  6. The test on the excelco.on.ca site above works for me on IE6. On Firebird 0.7 the images float against the right border of each box they are contained in.

  7. I’m using the form example that he gave, and the result of the text next to the textarea is all the way at the bottom of the textarea, instead of at the top, like in said example. I’ve examined the css and all, and found nothing different, doctype is appropriate as well.

    anyone have this problem? and any insight on a solution?

  8. Everyone seems to be hyping standards-compliant, semantically valid markup. Every so often, though, we see something like this: use of the span tag with class attribute when a tag was specifically designed for this: label. Why?

    I’ve found < label > to be a good tag for this. I just link it to the form element with the “for” attribute, style it a little (I make it a block element so it’s on the line above the form input method), and I’m good to go with better markup than before. It also helps voice browsers tell users how better to access form elements. A link to some < label > information:

    http://htmlhelp.com/reference/html40/forms/label.html

  9. I’ve been looking for a way to align the element to the bottom of the label

    . The situation I’m trying to design for is when a label breaks to two lines, I’d like to allow the field to appear at the bottom of label.

    Example of what I want to do:

    Field
    Label :

    What happens now is :

    Field
    Label :

  10. I wanted to try your gallery code using inheritance. So, instead of :

    div.float

    I used
    div.container>div
    unfortunately, the browser acted as if the container div was empty. Even the spacer fix wouldn’t work. As a matter of fact, it only worked if I made spacer into a

    instead of a

    . Does anyone know why??

  11. I’m also using this code. it’s excellent for divs that have the same height. but, as darien notes above, if the divs each have different amounts of content–and therefore the divs have different heights–the wrapping breaks.

    there is an example of this ‘malfunction’ in a site that mark newhouse refers to from his site: http://www.janneland.co.nz/current.html

    mark newhouse’s site: http://realworldstyle.com/thumb_float.html

    thanks in advance to anyone who can help me resolve this.

    brad

  12. Great article! Lots of good info. Re: layout of forms, I like the idea but the layout doesn’t look that great if viewed without stylesheets… I tried this and it seems to work: I used a definition list inside my form. I gave the

    an id of “webform” and used contextual selectors to define style. I put the label tags in the

    tags and the inputs in the

    tags. I floated the
    ‘s left and set a static width. This lined things up well, then when viewed without stylesheets, each input was below the respective label and indented. But from a standards perspective, is this “technically” compliant? Any thoughts? Thanks!
  13. I have to agree with Jeff Walden on the comment about the

  14. I’ve tried to include the thumbnails method described here in the content div of a 3 floating columns layout, and it breaks with IE.

    The layout code is from the following page:

    http://css.maxdesign.com.au/selectutorial/tutorial_intro.htm

    Here are both my examples:

    http://www.gasteroprod.com/tests/3col_gal/example1.html
    http://www.gasteroprod.com/tests/3col_gal/example1.html

    Both work well with Firebird, but the second breaks with IE6 because the spacer div also clears the left column !

    Any help would be appreciated… 🙂

    Regards,

    Nicolas.

  15. This is in part a response to Jeff Walden’s comment regarding using the label tag to denote a label… as other’s have noted, he’s right. Using a label for this is more correct. However, I have run into a small snag with that, floating a label tag in NN4.8 causes it to disappear completely. The simple fix was to wrap the label tag in a span, and float the span instead – which puts us back where we started. My second solution removed the Float entirely, and used a PRE tag, which just seems just as wrong for the same reasons; semantics. I don’t WANT to preformat anything! But I DO want labels on left, inputs on right; and I want semantically correct, valid code.

  16. haikal, i like you suggestion as it avoids extraneous div’s. i’ve noticed that safari and older mozilla ignore width declarations on an inline div. this makes a thumbnail list with different size thumbnails very messy. is there a way around this?

  17. Thank you so much for this article. I especially appreciated the inclusion of so many concrete examples.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career