When asked about developing web applications, I used to joke that it is a black art, mixed with a little bit of illogical reasoning, and brought together under completely unsound engineering principals. Unfortunately, this describes the state of many cross-browser CSS work-arounds.
It is well known that each browser has its own slightly unique way of interpreting CSS. These differences create headaches for web-developers. We have all learned how to manipulate the various box models by introducing extra structure into the page. While adding structure can smooth out some of the box-model differences, I never understood how any engineer could then decide to apply their knowledge of bugs and incomplete implementations to create and apply the styles to their page.
Below I am going to list a few hacks I found after a very quick search of the web (there are hundreds of documents on the subject). These are mostly to illustrate some of the "state of the art" of cross-browser CSS:
from The “Be Mean to Opera” Hack
html>body div.myDiv { color: red;
c\olor: black;
/* be ~mean~ to Opera -- http://www.albin.net/CSS/beMeanToOpera.html */ }
from Tantek's famous box model hacks
div.content { width:400px;
voice-family: "\"}\"";
voice-family:inherit;
width:300px; }
My favorite hacking mistakes are those that try to leverage unsupported "real" features:
html>body #header {margin-bottom:1em}
The above examples have actually become part of the CSS development "patterns" - and more amazingly, there are many more. While these solutions are very creative, I refuse to explain what they do or why they may be used for a simple reason - they promote the black art of the web instead of pushing sound engineering. I have seen stylesheets mix so many different hacks, all without comments, making it impossible to decypher the intended presentation, and even more impossible to debug. Furthermore, as new browsers are released, how do you know the parsing bug or feature you are relying on will not be removed or modified.
Now, I wouldn't be bringing this up if there wasn't a very simple and logical solution to this issue. Let's start by thinking about the problem. In a perfect world, we would create a single CSS stylesheet that would work everywhere. Unfortunately, the browser world is probably about 95% perfect (which I determined in a completely arbitrary and unscientific way) leaving us to find a way to deal with the difference.
So... assuming the world of CSS is almost perfect, we focus on building a single style sheet and explicitly targeting any such imperfections. How do we accomplish this? CSS has a built-in mechanism called the class name. Differing class names can be used to generate completely different layouts. We can leverage this approach for browser targeting.
I focus on applying a classification scheme onto the <HTML> element either dynamically generated on the server side or injected into the element via client script. This classification can be used generate CSS overrides. Confused? Let me give you a few examples:
<html class="firefox m1 d03 mac">
<html class="ie m6 d0 win">
<html class="opera m8 d0 win">
If you haven't figured it out, I specify the browser type, the major version, the minor version, and the platform. This classification scheme can be as simple or as complex as you wish. Now... in my css sheet, let's look a simple few rules:
div.Box {margin:5px;width:100px;padding:5px}
html.firefox div.Box {width:95px}
html.opera.m8 div.Box {padding:0px}
Can you guess what the above does? The div element with the class "Box" gets a default width of 100 pixels. For FireFox and Opera V8 we created two targeted overrides: In Firefox, the width is set to 95px. In opera, major version 8, the same element has no padding (and is 100 pixels wide).
Why do I like this approach? Anyone familiar with CSS can walk up to the stylesheet and quickly decypher the intent of the stylesheet and the specific browser issues being addressed. No bugs or missing features being leveraged, just good old solid understanding of the CSS model being applied in a reasonable, logical, and sound way.
UPDATE: I have seen a few comments (from other blogs) concerned about "coding" in specific versions. This was for illustrative purposes. Typically, you only need generalized overides (e.g., html.opera) with version specific overrides that may exist for legacy versions. This way we are reasonably protected when a new browser is released. If the new browser fixes the issue, we then increase the specificity to use the version.
Also, for caching - these are psuedo-dynamic pages and depending on your architecture can serve these cached once you determine the user-agent. The biggest issue occurs if your page is truly static and is intended to be cache by proxies. In this scenario, a server generated approach is not the way to go. For this reason, and since I do not like to burden the server with processing the client can easily handle, I typically use client-based solutions whenever possible. For my scenarios, I am less concerned about users that disable scripting - we are building applications, not web pages - and I view the argument that our applications should be able to run with scripting turned off as academic. I will save a deeper explanation for another post and how to handle the no-scripting case.
Also, this approach only needs to recognize browsers of interest and align them to your desired classifications scheme which may be different from using version, etc. If the browser is not recognized, it will get render with "standard" stylesheet, which is a reasonable and perhaps the only possible result.
As my closing nugget:
CSS is an extremely powerful system that can greatly reduce your need for script. By simply changing the class name on an element, you can completely impact the entire layout of the document (hiding content, showing other content, repositioning, etc). Think about how you can apply this technique to replace widespread "presentation"-driven scripts with a single line that swaps the class name of an html element.