Welcome!

Adobe Flex Authors: Liz McMillan, RealWire News Distribution, Maureen O'Gara, Yakov Fain, Keith Swenson

Related Topics: Adobe Flex

Adobe Flex: Article

Avoiding CSS Pitfalls

Avoiding CSS Pitfalls

CSS has been around for years, but many Web designers still do not think it is ready to be used extensively because of the host of browser rendering inconsistencies that exist. However, by knowing a few CSS hacks and tricks, you can learn how to write code that is cross-browser compatible and allows you to fully separate your content from its presentation.

This article assumes you know what CSS is and have some idea how to use it to style Web pages, but is aimed at Web designers who have not yet taken the leap of using it as their primary layout method due to frustrations with browser inconsistencies. It also assumes you know the benefits of CSS-based layout and are eager to make use of it. Many of the hacks I'll go over depend on your document using a doctype that will put it into browsers' "standards rendering mode," so be sure you are using a proper doctype as well. (Dreamweaver MX 2004 uses such doctypes on its HTML and XHTML templates, so you should be good to go. If in doubt, read my article in the last issue of MXDJ or check out the CSS Wiki - http://css-discuss.incutio.com/?page=DoctypeSwitch - for information about doctype switching.)

Squashing Bugs
Even the most modern browsers have bugs in the way they render CSS styling. Worse still, many people continue to use older, buggier browsers, so achieving a consistent cross-browser design can be frustrating even when you adhere to the best CSS practices. Luckily, you can use the browsers' own bugs against them to fix many of these problems. CSS hacks are code tricks that take advantage of various browsers' bugs or shortcomings to hide certain rules from or feed different rules to browsers that need special treatment. I'll outline some of the most irksome bugs and how to work around them with CSS hacks.

A word of caution: keep in mind that not everyone supports the use of hacks. Some developers argue that the browser failings we are forced to work around will never be fixed because our hacks create the appearance that everything is working correctly. Hacks can also be dangerous because they rely on browser bugs that may be corrected in future browser releases, rendering your hacks useless or perhaps introducing new problems of their own. However, by keeping our hacks in the CSS instead of the (X)HTML markup, we should be able to change or remove broken hacks in one central file and update an entire site if it becomes necessary in the future. If we're going to create the best experience for our users, hacks are a necessary tool in commercial Web site design - as long as they are used sparingly and thoughtfully with periodic testing to make sure they are still functioning correctly.

Box Model
One of the most common problems with CSS-based layouts is getting all of your columns and boxes to line up and fit together due to Internet Explorer's (IE) faulty implementation of the box model. According to the World Wide Web Consortium (W3C), the assigned width or height of an element refers to its content area only. The padding and borders are then added to this value to arrive at the total area of the element that you see on screen. This means that if you set a div's width to 200 pixels with 20 pixels padding and 2 pixels borders, the total area it will take up is 200px + 20px (left padding) + 20px (right padding) + 2px (left border) + 2px (right border) = 244px. Unfortunately, all CSS-enabled versions of Windows IE before IE6 (in standards mode) use a different box model that counts the padding and borders of a box as part of its assigned width. This means that your div that should take up 244px on the screen only takes up 200px in WinIE5.x because it subtracts the padding and border values from the content area of the box. You're left with a box that looks completely different and has much less room for content in WinIE5.x than it does in IE6 and other current browsers (see Image I for a comparison).

In many cases, the box model problem can be worked around without any hacks. For instance, instead of applying padding to a box to get its content to move away from the edges, you can apply the padding to the content inside the box:


div {
	width: 200px;
}
div p {
	padding: 20px;
}

But we also wanted 2px borders on our box. You could choose to ignore the four pixel difference they would introduce in WinIE5.x - some layouts don't rely on such precision. Or, you could nest another div inside the first one and apply the borders to the nested div, but this mucks up your markup. What you really want is a way to give WinIE5.x a bigger value than other browsers use, a value that is equal to the total box width, including padding and borders, so that it can happily subtract them without making the box too small. Tantek Çelik developed a box model hack to do just that, and since then several simplified versions have followed. My favorite is the Modified Simplified Box Model Hack (MSBMH) developed by Edwardson Tan:


div {
	width: 200px;
	padding: 20px;
	border: 2px solid red;
}
* html div {
	width: 224px;
	w\idth: 200px;
}

The first rule gives the correct width, padding, and border values to browsers that correctly implement the box model. The second rule is seen only by IE through the use of the Star HTML Hack. "* html div" tells the browser, "apply this rule to any div element that is a descendant of an HTML element that is a descendant of any element" (the asterisk is the universal selector and means "any element"). This rule is nonsense: HTML is not a descendant of any element - it is the root element - so this rule should not apply to anything and compliant browsers should (and do) skip over it. However, IE seems to ignore the universal selector when it precedes "html," so this rule gets applied by IE and IE only.

The first width value in the second rule is read by all IE versions, giving WinIE5.x the total on-screen box width that it uses in its box model. Unfortunately, IE6 for Windows and IE5 for the Mac also see this 244px value, but they don't have a box model problem and need the correct 200px value. The second value with the backslash character sets things back correctly for these browsers. Since WinIE5.x cannot understand a rule with a backslash in it, it keeps the larger 244px value, and all other browsers including IE6 and MacIE5 get the correct 200px value, making things look the same cross-browser.

Keep in mind that IE6 needs to be in standards-rendering mode for this to work correctly. Otherwise it will use WinIE5.x's incorrect box model, and since the hack only targets WinIE5.x, IE6 will not get fixed.

The Star HTML component of the MSBMH comes in handy quite often: IE has a host of bugs, and the Star HTML Hack allows you to feed it different values when it's misbehaving.

Three Pixel Gaps
You'll want to have the Star HTML Hack at your disposal when you're using floats. WinIE, including IE6, inserts an extra three pixels of space between the edge of a float and the edge of the following content. If the following content has a width or height assigned, the three pixel gap shows up as a space between the boxes (I'll call this the Three Pixel Float Gap; see Image II). If it does not have a width or height, the boxes seem to sit next to each other, but the content within the following box is pushed over by three pixels for as long as the float extends beside it, shifting back over to the edge where it belongs after the end of the float (Big John [John Gallant] of positioniseverything.net calls this the Three Pixel Text Jog; see Image III, with closeup). Although both are seemingly minor, the Text Jog is pretty unsightly, and the Float Gap can throw everything off in precise layouts, making it impossible to create a two-column layout with the float and following content sitting flush against each other.

To get rid of the Three Pixel Float Gap, you need to assign the float a negative margin to pull the following box back against it. Let's say we're using the following code to create two columns that sit flush against each other:


#sidebar {
	float: left;
	width: 300px;
	}
#content {
	width: 400px;
	margin-left: 300px;
	}

