The so-called "CSS hacks" are particular patterns used by authors to deliver style rules only to one browser or to prevent a browser from reading certain rules that might cause rendering problems. These patterns may rely on parsing bugs, wrong DOM implementations or syntactical constructs supported only by the targeted browser. Hacks may validate or not. In this article I'll review the most popular hacks used by authors, explaining why such patterns should be avoided and how one can actually target a browser by deploying other techniques.
The Box Model hack
This hack is used in order to hide style rules from Internet Explorer 5 Windows. It relies on a parsing bug related to the correct handling of escaped sequences inside strings. The pattern is the following:
body { font-size: x-small; voice-family: "\"}\""; voice-family: inherit; font-size: small; }
The voice-family
property is used only as a container of the escaped sequence,
since this is an aural property that has no effect on the visual rendering of a document
(note, however, that its first value is then overridden by the inherit
value).
Internet Explorer 5 reads first the '{' token (interpreted as the end of the declaration block) and then
an escape ('\') that hides the following declarations. In other words, Explorer 5 can only read the first
declaration of the block.
The Tan hack
This hack is used in order to hide style rules from Internet Explorer 5 Windows. It relies on a parsing bug related to the correct handling of escapes inside property names. The pattern is the following:
#box { width: 200px; w\idth: 180px; }
An important thing to keep in mind is the exact placement of the escape character. Explorer 5 ignores the declaration only when the escape is inside the property name and comes before any alphabetical character except of a, b, c, d, e, f. In fact, these six alphabetical characters will be interpreted as hexadecimal Unicode characters, undoing the hack. However, this hack is not valid, since the escaped property name will be interpreted as malformed.
The Star-HTML hack
This hack is used in order to deliver style rules only to Internet Explorer 6 (and lower). It relies
on a wrong DOM implementation that affects Explorer since 1997. According to the specifications,
the actual root element of any well-formed (X)HTML document is the html
element. Instead,
Explorer 6 (and lower) considers the html
element as wrapped in another unknown element. The pattern
is the following:
* html #box {width: 200px}
Variants of this pattern are now used in order to target Internet Explorer 7. The patterns are the following:
*:first-child + html #box {width: 200px} * + html #box {width: 200px}
CSS soup: hacks drawbacks
Hacks should be avoided for the following practical reasons:
Hacks are a short-term solution. They work as long as a browser supports (or doesn't support) a particular feature or is affected by a particular bug. Browsers are changing and their CSS support is still improving. Hacks give no guarantee for the future.
Hacks make a style sheet more difficult to read and even more difficult to be maintained.
There are other techniques for addressing our problems with CSS, as we'll see in the next section.
A different approach
Since the overwhelming majority of CSS problems are usually related to the CSS support of Internet Explorer, we can address such problems by using conditional comments to deliver particular styles only to Internet Explorer. Example:
<head> <link rel="stylesheet" href="styles.css" type="text/css" media="screen" /> <!--[if lt IE 8]> <link rel="stylesheet" href="ie.css" type="text/css" media="screen" /> <![endif]--> </head>
Since the second style sheet will be read only by Internet Explorer, we can use hacks inside it to deliver style rules to a specific version of Internet Explorer. For example:
* html p {color: green} /* IE6 and lower */ * + html p {color: olive} /* IE7 */
To deliver style rules to browsers other than Internet Explorer, we can use JavaScript (or a more powerful server-side language) to fulfill this task. The browser detection problem can be addressed through a simple object detection or more complicated string search routines. A simple object detection routine for the Opera browser is shown in the following example.
if(window.opera) { var head = document.getElementsByTagName("head")[0]; var css = document.createElement("link"); css.setAttribute("rel", "stylesheet"); css.setAttribute("href", "opera.css"); css.setAttribute("type", "text/css"); css.setAttribute("media", "screen"); head.appendChild(css); }
Note that in this example we've used the W3C DOM standard methods to attach
a style sheet to the current document, since the overused document.write()
method is not allowed when serving a document
as application/xhtml+xml
. In fact, we cannot add content to the document while the parsing process is still running.
Finally, we can also use regular expressions in order to find a particular string inside the userAgent
property of the
navigator
object. The following example attaches a style sheet for the Firefox browser.
var ua = /Firefox/.test(navigator.userAgent); if (ua) { var head = document.getElementsByTagName("head")[0]; var css = document.createElement("link"); css.setAttribute("rel", "stylesheet"); css.setAttribute("href", "firefox.css"); css.setAttribute("type", "text/css"); css.setAttribute("media", "screen"); head.appendChild(css); }
On the server side you can either use the PHP's super-global $_SERVER['HTTP_USER_AGENT']
value or the get_browser() function:
echo $_SERVER['HTTP_USER_AGENT'] . "\n\n"; $browser = get_browser(null, true); print_r($browser);
This will output:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040803 Firefox/0.9.3 Array ( [browser_name_regex] => ^mozilla/5\.0 (windows; .; windows nt 5\.1; .*rv:.*) gecko/.* firefox/0\.9.*$ [browser_name_pattern] => Mozilla/5.0 (Windows; ?; Windows NT 5.1; *rv:*) Gecko/* Firefox/0.9* [parent] => Firefox 0.9 [platform] => WinXP [browser] => Firefox [version] => 0.9 [majorver] => 0 [minorver] => 9 [cssversion] => 2 [frames] => 1 [iframes] => 1 [tables] => 1 [cookies] => 1 [backgroundsounds] => [vbscript] => [javascript] => 1 [javaapplets] => 1 [activexcontrols] => [cdf] => [aol] => [beta] => 1 [win16] => [crawler] => [stripper] => [wap] => [netclr] => )
Excellent article !
Thanks for sharing,
Thomas.