Making Friends With Bad CSS
Don't let your sanity be a casualty of the browser wars
Ever since I initially dove headfirst into the shallow end of the pool that is standards-based Web design a couple of years ago, I've endured a love-hate relationship with CSS. I love the flexibility and simplicity of offloading page styles to an external document, but hate the varying ways in which today's modern browsers interpret my intentions. That's where so-called "bad" CSS comes in, and one set of tricks in particular has become an oasis in my quest to get closer to the elusive "pixel-accurate" promise of CSS.
As much as I've tried to leave things like tables and browser-specific hacks out of Web design, in the real world, sometimes you have to resort to trickery to get the job done. Indeed; after an article I wrote in January of 2004 that cautiously praised pure XHTML/CSS markup, it only took a few months for old habits to creep back in. As I wrote in a blog entry from May of the same year:
...As I'm finding out more and more all the time, different browsers are pretty finicky about how they display pure CSS sites. Oh sure, stay simple, and the results are pretty predictable. But try anything complex, and you get magically transported back to a bygone era (1997) where kludges were the norm as far as getting browsers to play nice, and tearing out of hair was commonplace.
Things haven't changed much for me since then—I've done a couple of "tableless" sites when it made sense to (and was feasible under the budget), but most of the time I've settled on a mix of tables for overall page layout and CSS for everything else. That should do it, right? Uh, not so much. CSS is great on the conceptual level, but browser hell still reigns supreme when it comes to displaying CSS. The big problem is still IE and its very incomplete standards implementation, but sometimes even browsers that are quote-unquote standards-compliant have issues displaying the same content. I expect IE to cause problems, but sometimes Safari and Firefox, which almost always render things more or less identically, have different interpretations of the same code. For example, consider the following screenshot from a site I'm currently working on (fig. 1):

