(update July 2022: This tutorial is obsolete. Now we tend to use flexbox or grids.)
My ultimate CSS layout
The goal is to have a 3-columns, liquid layout with the cleanest possible html code. By clean, I mean something like this:
<!doctytpe html>
<html>
<head><link rel="stylesheet" href="example.css" /></head>
<body>
<header> header header header header header </header>
<article> article article article article article </article>
<nav> nav nav nav nav nav </nav>
<aside> aside aside aside aside aside </aside>
<footer> footer footer footer footer footer </footer>
</body>
</html>
Header first, then the main content. Side columns come last. This is
better for text browsers, search engines, and Über Geeks who disable
CSS. Note that I use html5 tags avoid clutter. The actual examples use
<div>
tags.
Default layout
First, the colors. They help visualizing the layout. If you are using Firefox, you should also try the firebug extension. It helps visualizing even the margins of the boxes.
/* html5 reset */
article, nav, aside, header, footer { display:block;
padding:1em; }
/* colors */
html { background-color:#fff; }
body { background-color:#ddd; }
header, footer { background-color:#aaa; }
nav, aside { background-color:#8aa; }
article { background-color:#a8a; }
The result is here.
Fixed width layout
The total width is specified in ems. Don’t use pixel widths, they
don’t scale properly. The trick here is negative margins.
<article>
comes first and is 30ems wide (this count
it’s “width” plus the padding and left margin. <nav>
should fit on the right, but a negative margin of 30ems push it on the
left. This leaves enough room for <aside>
, which fits
on the right.
body { width:35em; margin:auto; }
article { float:left; margin-left:5em; width:23em; }
nav { float:left; margin-left:-30em; width: 3em; }
aside { float:left; margin-left:0; width: 3em; }
footer { clear:both; }
The result is here.
Liquid layout with percentages
The technique is exactly the same. Just replace all em mesures by percentages.
article, nav, aside, header, footer { padding:4% }
body { width:50%; margin:auto; }
article { float:left; margin-left:18%; width:56%; }
nav { float:left; margin-left:-82%; width:10%; }
aside { float:left; margin-left:0; width:10%; }
footer { clear:both; }
The result is here (you may see a rounding error resulting in an 1-off pixel). See what happens when you resize your browser.
Ultimate: liquid layout with fixed side columns
Percentages are not ideal. Side columns generally have few text, and
should vary with the font size only. We need
<article>
to be as wide as <body>
,
minus the width of the side columns. We can achieve this by not
specifying the width of <article>
. It will then
expand as much as it can, minus the specified margins.
body { width:50%; margin:auto; }
article { float:left; margin-left: 5em; margin-right:5em }
nav { float:left; margin-left:-35em; width: 3em; }
aside { float:left; margin-left: -5em; width: 3em; }
footer { clear:both; }
The result is here. Do we have found the Holy Grail?
Compromise
No. When <article>
doesn’t have enough content, it
narrows and wrecks havoc in the layout. The resulting catastrophy is here.
I found 2 ways to compensate. The first is to artificially add
content to <article>
, with some additionnal CSS:
article:after{ content:"m m m m m m m m m m m m m m m m m m m";}
The result is here. Don’t forget to change the font color, so it matches the background:
article:after{ color:#a8a; }
The result is here. The letters will disapear, but the gap will remain.
The second way is to pollute the html code a bit: wrap
<article>
in a <div id=wrap>
tag:
<!doctytpe html>
<html>
<head><link rel="stylesheet" href="example.css" /></head>
<body>
<header> header header header header header </header>
<div id=wrap>
<article> article article article article article </article>
</div>
<nav> nav nav nav nav nav </nav>
<aside> aside aside aside aside aside </aside>
<footer> footer footer footer footer footer </footer>
</body>
</html>
And the CSS:
body { width:50%; margin:auto; }
#wrap { float:left; width:100%; padding:0; margin:0; }
article { margin-left: 5em ; margin-right:5em }
nav { float:left; margin-left:-100%; width: 3em; }
aside { float:left; margin-left:-5em ; width: 3em; }
footer { clear:both; }
The result is here.
Note that it works well with little content. Because
<article>
is not a float any more, it will extend to
its maximum width no matter what.
How to choose
If you want absolutely no ugliness, you will have to choose a percentage based layout, or an em based one (pixel based sizes are evil and ugly, period). If you must have a liquid layout, you will have to choose where to put the ugliness: in the stylesheet, or in the html code. And don’t forget that even liquid layouts have limits. If you let it be too wide, long lines will be hard to read. If you want to support very narrow screens, you should write an alternate style sheet with only one column.