We've assigned #content a margin-left exactly the same size as #sidebar's width so that they will sit flush against each other, but in IE the Float Gap shows up, preventing them from touching. Add the following hack below your regular rules to get rid of the gap:


/* hide from MacIE \*/
* html #sidebar {
	margin-right: -3px;
	}
* html #content {
	margin-left: 0;
	}
/* end hide */

We've used the Star HTML Hack above to give values to just IE, then enclosed it in a Mac Backslash Hack that hides the values from MacIE due to the backslash in the comment preceding the rule (a MacIE bug). Thus, only WinIE (the problem browser group) sees the hacked values.

What if you do want some space between your boxes, you just want to kill the extra three pixels WinIE adds? Just give WinIE a margin-right that is three pixels smaller than it should be. Code I is an example that leaves 10 pixels between the two boxes.

The examples in Code I have a width assigned to #content, but when you leave off this width, the Three Pixel Text Jog shows up. How can you get rid of the Text Jog if you have a fluid layout and can't assign #content a width? Holly Bergevin discovered that a height works just as well, and since IE will (incorrectly) expand a box's height to accommodate its content, you can assign a very small height and still keep your fluidity! Code II shows the Holly Hack to kill both the Float Gap and Text Jog.

The Holly Hack can be used in a variety of situations when IE is misbehaving. If IE is doing something strange, check to see if the offending box has a dimension assigned. If not, try giving it a height of 1%, and often this will fix things.

Peek-a-Boo Bug
Another IE problem you may run into while using floats occurs when you have a container with a float inside and content alongside the float. In IE6, the content following the float may not appear at all, or appear only partially, until you scroll down or switch to another window and switch back. This bug was aptly named the Peek-a-boo Bug by Big John, who lists several workarounds for it on his site. The easiest one to apply was discovered by Matthew Somerville. If you give the container holding the float a line-height of any value, it cures the Peek-a-boo. If assigning the line-height would cause problems with child elements of different font sizes, you can use the Holly Hack on the container for an alternate quick fix.

Doubled Float-Margin Bug
Since we're on the subject of floats in containers, we'd better cover the Doubled Float-Margin Bug, again a WinIE-only problem. If you apply a margin to a float to move it away from the edge of its container, WinIE doubles that margin. So the following code produces a 10 pixel margin on the left of the float in everything but WinIE, which shows a 20 pixel margin on the left:

The CSS:


#float {
	float: left;
	width: 100px;
	margin: 10px;
}

The (X)HTML:


<div id="container">
<div id="float">float</div>
</div>

Your reaction to this is probably, "I know! Use the Star HTML Hack to feed IE a halved value!". This would work, but luckily can be fixed even more easily and cleanly. Steve Clason discovered that if you apply "display: inline" to the float it gets rid of the doubled margin. Since "display: inline" on floats is ignored by other browsers (floats are block elements by definition) you don't even have to hide this rule from them via the Star HTML Hack! Easy.

