I've just finished the design of my new personal business site, the template being based on Matthew Levine's "Holy Grail"... (To quote Matthew: "I’m sorry. Really. I didn’t name it.")
This post is a list of warnings/potential pit-falls, along with some nifty solutions, for anyone out there thinking of using the website template found on http://alistapart.com/articles/holygrail, outlining some of the problems that I (and almost everyone else) faced when implementing the layout.
To see the 'fixed' template in action, see: www.mathesonbayley.com
Despite all the obstacles the original template presented, my conclusion will not be to avoid using it: on the contrary, it works like a dream (if you can get it to work!). Upon popular demand, after numerous emails wondering how I circumvented certain bugs, I've collated all the 'fixes' lying around over the net + scribbled down a few tips of my own in the hope of 'easing the passage' for other CSS newbies.
This can therefore be considered an 'update' to the original article. But first off, to summarise the pros and cons: the template admirably achieves 4 of the 5 criteria outlined by Matthew Levine on the A List Apart URL mentioned above:
- 1) have a fluid center with fixed width sidebars,
- 2) allow the center column to appear first in the source,
- 3) allow any column to be the tallest,
- 4) X
- 5) require very simple CSS, with minimal patches.
Number 4, which stated: "require only a single extra div of mark-up", is true in theory, but real-life scenarii often involves complex content mark-up interacting with buggy browser software, resulting in the need for several other non-semantic divs in order to achieve absolute cross-browser compatibility.
Compatibility
The new amended code below will get the template working perfectly with the following platforms and browsers:
PC (+ Mac & Linux where available):
IE6 / 7,
Mozilla 1.7.12,
Firefox 1.5.0 & 2.0,
Netscape 7.2 + all other Gecko based browsers, &
Opera 9.0
Mac:
Safari 1.3 & 2.0
Linux:
Konqueror 3.4.0
I've thoroughly tested all of the above, so, in short, it'll work with all modern browsers.
Before getting to the code, note the following:
- 1) What follows is for the 'padded version', as that seems to be the most popular.
- 2) I've added explanatory comments to the code, in which 'ff' stands for Firefox (/ gecko-based browsers), & 'IE' means Internet Explorer
- 3) A JavaScript fix for IE7 results in the IE7 Zoom function not working (and, of course, JavaScript needs to be enabled for the layout to work in IE7)
- 4) One short-coming of the fixed template is that most browsers (although not IE) will screw up the layout when clicking on 'within page' / 'jump' anchor links that direct to anywhere other than the top of a page. That is, links in the form " <a href="page.html#heading"> " etc. Accessibility purists, however, tend to agree that such links are a bad idea anyway, so this isn't so bad. For an in depth, and very persuasive, discussion as to why such links should be avoided, see this superb article on: Why not to use Jump Links, by Jakob Nielsen.
- 5) IE5.5 or earlier: forget it! This template is not for you if this increasingly negligible percentage of the market is something you need to cater for – it simply falls apart, (and let's not even talk about venturing into the ungodly realms of early Netscape...!) :-)
- 6) Everything in the code below (including IE conditional comments) will validate at http://validator.w3.org/ as XHTML 1.0 Transitional, and no deprecated tags have been used.
- 7) If you're confused by the unusual syntax for IE conditional comments in the code below, see the notes after the code for an explanation.
- 8) Many of the ff container divs in the code below are only necessary if you have floated elements in the center column as well as more than one image over which you wish text to appear (i.e. 'pseudo-background images'). Specific as that may sound, it's certainly a common desire, and a detailed explanation is given after the code. If, however, your requirements fall short of such nugacities, then all the ff container divs can be safely ignored.
- 9) Every time a '???' appears in the code below, simply fill in the number of pixels you want according to your design. All padding values can safely be played around with too, as long as you take the figures into account when calculating the right and left column 'full-widths' (see Matthew Levine's original article for clarification on what 'full-width' means if you're confused).
Apologies, by the way, for the horizontal scrolling in the code: I had to use the 'pre' tag...
The html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Insert your title here</title>
<meta name="description" content="Insert your content description here" />
<meta name="keywords" content="Insert your keywords here" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="index,follow" />
<meta http-equiv="Author" content="Matheson Bayley" />
<link rel="stylesheet" type="text/css" href="the-name-of-your-style-sheet-file.css" />
<style type="text/css"></style>
</head>
<body>
<div id="header">
<div id="headerprop"></div>
<a style="position: absolute; top: 0px; left: 0px; z-index: -1; line-height: 1px;" name="top"> </a><br/><!-- ff anchor destination for 'back to top' buttons (if you have any!). IE doesn't require this, but the button will do nothing in ff without it -->
</div> <!-- Close header div -->
<div id="container">
<div id="centercolumn" class="column"> <!-- CENTER COLUMN -->
<div style="position: relative; top: 0px; left: 0px; z-index: 2;"> <!-- TEXT WRAPPER ff 'negative z-index fix' to make background images appear. See notes after code for explanation. -->
<div class="prop"></div> <!-- IE MIN-HEIGHT HACK PART 1 -->
<!-- Insert your main content here. I use the following php code for the text inclusion, which I offer in case anyone's interested, but this can be safely ignored if you're only interested in the template. -->
<?php
echo "<h1 class=\"glow\">$page</h1><h1>$page</h1>"; // print title with shading
if (isset($subpage)) {
echo "<table style=\"position: relative; left: 70px; bottom: 30px;\"><tr><td><img src=\"images/subarrow.jpg\" alt=\"\" title=\"\"/></td><td><h2 class=\"child\">$subpage</h2></td></tr></table>";} // print subarrow + subpage title if applicable
if (isset($non_latin)) {
echo "<p class=\"dimindent\">This page contains $non_latin characters. If they appear as gobbledygook, see <a href=\"help.php\">technical help</a></p>";} // print font warning if applicable
$include_name = basename($_SERVER['PHP_SELF']); // set the name of the data file to include as the same as the root file
if ($page == "Home" && !isset($subpage)) {include ("text/home.php");}
else {include ("text/$include_name");} //prints main text data
if (!isset($short)) {
echo "<br/><a href=\"#top\">[Back to top]</a>";}
?>
<!-- End of php. Template continues from here. -->
<div class="clear"></div> <!-- IE MIN-HEIGHT HACK PART 2 -->
</div> <!-- END text-wrapper 'ff negative z-index fix' -->
<!-- The next section deals with adding multiple background images. It can be ignored if you don't need them. See notes after code for explanation -->
<!-- conditional comments for IE "z-index reset of floats within relatively positioned divs" bug. See notes after code for explanation -->
<!--[if IE]>
<img style="position: absolute; z-index: -1; left: 0px; top: 0px;" src="images/center-column-top-left.jpg" alt=""/>
<img style="position: absolute; z-index: -1; right: 0px; top: 0px;" src="images/center-column-top-right.jpg" alt="" title=""/>
<![endif]-->
<!--[if !IE]> <!-->
<img style="position: absolute; z-index: 0; left: 0px; top: 0px;" src="images/center-column-top-left.jpg" alt="" title=""/>
<img style="position: absolute; z-index: 0; right: 0px; top: 0px;" src="images/center-column-top-right.jpg" alt="" title=""/>
<!--<![endif]-->
<!-- END conditional comments for 'IE z-index bug' -->
<!-- End of 'multiple background images' section. -->
</div> <!-- END CENTER COLUMN -->
<div id="leftcolumn" class="column"> <!-- LEFT COLUMN -->
<div style="position: relative; top: 0px; left: 0px; z-index: 2;"> <!-- TEXT WRAPPER ff neg z-index fix to make background images appear. See notes after code for explanation. -->
<!-- insert left column text here -->
</div> <!-- END text-wrapper ff neg z-index fix -->
<img id="left-column-graphic" src="images/left-column-graphic.jpg" alt="" title=""/> <!-- has z-index of 1 -->
</div> <!-- END LEFT COLUMN -->
<div id="rightcolumn" class="column"> <!-- RIGHT COLUMN -->
<!-- insert right column text here -->
<!-- The following conditional comments are only necessary if you have a graphic in the right column. IE7 doesn't display any of the three columns at all without a container div for the right column graphic! See notes after code for more detail. -->
<!--[if !IE 7]> <!-->
<img style="position: absolute; z-index: 0; right: 0px; top: 0px;" src="images/right-column-graphic.jpg" alt="" title=""/>
<!--<![endif]-->
<!--[if IE 7]>
<div>
<img style="position: absolute; z-index: 0; right: 0px; top: 0px;" src="images/right-column-graphic.jpg" alt="" title=""/>
</div>
<![endif]-->
<!-- template continues from here if you didn't have an image in right column -->
</div> <!-- END RIGHT COLUMN -->
</div> <!-- closes container div -->
<div id="footer-wrapper"> <!-- IE6 fix for stopping the column backgrounds from spilling out over the footer if the page is not as tall as the viewport -->
<div id="footer">
<div style="position: relative; z-index: 2; top: 0px; left: 0px;"> <!-- 'ff z-index fix' div -->
<!-- insert footer text here -->
</div> <!-- close ff z-index fix div -->
<img style="position: absolute; z-index: 0; right: 0px; top: 0px;" src="images/footer-graphic.jpg" alt=""/>
</div> <!-- close footer div -->
</div> <!-- close IE6 footer wrapper div -->
</body>
</html>
The CSS:
body {
font-family: arial, times, helvetica, sans-serif;
min-width: ???px; /* 2x (LC fullwidth + CC padding) + RC fullwidth */
background-color: #ffffff;
margin: 0px;
padding: 0px;
width: 100%; /* FF doesn't always fill the screen without this */
}
#header {
z-index: 5; position: absolute; left: 0px; top: 0px; width: 100%;
background-color: #ffffff; background-image: url('images/header-repeat.jpg'); background-repeat: repeat-x;
}
#headerprop {
height: ???px; /* add height of header */
float: right;
width: 1px;
}
#container {
position: relative; /* fix to 'IE7 10000px padding-bottom spill over footer' problem */
overflow: hidden;
padding-left: ???px; /* LC fullwidth */
padding-right: ???px; /* RC fullwidth + CC padding */
}
#container .column {
position: relative;
float: left;
padding-bottom: 20010px; /* enormous value + padding-bottom, assuming padding-bottom if 10px (this is how we get the equal hieght left and right columns) */
margin-bottom: -20000px; /* enormous value */
}
#centercolumn {
min-height: ???px; /* make it the same as the IE min-height hack below */
padding: 10px 20px; /* CC padding */
width: 100%;
z-index: 4;
margin-top: ???px; /* header graphic height, or height you want the header to be */
background-color: #ffffff;
background-image: url('images/center-column-repeat.jpg'); background-repeat: repeat-x; background-position: ???px ???px;
overflow: hidden; /* stops the left column from jumping about upon resizing of window */
}
/* IE min-height hack */
.prop {
height: ???px; /* 900 pixels or so, if optimally desiging for high resolution screen setting. Adjust to suit needs. */
float: right;
width: 1px;
}
/* IE min-height hack continued */
.clear {
clear: both;
height: 1px;
overflow: hidden;
}
#leftcolumn {
width: ???px; /* LC width (ie fullwidth minus padding) */
padding: 0px 10px 0px 10px; /* LC padding (top, right, bottom, left) */
left: ???px; /* RC fullwidth */
margin-left: -100%;
margin-top: 0px;
z-index: 2;
top: ???px; /* header graphic height */
background-color: #ffaaaa; border-right: 1px solid #000000;
background-image: url('images/left-column-right-edge-repeat.jpg'); background-repeat: repeat-y; background-position: ???px ???px;
}
/* Original Holy Grail IE6 hack to stop the negative margin pulling the left column too far to the left, incorporating the 'left column disappearing hack' + the IE7 javascript fix */
#container > #leftcolumn {
left: -???px; /* Negative of (LC fullwidth + CC padding) = width for all browers other than IE7 */
margin-left: expression(
document.all.centercolumn.offsetWidth * -1 +
parseFloat(document.all.centercolumn.currentStyle.paddingLeft) +
parseFloat(document.all.leftcolumn.currentStyle.paddingLeft) +
parseFloat(document.all.leftcolumn.currentStyle.paddingRight)
); /* Fix for IE7 */
}
/* Note that both 'centercolumn' AND 'leftcolumn' are written twice each in the javascript above. If the names you've given to your column div id's are different, then adjust the javascript to match your given id names. */
#rightcolumn {
width: ???px; /* RC width (ie fullwidth minus padding) */
padding: ???px ???px ???px ???px; /* RC padding (top, right, bottom, left) */
margin-right: -???px; /* This value is supposed to be 'Negative of RC fullwidth + CC padding', but the right column either doesn't display or moves to within the center column in Opera, Safari, and Konqueror if set according to Matthew Levine. If we call the original Matthew value (i.e., Negative of RC fullwidth + CC padding) 'x', then we need to use -(x - a bit more) to make it work. E.g. if x was -240, then we'll use -280. The precise value requires a little experimentation. Having said all of that, the original x will probably still work if you don't fiddle with Matthew's original padding values! */
z-index: 3;
top: ???px; /* header graphic height */
background-color: #ffffff; border-right: 1px solid #000000; border-bottom: solid #000000; border-left: 1px solid #cccccc;
background-image: url('images/right-column.jpg'); background-repeat: no-repeat; background-position: ???px ???px;
}
#footer {
clear: both;
background-color: #777777; background-image: url('images/footer-background.jpg'); background-repeat: repeat-x;
position: relative; z-index: 10;
border-bottom: 2px solid #000000; border-top: 1px solid #000000;
color: #ffdddd;
padding: ???px; /* choose a value - it won't mess up anything else! */
}
/* IE6 Fix for spill over footer */
* html body {
overflow: hidden;
}
* html #footer-wrapper {
float: left;
position: relative;
z-index: 10;
clear: both; /* IE fix for footer jumping up when right column is short */
width: 100%;
padding-bottom: 10010px;
margin-bottom: -10000px;
background: #ffffff; /* Same as body background */
}
Notes
Conditional comments for IE z-index bug
Yup! IE is at it again. The bug in question, this time, is that floated elements within relatively positioned divs have their inherited z-indexes 'reset'. This only poses a problem when trying to get such elements working in both a Gecko based browser and IE at the same time, but the solution is incorporated in the above code and explained in the next section on background images:
Multiple background images / firefox TEXT WRAPPER neg z-index fix
Quest:
You have a div which contains floats (say, images), and you want that div to have multiple backgrounds images. You also want this to work in all major modern browsers.
Difficulties:
1 Only Safari supports multiple backgrounds images as of 2007.
2 Opera and IE don't stick strictly to W3C guidelines regarding z-indexes, whereas Firefox DOES.
3 Moreover, IE has a separate z-index bug regarding floats.
Solution for IE + Opera:
Position your images and give them negative z-indexes, then put them in the div that contains your text or whatever else. Content is then visible over the img, even though it's not supposed to be. (Many argue that it's an advantageous use of negative z-indexes and should be standardised!) Floats work fine.
Unfortunately, the images will be invisible in FF.
Solution for FF:
Wrap all your text in a div WITHIN the main div (which we'll call "text-wrapper") and relatively position it (choose values of top: 0px; left: 0px; so it stays where you want it), then set the z-index of the text-wrapper to, say, 2. Positioned images are then placed in the main div but OUTSIDE the text-wrapper div, and given a z-index lower than the text-wrapper (say, 1).
Unfortunately, with IE, the positioned images will overlap the floats that are in the text-wrapper.
Cross-browser compatible solution:
Implement the above solution for FF (always a good idea to start with the most compliant browser!), then add conditional comments (see the html code from the template) so that only IE adds the negative z-index to the images. Now, FF is happy already, the floats appear correctly in IE, and the text-wrapper doesn't interfere with anything... almost...
Caveat: other relatively positioned elements within the text-wrapper will be knocked slightly out of place, but this is easily fixed by changing a few values.
IE Conditional Comments unorthodox syntax:
<!--<![endif]-->
...is simply the same as an ordinary "if not IE" conditional comment, but designed to pass validation.
Right column image wrapper
If including any image in the right column, IE7 presents a problem: the footer jumps up to the top of the screen covering the header, with nothing else being displayed beneath it. Why? - Anyone's guess! The image in the right column was absolutely positioned, but no fiddling with the exact pixel values of the positioning or the z-index made any difference - it's presence still resulted in IE7 screwing up the entire layout.
Other factors: there were no floats in the right column; the image was much smaller than the width of the column; the right column, in accordance with the layout of "A List Apart's Holy Grail" had a negative margin. I mention these things as I can imagine them being first ports of call for debugging, but every other browser was displaying it perfectly. (No known IE7 bugs seem to fit the bill as potential culprit)
Anyhow, the solution (as outlined in the code above), is simply a container div for the image in the right column:
<div>
<img....>
</div>
...with no other specifications, and as if by magic, IE7 is brought back into line! (Ah, so much for semantic accuracy!)
Conclusion
Well, it's all a bit of a fiddle, but it works! I hope this has been of use to someone. :-)
Once more, in order to see the 'fixed' template in action, see: www.mathesonbayley.com or www.indigoivories.com.