Figure 1
What you're looking at in Figure 1 is a blowed-up-real-good image of the main template's footer area, where I have a table row on the bottom containing a DIV that bumps itself up into the row above it to effect a "cutout" of the sidebar. However, as the image shows, Safari has placed the DIV in the correct place (fig. 1, top), while Firefox is off by 1 pixel to the left (fig. 1, bottom), meaning that instead of creating a 2-pixel border, the line is 3 pixels and noticeably thicker (and therefore bothersome). Between the rare case of Safari and Firefox being out of whack and knowing beyond a shadow of a doubt that IE is going to cause its very own special set of problems, some CSS tomfoolery is definitely in order. I know standards purists aren't going to like this part, but tough. There's real-world work to do, so hacks have unfortunately become the norm rather than the exception. Forget about things like browser sniffing in order to feed different stylesheets to different browsers or some of the more esoteric (at least to me) solutions like the IE box model hack. It's as simple as this: two little characters are going to become your best friends. In case you haven't met, let me introduce you to the comma (,) and pound sign (#).
The example
Consider, for a moment, the simple box that is Figure 2:
Figure 2
There ain't much there—just a box, a background color, a colored border, and an image stuck in the middle. What you see, though, depends on which browser you're using. In Safari (or OmniWeb, Shiira, or other WebKit-based browser), it should look like this (fig. 3):

Figure 3
And in a Mozilla-based browser, such as Firefox, Camino, or Netscape, the same box appears as this (fig. 4):

Figure 4
And what about IE? Specifically, IE 5, 5.5, and 6 on Windows or IE 5 on the Mac? Well, here you go (fig. 5):

Figure 5
That pretty much covers the "big three" rendering engines (or two-and-a-half, given Safari's small—albeit growing—market share) I typically code against to look identical. (Note that if Opera happens to be a target browser for you, Opera adopts the rules that Mozilla follows in this case.) IE's troubles with rendering the box at different sizes and its lack of transparent PNG support on Windows notwithstanding, with our new friends comma and pound sign, you can feed different styles to different browsers without resorting to sniffers or scripts or anything. Wanna learn how? Allrighty then, let's go.
The code
To create the box in Figure 2, all I needed to do was insert a single, simple DIV:
<div id="hackbox"></div>
To that, it's just a matter of adding a style:
<style>
#hackbox {
height: 150px;
width: 150px;
background: #ff581c url(path/to/safari.png) no-repeat center center;
border: 5px solid #3d9dcd;
}
</style>
What you'll get with that, in all browsers, is what you saw in Figure 3: an orange box, blue border, with the Safari icon placed in the middle (albeit with IE screwing up the transparent PNG). With me so far? Good. Now it's time to start taking advantage of the rendering quirks to help change things around.
Order is important
One thing to keep in mind before we get too far here is that the order you place your styles in is the whole ball game. Your garden-variety browser these days renders CSS on a "last-in" basis, meaning that if you have redundant selectors, whichever one it encounters last is what it will go with. So, for example, if you have a selector listed twice, with the first instance specifying background: red; and the second specifying background: black;, guess what? You're getting a black background. That's the way it works, so with that particular gauntlet thrown down, our style order needs to be:
- Default style
- Style(s) for Safari to ignore
- Style for only IE to see
So let's go ahead and add to our original style. These new additions will represent what Safari will ignore (and hence, what Mozilla—and for the moment, IE—will render instead of the initial style):
#hackbox {
background: #3d9dcd url(path/to/firefox.png) no-repeat center center;#
}
#hackbox {
border: 5px solid #ff581c;
}
Couple things to note here. First, and most obvious, is what I outlined in red: our new friend, the pound sign. When placed at the end of the first line inside a selector, the line that the pound sign is on as well as everything after it from that point forward will be ignored by Safari. I can't stress that enough, hence the italics. The rest of your stylesheet simply won't exist as far as Safari is concerned, so make sure that whenever you use this trick you save this code for the end, so Safari can make use of all the general styles present in the document before you blind it.
The other noteworthy item is something I can't really explain. Why didn't I just put the border style on the line right below the background style instead of starting a whole new instance of the same selector? Because it won't work any other way. Once you use the pound sign, the best analogy I can make is that somewhere along the line there's a need to "cleanse the palette," so to speak. So just put the line with the pound sign inside one selector, and then if you have other styles to deal with, just put a new instance of the same selector after that. Why? Don't know; don't care. We're in kludgeland here, and I've learned not to ask why things work as they do.
At this point, Safari should render the box as in Figure 2, and everything else should render something akin to Figure 3. Again, IE will not render the PNG as transparent, so you'll get a gray box around the PNG that will make it look like there's actually a double border, but oh well. We're going to deal with IE right now anyway, so let's make another addition to the stylesheet:
#hackbox, {
background: #e3dfe0 url(path/to/ie.png) no-repeat center center;
border: 5px solid #1760bc;
}
Our other new friend, the comma, is highlighted in red. For whatever reason, IE will "see" that selector when everyone else doesn't (put more succinctly, everyone else shouldn't, since adding the comma without another selector after it is "bad" CSS). Anyway, once that's in there, IE (even the ancient, dead, disavowed Mac version) will render something similar to Figure 5.
It's worth noting at this point that these tricks aren't affected by user agent spoofing. For example, if you have Safari's Debug menu enabled and have set Safari to identify itself as IE 6 on Windows, it won't matter—the box will still render with the Safari image. Since we're taking advantage of quirks in the rendering engines themselves, changing the user agent string won't make a lick of difference.
That's all there is to it
That wasn't too bad, right? Of course, this is only the beginning—there are a million uses in everyday design for these simple hacks. The IE comma trick in particular has proved itself invaluable many times over, as it has let me move ever so closer to true pixel-accurate rendering with CSS. I'm sure that whenever IE7 is released things will break horribly, but for now, with IE still dominant but with Firefox big enough to matter, it's nice to be able to reconcile the different render engines in a relatively straightforward way.
Got Feedback? Click here to send an email. I'll do my best to answer. Really.