Flash of Unstyled Content
Another IE bug with an equally simple solution is the Flash of Unstyled Content, or FOUC, named by Rob Chandanais of bluerobot.com. A page afflicted with this bug will show a quick flash of the page without any styles before the CSS takes hold. This only occurs in WinIE on the first page view, before the CSS is cached, and it happens when you are using @import to call your style sheet. Adding just one link or script element to your document will fix the problem. The addition of a link element is an easy and natural fix because most pages can benefit from an alternate style sheet, such as a print style sheet:


<head>
<title>My Page</title>
<style type="text/css">@import "screenstyle.css";</style>
<link rel="stylesheet" type="text/css" media="print" href="printstyle.css">
</head>

If you decide to add a script to fix the problem instead, keep in mind that the script doesn't have to be in the head to prevent the FOUC. Placing it inside the body but before all visual content works just as well.

Text Inheritance
WinIE5.5 has a problem inheriting the correct text size into tables, but a simple rule gets it back on track:


table {
	font-size: 1em;
	}

This makes sure the font size of the table displays at the same size as the surrounding text, which it ought to do by default, so the rule doesn't need to be hidden from other browsers.

Netscape Navigator (NN) 4.x also has major - and less predictable - text inheritance problems. Although you may want to present NN4.x with an unstyled version of your pages (using @import instead of <link> to call your style sheet), there may be times when you want to give it at least some basic text formatting. Since NN4.x often loses text properties at seemingly random spots in your page, it's best to explicitly set the text parameters on everything that could possible need them in NN's style sheet:


body, div, p, blockquote, ol, ul, dl, li, dt, dd, td {
	font-family: Arial, Helvetica, sans-serif; 
	font-size: 12px;
	}

Opera has its own text problem when you set font-size to 100%: it computes it to be one pixel smaller than it should be. Using 100.01% instead of 100% fixes this.

Horizontal Centering
To center your entire layout in the browser window, use the following CSS:


body {
	min-width: 700px;
			text-align: center;
	}
#wrapper {
	width: 700px;
	margin: 0 auto;
	text-align: left;
	}

Setting the left and right margins to auto centers #wrapper in the window because the margins are set to equal values. Since WinIE5.x does not recognize this technique, add "text-align: center" to the main div's container, in this case "body," to achieve the centering in that browser (this property won't work in other browsers because it's only supposed to center inline content, not blocks, but WinIE5.x ignores that little detail).

Set a min-width on the body equal to the width of the centered box to avoid problems in Gecko-based browsers. If you don't set this min-width, when you size your window below the size of the centered box, its left side will get cut off without the ability to scroll over to the left to see the cut-off content (see Image IV). This is because when the window is too small, the auto margins must get set to negative values, thus extending the box equally off both sides of its containing block (in this case, the body).

Containing Floats
Keep in mind while using floats to create columnar layouts that floats, not just absolutely positioned boxes, are removed from the flow of the document. This means that a parent box doesn't know the float is there and thus will not expand to hold the child float. This may seem illogical at first, but think about when you are not using floats for columns, but for images placed in paragraphs - the most traditional use of floats. In this case, you want all the text to flow around the image - not just the text of the first paragraph where the image is placed, but all subsequent paragraphs that encounter the float as well. If the float made its container, a paragraph, expand to hold it, the next paragraph wouldn't start until the image ended, leaving a potentially large gap (see Image V). So, floats naturally stick out of their parent elements instead.

But don't worry - there is a way to overcome this behavior when your layout calls for it! All you need is a block-level element, set to clear the float, placed within the container but after the floated element. This forces the container to expand down around the element beneath the float, enclosing the float in the process.

The CSS:


br.clear {
	clear:both;
	height:0;
	margin:0;
	font-size: 1px;
	line-height: 0;	
	}

The HTML:


<div id="container">
<div id="float">float</div>
<br class="clear">
</div>

Another easy way to contain a float is to float its parent: a floated parent automatically expands to hold children floats.

While we're on the subject of floats, one caution: make sure you assign your floats an explicit width, as required by the CSS 2.0 spec. Although most browsers are lax about this (so much so that the requirement's been dropped from the upcoming 2.1 spec), MacIE takes floats that don't have a width and expands them to fill up their entire container. This is a problem if you are using floated <li>s to create a horizontal nav bar, for instance. In this particular case, you can float the <a>s inside the <li>s to get them to sit beside each other in MacIE, but most of the time you have to bite the bullet and give all floats a width to keep MacIE from expanding floats to 100%.

Conclusion
I wish I could tell you that the bugs I've described are the only ones you will run into and that the hacks I've outlined will cure all your CSS ills, but I can't - I'd be lying. Unfortunately, there are quite a few other bugs I haven't touched on, and new bugs will certainly continue to pop up as browsers evolve and Web developers push the limits of CSS and (X)HTML. But, I can tell you that you're now armed against the most common CSS bugs and ready to create your first all CSS, cross-browser layout without fear.

More Stories By Zoe Gillenwater

Zoe Gillenwater is a Web designer at the University of North Carolina at Chapel Hill with a passion for standards-based development. She also keeps busy with graphic design and multimedia projects. Zoe is an active participant in the css-discuss community and is one of those who believes CSS-based layout is ready for primetime.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.