Web DevCenter    
 Published on Web DevCenter (http://www.oreillynet.com/javascript/)
 See this if you're having trouble printing code examples


Introduction to CSS Layout

by Eric Costello and Apple Developer Connection
03/01/2002

There aren't many web developers who have not at least tried using Cascading Style Sheets (CSS) to define typography and simple page attributes such as background and text color. These days more and more developers are pushing CSS even further, eschewing tables and embracing CSS as a web page layout tool. In this article I'll explain why CSS is in many ways preferable to using tables for layout, and then I'll show you some CSS layout techniques using a complete re-coding of the Apple Internet Developer home page as an example.

This article assumes a familiarity with Cascading Style Sheets (CSS).

In this article

What is CSS layout?

CSS layout uses style sheets to define the placement of elements on a web page. Typically CSS layout replaces tables, which are currently the most popular method of placing page elements. There is a common misconception that CSS layout techniques are incapable of producing complex page layouts. While it is true that tables generally provide more flexibility, I will show you that complex layouts are quite possible with CSS.

To get a quick look at the nuts and bolts of CSS layouts, take a look at the markup of a 3 column liquid page layout with a header, first done with tables, then with CSS.

Markup of 3 Column Layout with Tables

This is the traditional method of web page layout, accomplished with a table. Notice that the content of the document (which is just dummy text) is interspersed with markup intended to define the content's visual presentation:

<html><head><title>Table layout</title></head><body bgcolor="white">
<table cellpadding="0" cellspacing="0" border="0">
<tr><td colspan="3" valign="top">
  <font color="red" face="verdana, sans-serif">
  A banner that sits at...top of the page.
  </font>
</td></tr>

<tr><td width="190" valign="top">
  <font color="green" face="verdana, sans-serif">
  A column on ... left of the page.
  </font>
</td>

<td valign="top">
  <font color="blue" face="verdana, sans-serif">
  A column in ... center of the page.
  </font>
</td>

<td width="190" valign="top">
  <font color="orange" face="verdana, sans-serif">
  A column on ... right of the page.</font>
</td></tr></table>
</body></html>

Markup of 3 Column Layout with CSS

Notice that this markup is relatively uncluttered because I've removed the visual layout instructions:

<html><head><title>CSS layout</title>
<style type="text/css">
  @import url("threecolcss.css"); 
</style>
</head><body>
<div id="banner">
  A banner that sits at ... top of the page.
</div>

<div id="rightcontent">
  A column on ... right of the page.
</div>

<div id="leftcontent">
  A column on ... left of the page.
</div>

<div id="centercontent">
  A column in ... center of the page.
</div>
</body></html>

Of course you probably noticed the style element at the top of the markup which specifies an external style sheet for the example: @import url("threecolcss.css");. The style rules in that external document are as follows:

The relevant CSS

body {
  font-family: verdana, sans-serif;
  background:white;
  }

#leftcontent {
  color:green;
  position: absolute;
  left:10px;
  width:190px;
  }

#centercontent {
  color:blue;
  margin-left: 190px;
  margin-right:190px;
  
  }

#rightcontent {
  color:orange;
  position: absolute;
  right:10px;
  width:190px;
  }

#banner {
  color:red;
  }

Now, if you're not using Internet Explorer 5+, Opera 5+, or a browser based on Mozilla, such as NS6+, then those two pages (table layout and CSS layout) probably look quite different. In fact, although the table layout page will look nearly identical in all browsers dating back to Netscape 1.1, only the most recent browsers (such as those listed in this paragraph) will properly display the CSS layout. According to recent browser usage stats, those most recent browsers make up of roughly 75% of the browsing audience.

Related Reading

Cascading Style Sheets: The Definitive Guide
By Eric A. Meyer

