CSS Tutorial
CSS Filters & Hacks
There are a number of known bugs in certain browsers, and developers have devised clever hacks to work around them. Perhaps the most well-known is IE5's traditional (flawed?) box model (introduced above), and IE5/6's misapplication of the height property. Hacks come in two varieties. Some involve a clever use of comments, the universal element, and combinators within the style sheet; others involve importing a special style sheet that only some browsers can use, known as filtering. Some hacks validate and some do not. The Star-HTML hack (below) validates while several of the older hacks do not. I strongly urge you to avoid non-validating hacks.
Since the release of IE7, some IE-directed hacks need to be stripped out lest sites break that use those hacks. So the good news is that whether you know anything about these old hacks or not, you should happily avoid most, if not all, of them and write standards-compliant CSS. There is another reason to avoid hacking up the style sheet. At the time of this writing, some of the buggier browsers are not much being used. There are few who surf with IE5 on the Mac since most Mac users have adopted the standards-compliant Safari.
IE5 Box Model "Bug"
According to the W3C model the width of a block box is the sum total of the width of the content plus padding and borders. However, IE5 supports the traditional model and assumes that the declared width includes padding and border. This is also true for later versions of IE when in quirks mode. Let's look at a refresher example.
.box {width: 100px; padding: 10px; border: 2px;}
This box will take up 124 pixels in the W3C model if we calculate as follows:
left border + left padding + content + right padding + right border
2 + 10 + 100 + 10 + 2 = 124
However, IE5 calculates the base width of 100 pixels as being the total of the content including padding and borders. So IE5 will try to make the block box 100 pixels wide. I say "try" because an image or other object may force it to be wider. If you are supporting IE5.5 one workaround is to hack up the style sheet so that compliant browsers see the correct width of 100 pixels, and only IE5 sees 76 pixels.
.abc {width: 100px; _width: 100px; padding: 10px; border: 2px;}
The disadvantage of this method, the underscore hack, is that the CSS will not validate. I know that it has been argued that it should validate, but nevertheless it won't. The other method is to put IE5 specific styles in a single style sheet that would be called into an HTML document with the LINK element, and then import the style sheet that holds styles for compliant browsers. The import is achieved in a way that IE5 cannot process. Thus IE5 will only see the one style sheet, and compliant browsers will first process the IE5 style sheet, and then process the compliant style sheet, which will override the IE5-specific styles. This is called a filter hack. As we will see below, this hack is more on the right track.
CSS Filtering
The star-HTML hack takes advantage of an oddity in IE's DOM treatment. In the document tree, the HTML element stands at the head. But in IE, for both Win and Mac, there is an unknown root element outside of HTML. Though its name is unknown it can be accessed with the universal selector (*). In the example below all browsers process the first line, and only IE6 and prior versions process the second line. To target IE7/8 the hack is: "* + HTML", which is a combination of the universal selector and the sibling selector, which we will introduce below. Note: without a proper doctype IE7/8 will ignore the Star-sibling HTML hack, and process the star HTML hack, if declared.
p.someBox {width: 100px;} /* all browsers */
* HTML p.someBox {width: 124px;} /* IE6 override */
* + HTML p.someBox {width: 144px;} /* IE7/8 override */
I call these hacks, but in a way they are not hacks because they validate. There are a number of other ways to write declarations that only IE6 will see. Here are some commented examples.
.abc {width: 300px;} /* all CSS-aware browsers see this, but we are really targeting IE6 here */
*[class="abc"] {width: 310px;} /* only browsers that understand CSS2 can recognize an attribute selector,
so override here for those browsers like Firefox 2 */
*[class~="abc"] {width: 310px;} /* only CSS3-aware browsers (note the tilde) can process this,
so we target a newer version of Firefox, Chrome, Opera, and Safari */
.xyz {font-size: 1em;} /* all CSS-aware browsers see this, though you are likely targeting IE6 */
html > body .xyz {font-size: 1.1em;} /* all compliant browsers see this,
but IE6 does not recognize the child selector */}
Peter-Paul Koch recounts some of these old-style hacks for reminiscing purposes only. There he also mentions that IE supports the proprietary <comment> tag. I myself would not use it since it is invalid HTML, and because it is using a tag contrary to its intended semantics. If you must use this tag, use it for IE comments.
Conditional Comments
Way back with the advent of IE5, MS introduced HTML conditional statements. These statements are forward-compatible and will not break HTML validation tools since anything can go in comments, and because of that, other browsers pass over them in silence. These statements do not go into your CSS because you do not want hacked code there. Instead, you place conditional statements into the HTML that only IE understands. You use these statements to call a special style sheet to handle IE.
<!--CSS for all browsers -->
<link rel="stylesheet" type="text/css" href="otherBrowsers.css" />
<!--IE5 and later style sheet -->
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="iehacks.css" />
<![endif]-->
You could serve up a CSS file to handle IE5x's box model bug, and another to handle any peculiarity of IE6, such as the height property or the peekaboo bug.
<!--CSS for all browsers -->
<link rel="stylesheet" type="text/css" href="otherBrowsers.css" />
<!--IE5 style sheet -->
<!--[if IE 5]>
<link rel="stylesheet" type="text/css" href="ie5hacks.css" />
<![endif]-->
<!--IE6 style sheet -->
<!--[if IE 6]>
<link rel="stylesheet" type="text/css" href="ie6hacks.css" />
<![endif]-->
or
<!--CSS for all browsers -->
<link rel="stylesheet" type="text/css" href="otherBrowsers.css" />
<!--CSS for all IE browsers prior to version 7 -->
<!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" href="ie5and6hacks.css" />
<![endif]-->
Let's suppose that in your standards-compliant CSS file you have defined a content container as follows.
.box {float: left; clear: both; width: 200px; min-height: 100px; margin: 6px 10px 4px;
padding: 2px; border: 1px solid #000;}
In your IE6 style sheet you would include the following.
.box {display: inline; height: 100px;}
In your IE5 style sheet you would replicate what is in the IE6 style sheet, and change the "width" value to accommodate IE5's traditional box model.
.box {display: inline; width: 226px; height: 100px;}
In sum, the IE6 style sheet substitutes the buggy (but legal) height property to cover the min-height property that it does not support. The IE5 style sheet does the same, but also has the re-calculated width for the content, which, as we have said above, is the width of the content container plus padding, and borders. To this style sheet we also add "display: inline" for the IE Doubled Float-Margin Bug (above). Again, IE5 has the traditional box model, not IE6. But what about IE7? No problem. It will use the compliant style sheet, or if it turns out to be buggy, you can assign a style sheet to it.
Another way to handle this would be to use an IE-specific style sheet, and then another style sheet only for IE5. Due to inheritance you only need to supply property values that need to override the compliant ones. This practice should keep these extra CSS files short. Make sure to reference the IE-specific style sheets last so that they will have greater specificity.
MS has introduced a helpful syntax to help you target their browser versions, as shown below.
| Item | Syntax | Comment |
|---|---|---|
| Feature | IE | String. The only currently supported feature is the string "IE", corresponding to Internet Explorer. |
| Version | number | An integer or floating point numeral, corresponding to the version of the browser. |
| Operator | ! | The NOT operator. This is placed immediately in front of a value or expression or feature and reverses the Boolean value of the operand. |
| Comparison | Feature | Returns a Boolean value of true if the feature matches the browser type. |
| Comparison | Feature Version | Returns a Boolean value of true if the feature matches the browser type and the version number matches the browser version. |
| Comparison | lt | The less-than operator. Compares values or expressions. Returns a Boolean value of true if the first argument is less than the second argument. |
| Comparison | lte | The less-than or equal operator. Compares values or expressions. Returns a Boolean value of true if the first argument is less than or equal to the second argument. |
| Comparison | gt | The greater-than operator. Compares values or expressions. Returns a Boolean value of true if the first argument is greater than the second argument. |
| Comparison | gte | The greater-than or equal operator. Compares values or expressions. Returns a Boolean value of true if the first argument is greater than or equal to the second argument. |
A nice thing about this proprietary tag set is that you can serve up IE-specific JavaScript as well.
<!--IE6 JS -->
<!--[if IE 6]>
<script src="ie6-script.js" type="text/javascript"></script>
<![endif]-->
In fact, anything that you want only IE to see can be inside the conditional comments, including an embedded style sheet.
<!--IE6 Embedded CSS -->
<!--[if IE]>
<style type="text/css">p {font-size: 200%;}</style>
<![endif]-->
Note also that the "not" operator above makes the comment work in reverse, which is useful if you want all browsers except IE (or a version of IE) to process the line. Note also that the comment is constructed differently.
<![if !IE]><p>You are not using Internet Explorer.</p><![endif]>
An argument could be made for keeping IE-specific styles with other styles rather than in a separate style sheet because of increased maintainability. Grouping IE styles with all styles decreases the number of HTTP requests.
.xyz {margin-left: 30px;}
* html {margin-left: 38px;}
For more on CCs see MSDN. For a more complete guide to IE browser bugs see Position Is Everything.