Articles / No More CSS Hacks
No More CSS Hacks
Associated links:
Introduction
If you are a web designer or front-end developer, you are probably familiar with how different browsers or user agents displays your code in their own way. Picture this: You are pushing pixels and refining your designs so it fits perfect in your Firefox browser, but when presenting your design to the client in Internet Explorer, your pages might brake completely. Bye bye contract. Designing with CSS is no exception. On the contrary – table based layout seems to be more cross-browser consistent than CSS positioning. This probably one of the reasons why several big names still uses tables in their web design layouts.
CSS Hacks
To compensate these browser glitches, many CSS designers have been working on setting up CSS hacks. A CSS hack is a way to force some user agents to ignore a certain CSS property by putting faux code into the CSS file. One of the most infamous issues when designing with CSS is the Box model. IE5/Windows and IE5.5/Windows misinterpret the CSS1 box model by placing border and padding inside the specified width instead of outside. Here is an example of one of the most common CSS hacks used to solve this:
.content
{
width: 700px;
padding: 0 100px;
voice-family: "\"}"";
voice-family: inherit;
width: 500px;
}
So what does this mean? The first rule width: 700px; will apply to all user agents. But there is a second style rule, which takes advantage of a CSS parsing bug in IE5/Windows and IE5.5/Windows, to apply a width which is then overriden. So IE5/Windows and IE5.5/Windows will read width: 700px and all other browsers will read width: 500px. So far so good. Except that everyone will see your hack.
Why Hacks are Ugly
There is something fundamentally wrong with how hacks work. They are based on producing errors in the CSS code to exploit browser flaws that prevents them from parsing certain CSS properties and values. Most CSS hacks works fine enough, but looks really bad and a hack like the one above does not validate using the W3C validator. If you are interested in semantics and standards you would never put a similar hack in an XHTML file, so why would you do it in the CSS file?
The Other Way
What if we could detect browsers and serve different CSS rules to different user agents, without using hacks or conditional comments? What if the end user or validator never saw the CSS rules specified for other browsers than the one they are using? We can. Many server side languages can detect browsers just as good as any CSS hack, and in this example we will use some simple PHP magic to make things happen.
Let's Get Going
To be able to use PHP in a CSS file, we need to link up the php file in the XHTML header as CSS and then send the correct header in the php file. As we don't need the @import hack anymore, put this line in the header of your XHTML file:
<link rel="stylesheet" href="styles.php" type="text/css" />
Create a new file called "styles.php" and save it into the same directory as your XHTML file. The first line in this file will be a header command to define a text/CSS header to the document. Once this is done, we can start producing variables in PHP and use them to print different rules in the CSS file depending on user agent. Let's create a small application that detects browser types and versions and platform, and then put these values in variables for later use:
<?php
header("Content-type: text/css");
$d = detect();
$b = $d['browser'];
$v = $d['version'];
$o = $d['os'];
function detect()
{
$browser = array ("IE","OPERA","MOZILLA","NETSCAPE","FIREFOX","SAFARI");
$os = array ("WIN","MAC");
$info['browser'] = "OTHER";
$info['os'] = "OTHER";
foreach ($browser as $parent)
{
$s = strpos(strtoupper($_SERVER['HTTP_USER_AGENT']), $parent);
$f = $s + strlen($parent);
$version = substr($_SERVER['HTTP_USER_AGENT'], $f, 5);
$version = preg_replace('/[^0-9,.]/','',$version);
if ($s)
{
$info['browser'] = $parent;
$info['version'] = $version;
}
}
foreach ($os as $val)
{
if (eregi($val,strtoupper($_SERVER['HTTP_USER_AGENT']))) $info['os'] = $val;
}
return $info;
}
Let's use these variables to print out user agent specific values, taking the box-model hack as an example. We will use simple if/else statements to check our variables, but you could also use ternary operators. What we are doing here is basically: if platform is windows and browser is IE and version is less than 6, width is 700px, else width is 500px.
.content
{
padding: 0 100px;
width: <?php
if ($o == "WIN" && $b == "IE" && $v < 6) echo "700px;"
else echo "500px;";
?>
}
There are no limits to how useful a script like this can be for the advanced CSS designer. In the rule above, IE5/win and IE5.5/win will see width: 700px; and all other browsers will see width: 500px;. Without hacks and invalid CSS markup You can even skip the popular @import hack to prevent browser versions below 5 to display the CSS. Another useful example would be to serve .gif instead of .png to IE5/win & IE5.5/win if you are using alpha transparency as background:
body
{
background: url(<?php
if ($o == "WIN" && $b == "IE" && $v < 6) echo "background.gif";
else echo "background.png";
?>);
}
Here is another scenario: You want to apply max-width, but just found out that IE doesn't support it. So you found out about a max-width hack, but don't like to put ugly javascript expressions in your CSS. Here's how to serve the javascript to IE only:
body
{
<?php
if ($b == "IE") echo "width:expression(document.body.clientWidth > 800? \"800px\": \"auto\" );";
else echo "max-width: 800px;";
?>
}
Final Example
The sample PHP/CSS file in this example will hide certain selectors and only show the selector that contains the correct browser information. At the same time we will get rid of the @import hack and serve no CSS to user agents that doesn't support it properly. You can watch what the html file looks like at our Demo page. Don't forget that you can download all associated files to this article here.
<?php
header("Content-type: text/css");
$d = detect();
$b = $d['browser'];
$v = $d['version'];
$o = $d['os'];
function detect()
{
$browser = array ("IE","OPERA","MOZILLA","NETSCAPE","FIREFOX","SAFARI");
$os = array ("WIN","MAC","LINUX");
$info['browser'] = "OTHER";
$info['os'] = "OTHER";
foreach ($browser as $parent)
{
$s = strpos(strtoupper($_SERVER['HTTP_USER_AGENT']), $parent);
$f = $s + strlen($parent);
$version = substr($_SERVER['HTTP_USER_AGENT'], $f, 5);
$version = preg_replace('/[^0-9,.]/','',$version);
if ($s)
{
$info['browser'] = $parent;
$info['version'] = $version;
}
}
foreach ($os as $val)
{
if (eregi($val,strtoupper($_SERVER['HTTP_USER_AGENT']))) $info['os'] = $val;
}
return $info;
}
?>
<?php if ($b != "OTHER" && $o != "OTHER" && $v >= 5) { ?>
/* BEGIN CSS RENDERING */
body
{
background: #fff;
font: small/140% verdana, sans-serif;
padding: 4em;
margin: 0;
}
<?php
echo ($b =="OPERA") ? null : ".opera { display: none; }\n";
echo ($b =="IE") ? null : ".ie { display: none; }\n";
echo ($b =="FIREFOX") ? null : ".firefox { display: none; }\n";
echo ($b =="MOZILLA") ? null : ".mozilla { display: none; }\n";
echo ($b =="NETSCAPE") ? null : ".netscape { display: none; }\n";
echo ($b =="SAFARI") ? null : ".safari { display: none; }\n";
?>
/* END CSS RENDERING */
<?php } ?>
This article was written by David Hellsing
David Hellsing is a designer and web developer living in Gothenburg, Sweden. He is the founder and gentle dictator of Stylegala and the swedish design firm monc.
Are you a web publisher?
We are always looking for people who can write good articles about web design, css and standards. Are you one of them? Talk to us.
There are 8 guest comments so far.
All articles
- We Are Linguists
- No More CSS Hacks
- Choosing a Good Domain Name
- Design Psychology
- Making a google search engine with standards
Search
Features
- Stylegala BookStore
- The Stylegala BookStore has a massive archive of great books for you as a professional..
- Bullet madness
- Bullet madness is a list of 200 bullets, arrows and icons uploaded by our users.
- CSS Reference
- An alphabetical list over the most common CSS1 and CSS2 syntax and properties.
Sponsors

thank you
Wow. All this time I've been using imports and hacks and error.css just to get around the IE bugs and you come up with this brilliant work around.
I will definitely start using your method from now on.
Thanks for the great write-up!
It's very interesting
thanks
thanks
Odd... just yesterday I was annoyed at how IE 6/7 handled padding so I said to hell with it and wrote something almost exactly the same!
But this will be replacing that!
Thank You
very good work ! interesting
Hi David,
Thanks for this code.
Add a comment:
Keep the comment relevant and constructive.
A valid email address or URL to your site must be provided, or the comment might get deleted. Content seemed inappropriate or offensive may be edited and/or deleted. Avoid explicit language. Email addresses are never displayed. Line breaks and paragraphs are automatically converted - no need to use <p> or <br/>. Quotes & apostrophes are automatically converted to smart punctuation. Be careful when copying and pasting portions of entries or other comments. The following inline HTML elements may be used: <strong><em><pre><q><blockquote><code>. All other code will get removed before posting. Don't forget to close your tags.