Make no mistake: pages laid out with CSS will NOT look the same in newer and older browsers. Beginning with 5th generation browsers (including the most excellent IE5/Mac, Netscape 6 on all platforms, and Opera 5) CSS support for layout reached an acceptable level. Nearly identical CSS layouts are possible in this browser set. Older browsers, including the tenacious NS4, and even the ahead-of-its-time IE4, are lacking in their support of even the CSS1 recommendation from 1996. Consequently, CSS layout is for the most part impossible in these browsers, with the exception of non-liquid, pixel-specific layouts such as those used in games.

But don't despair. As you will see, CSS pages are degradable, and CSS will create a useable page for any and all browsers. CSS layout will be used only by those browsers which support those features, but those using older browsers will still be able to see your content. What you give up in absolute control over layout in every browser you gain back in increased accessibility and more useful documents.

But given that roughly 25% of your site visitors will not be able to see your layout (although everyone will have access to your content), the natural question is "Why use CSS for layout?"

Why Use CSS for Layout

I've already mentioned a few of the negatives of CSS layout and hinted at some of the benefits. Let's take a look in more detail at the pros and cons of CSS layout. In the end, I think you'll agree with me that the benefits far outweigh the problems.

Benefits of CSS layout

CSS allows you to separate the structure and presentation of documents. The separation of structure and presentation (often incorrectly referred to as the separation of style and content) is a principle that governs (or should govern) all markup languages, such as HTML. HTML is intended to structurally organize the content of a document so that it's clear what the conceptual relationship is between various portions of a document. The markup language is NOT intended to define the display of the document, although display and structure are often tightly connected.

Related articles:

A Primer for Accessible Web Pages -- A look at the history of Section 508, which enforces accessibility for government web sites, followed by a discussion about how to prepare pages for those who cannot effectively use a graphical user interface.

Modifying Styles -- Although browser support for Cascading Style Sheets has improved, modifying styles on the fly can still be painful at best. Fortunately, Steve Champeon provides a script to read and change an element's styles--regardless of where they were originally defined.

DOCTYPE Explained -- The DOCTYPE element, in the head of your document, tells the browser what kind of HTML is used to describe the file. The better you match the DOCTYPE to your code, the more accurate your work will be rendered. Here's an introduction.

Working with Fonts and CSS -- Changing fonts on web pages is anything but intuitive and easy. Fortunately, CSS can help.

As web developers we have been trained to see an <h1> tag as a way to make something bold and large, not as a way of marking it as a section header. The introduction of physical tags, such as <b>, <i>, <font> and the abominable <blink>, all of which specify display qualities of document elements and not structural qualities, have only made things more confusing for the developer. And this confusion is unfortunately reinforced by the behavior of web browsers, which apply styles to section headers and indeed make them large and bold. But it is wrong to think that the markup is what specified the style; in fact, the browser decides how to display a section header according to internal logic given to it by its programmers. Had they wanted, they might have specified that section headers be displayed italicized, or with an underline. And that is where CSS comes in to the picture. CSS allows you to override the browser's plans for displaying any particular page element or group of elements.

The separation of structure and presentation results in the following benefits:

Yes, by eschewing tables for layout and ridding your HTML of physical tags your page may not look as nice in NN4, but your content will be 100% accessible. Muck your pages up with tables and your site may look better in NN4, but you also interfere with other less capable browsers and internet devices from retrieving your content. In nearly all situations, I'll choose 100% accessibility. In some cases, of course, the design must win. Some content is only intended to be seen by 4+ browser users, and you could justify the use of table-based layout in such situations. In other situations it is worth the investment of time and money to create server-side solutions to provide content to devices with different capabilities. Doing so, you can feed table-based layouts to 4+ browsers, and feed clean and simple markup to older and less-capable devices, but most of us only want to maintain a single document, not a server application. Valid structural markup and CSS provide us with the opportunity to offer a single document that works in all Internet devices and has a complex page layout in the 5+ browser set.

Problems with CSS layout

To fully understand CSS layout, you must not only be aware of it's benefits, but you must also be ready for some problems it presents. Most of these problems stem out from CSS's immaturity. Yes, the recommendation itself has been around for a while, but CSS layouts are only recently possible, and even more recently taken seriously by developers. Expect this immaturity to result in problems, such as:

So then, with all these problems, why use CSS? You'll have to make this decision yourself. Many people I know use CSS layouts for personal web projects, but still resort to tables when working on commercial sites. The time is coming when CSS will be suitable and preferred for all site layout, but many reasonably argue that the time has not yet arrived.

I use CSS because I believe in the fundamental principle of the separation of structure and presentation, and I am willing to put up with the problems CSS presents as I look forward to a time when CSS is as robust and simple to implement as tables are today. That time will come, and the web will be a better place for it.

Example: Re-coding the Internet Developer Home Page

To make things interesting, I took the Apple Internet Developer home page, which makes extensive use of tables, and I re-coded it using CSS for all layout. I've made a copy of the home page as it existed when I started this article, and I've turned on all table borders so you can see what we're working with: the Apple Internet Developer home page with table borders.

Now, If you're using IE5+ or NS6+, you should see virtually the same layout here: The Apple Internet Developer home page with only CSS. Opera 5+ users should see a very similar page, but with a few display quirks.

Now that I've shown you the fruits of my labor, take a closer look at the techniques used. Start at the top of the page, where there is a 600px wide graphical image bar centered in the browser window.

Image of ADC Top Nav Bar

using a table this is accomplished something like so:

<center>
  <table width="600"> ... </table>
</center>

The complete markup can be seen in the file top_nav_table.html.

Using CSS I got the same results by using a DIV instead of a table element, and setting the DIV's margin attribute to the value auto, like so:

<style type="text/css">
#top_nav {
  margin:auto;
  width:600px;
  } 
</style>

<div id="top_nav"> ... </div>

Sadly, this is where I ran into the first browser discrepancy. IE5.x does not properly implement the auto keyword, and so the DIV will not be centered in that browser. Happily, there is a simple workaround: by setting the text-align attribute of the DIV's parent element (in this case, the body element), to center, I ensure that IE5.x will center the DIV:

body {
  text-align:center;
  }

Those familiar with CSS may know that this is a mis-implementation of the text-align attribute. But in this case, it does provide a useful workaround.

Now take a look at all the style rules used in creating the top navigation bar using CSS (the complete markup can be seen in the file top_nav_css.html):

#top_nav {
  margin:auto;
  width:600px;
  height:32px;
  text-align:left;
  }

I've already talked about the margin and width attributes of the #top_nav rule, the height attribute is self-explanatory. The text-align:left attribute is necessary because the text-align:center on the body style rule will be inherited if this is not given. Actually, you would think both of these latter attributes would be unnecessary for this DIV, but removing them causes IE5 Mac to incorrectly display the DIV. Better to leave them in.

#top_nav a {
  display:block;
  float:left;
  }

This rule causes all the links in the top_nav DIV to be treated as block elements instead of using the default inline display value for anchor elements. You can then float each of these elements left, which is essentially the same as using the align attribute of a block element or an IMG. This would normally not be necessary, but notice that one part of the navigation bar is not an image at all: it is a form. Once I floated all the images left, which makes them align horizontally next to one another, I inserted a form in this series of images (a form with the id "MidSearch"), and applied the following style to it:

#MidSearch {
  float:left;
  margin:0px;
  background-image:url("img/navbg.gif");
  height:32px;
  }

Notice that I used a background image for the form, which places an image behind the form's text element and makes it blend in nicely with the images in the navigation bar. The following rule defines the display of the form's input element:

#MidSearch input {
  width:100px;
  height:18px;
  margin-top:4px;
  }

And there you have it. I've replaced a double nested table structure with a simple series of links wrapped around images and a simple form. All the layout information has been moved into the style sheet. If you haven't already done it, now is the time to compare the markup code: using tables vs. using CSS

Next, take a look at the top middle section of the page, starting below the top navigation bar and ending at the first gray horizontal bar:

Image of Internet Developer Nav Bar

I won't spend any time on this code for this section because it uses the same CSS techniques as the top navigation bar.

Below the first gray bar, you'll find the meat of the page, a two-column section with numerous links to various onsite articles and offsite resources.

Image of middle of Internet Developer page

The basic two-column layout for this section is created with three DIVs named main, left, and right. I've given some DIVs names that communicate their layout locations, which should help you correlate the HTML elements with the style rules that govern them. In real life, I would recommend giving your DIVs names that communicate their contents, not their layout locations. The markup looks something like this:

<div id="main">
  <div id="left">
    <img src="img/intro_descript.gif"
    width="200" height="99" alt="..." name="Desc" id="Desc">
    <div class="bodytitle">Related Links</div> 
    ...
    </div>
  </div>
  
  <div id="right">
    <span class="bodytitle">Web Development & Mac OS X</span>
    ...
  </div>
</div>

As you can see, the DIV main contains two child DIVs, left and right. The relevant styles look something like this:

#main {
  margin:auto;
  width:600px;
  text-align:left;
  }
  
#left {
  float:left;
  width:240px;
  background-image:url("img/bar_back.jpg");
  background-repeat:repeat-y;
  }
  
#right {
  width:330px;
  margin-left:260px;
  }

The style for the DIV main is much like the style for the DIVs of the preceding section, all of which define a 600px wide DIV centered in the middle of the page. main's child DIVs are the two columns; left has a float:right attribute, which allows right to abut it on the right side, and both DIVs have widths set so that they fit within the DIV main. right has a margin-left attribute set at 260px to ensure that it occupies the correct space within main.

To get the vertical gray bar along the left side of the page, a background image is specified for the DIV left, and the background-repeat attribute is set to repeat-y, which causes the background image to tile only vertically. And there you go.

In each of the two columns, there are various CSS layout techniques used to create different lists, boxes, and paragraphs. I'll describe a few of them. For instance, in the left div all children DIVs are given a left-margin of 25 with this rule:

#left div {
  margin-left:25px;
  }

Since all content in the left div is contained within children DIVs, all content is given a 25px left margin, allowing space for the vertical gray bar.

Image of Internet Developer links box

To create the box of links (pictured above), I nested a paragraph inside a DIV, like so:

<div id="links_div">
  <p id="links_para" class="portallink">
    <a href="...">CSS Compatibility Chart</a><br>
    <a href="...">Apple's Business Case</a><br>
    <a href="...">WebObjects</a><br>
    <a href="...">QuickTime</a><br>
    <a href="...">QuickTime Tutorials</a><br>
    <a href="...">AirPort</a><br>
    <a href="...">Apple.com</a><br>
    <a href="...">The Apple Store</a>
  </p>
</div>

I applied the following styles to those elements:

#left #links_div {
  background-image:url("img/chains.gif");
  width:201px;
  height:191px;
  margin-bottom:15px;
  margin-top:-5px;
  }
  
#left #links_div #links_para {
  padding:46px 10px 0px 40px;
  margin:0px;
  }

The border and chains are all part of the links_div background image, and the DIV's height and width are set to the exact dimension of that background image. Notice that the top-margin of the DIV has a negative value, which brings the "Related Links" and the links DIV a little closer together. The nested paragraph element is then precisely placed within the DIV by way of its border and margin attribute values.

CSS Pocket Reference

Related Reading

CSS Pocket Reference
By Eric A. Meyer

You'll notice my selectors, the part of a style rule that defines the page elements to which the rule applies, are failry verbose. Instead of the simple selector "#links_div", I use "#left #links_div". This labeling specifies that "#links_div" is a descendent, or is contained within, "#left". This practice is helpful when visually scanning my style sheets since it gives me more information about the part of my page that the style rules apply to.

The last section of the page I'll describe is the box at the top of the right-hand column:

Image of Mac OS X Web Developmet box

Here is the relevant markup:

<div id="top_box">
  <a id="top_box_image" href="http://developer.apple.com/macosx">
    <img src="img/macosxbox.jpg" height="119"
    width="100" alt="Mac OS X Box" border="0">
  </a>
  <div id="list_div">
    <ul id="top_box_list">
      <li><a href="...">Introduction</a>
      <li><a href="...">Information Architecture</a>
      <li><a href="...">Security Introduction</a>
      <li><a href="...">Perl on Mac OS X</a>
      <li><a href="...">PHP on Mac OS X</a>
      <li><a href="...">Security: Mac OS X and UNIX</a>
      <li><a href="...">Using mod_ssl</a>
      <li><a href="...">Open Source Databases</a>
      <li><a href="...">Java and Tomcat, Part I</a>
    </ul>
  </div>
  <p class="bodytext">
    <span class="bodytitle">Java and Tomcat</span><br>
    With the introduction of the Unix-based Mac OS X, server-side Java...
  </p>
</div>

The gray border of the box is created with the border attribute of the DIV which contains the rest of the content, like so:

#right #top_box {
  border:1px solid gray;
  padding:0px 12px 12px 12px;
  }

I used the padding property of the DIV to give the DIV's contents the proper amount of surrounding white space, but notice that I did not assign a value to the width property of the DIV. In general, assigning a width and either padding or margins to any box element will produce different results in different browsers. This is because of a discrepancy in IE 5.x PC's implementation of the box model, which is the cause of most of the grief developers encounter when attempting their first CSS layouts.

This issue has been expounded upon in numerous other places on the web, and there are several solutions. In this example, I solved the problem by stating the padding but not declaring a width for the DIV. The default behavior of a DIV without a specified width is to simply fill the width of its parent element. Since I defined a width (but not padding or margins) for its parent DIV (the DIV with id "right"), I got identical results in all 5x browsers, even the broken IE5.x PC. This topic and other common CSS problems are discussed in more detail here.

Now, within the DIV we have an image sitting to the left of a bulleted list, followed by a bit of text which fills the width of the DIV. The layout of the text requires no additional CSS, but the image and list use the following style rules:.

#right #top_box #top_box_image {
  display:block;
  float:left;
  margin:40px 40px 0px 0px;
  }

#right #top_box #list_div {
  margin-bottom:10px;
  float:left;
  }

#right #top_box #list_div #top_box_list {
  margin:0px;
  padding:0px;
  padding-top:10px;
  }

The CSS above uses the float attribute to create the two-column effect in same manner as was used for the two main content columns of the page. Before using the float attribute, however, I had to make the img a block element using the display attribute like so: display:block. I also wrapped the ul element in a DIV to work around discrepancies in how various browsers handle floating the ul element. Then I set float:left on BOTH the img and the DIV element (technically I should only have to do that on the img, but we do it to both to make sure all of our target browsers align our img and DIV next to each other as desired).

To get the nice aqua bullets for the list, I used the list-style-image attribute, like so:

#right #top_box #list_div #top_box_list li {
  list-style-image:url("img/aquadot.jpg");
  }

This replaces the browser's default bullets with an image of our choosing. IE/PC may not display the bullets properly, however, because of its idiosyncratic way of handling margins around the ul element; but the content does not suffer, and the layout suffers only minimally from the lack of bullet images.

Conclusion and Resources

You're now ready to start using CSS instead of tables for your web page layout needs. As you experiment with the techniques discussed in this article, you'll find that creating CSS layouts is a learned skill, and that with practice it can become an easy task. If you're looking for more resources to guide you into the CSS sunset, try these:

Eric Costello is a contract developer for hire, working out of his mysterious company Schwa. He maintains a personal site at glish.com, where he links to articles on Web standards, DHTML, CSS, XML, and other topics of interest to web developers.


Return to the JavaScript and CSS DevCenter.

Copyright © 2009 O'Reilly Media, Inc.