Anatomy of a CSS Hack

As web developers, CSS hacks form an integral part of our every day lives and as we’re building a web site, there is all too often occasions where our only choice is to resort to using one to work around a browser bug. But although we make such frequent use of them, many authors may not fully understand the issues at hand and may not be able to make the best decision about which hack to use, or even whether to use a hack at all.

Every single hack is made up of a combination of 3 different types of components that form together in combination to make up what we often refer to as a single entity: a CSS hack. These types are limitations, filters and patches and each hack will usually make use of at least one of each.

The Limitation

This is, in fact, the driving force behind the desire/requirement to use a hack in the first place. It is the buggy behaviour and/or lack of support for a specific feature that requires us to find an alternative. In IE, for example, the double margin float bug, the lack of support for a property like min-height or the ::before and ::after pseudo-elements are all limitations which often require workarounds, usually in the form of a hack.

Without a limitation, there is no need for a hack at all and when it comes to the decision of whether or not to use a hack, it’s important to understand whether the limitation is legitimate, or simply failure on your part to understand the correct behaviour.

For example, say you have some CSS in your page and are looking at it in two different browsers, but both browsers are displaying different results and you don’t understand which is correct (assume for the moment that one of them definitely is). In this situation, without more information, it is impossible to decide upon the best course of action to take — you cannot decide to use a hack because you don’t yet know which browser to target.

The Patch

The patch is, obviously, the mechanism used to fix, or patch, the limitation. These usually work by providing some alternate styling that either produces a similar result or triggers some special behaviour in the browser’s rendering engine to make it work, for the most part, as intended. For example, using ‘height‘ as the substitute for IE’s lack of support for ‘min-height‘ is a form of alternate styling, whereas using ‘display: inline;‘ to fix the double margin float bug or ‘height: 1%;‘ to trigger the hasLayout property are more like triggers to make IE work properly.

Some patches rely on further buggy behaviour, like IE’s broken implementation of ‘height‘; some are benign in nature which have no detrimental effect if applied to any other browsers, such as ‘display: inline;‘ for the double margin float bug; and, lastly, some are a completely conformant alternative which may not be as ideal, but are at least supported by the target browser and are a suitable substitute.

For example, one could use ‘display: inline-block;‘ which is supported by IE, as an alternative to ‘display: table-cell;‘ in some cases. They’re not perfect substitutes (and it’s probably not the best example), but one could make use of their similarities to produce similar results across the browsers.

The Filter

The filter is the glue that binds it all together and is used to make sure that only a select set of browsers will apply the patch and there is a large range of CSS filters available for the picking. These come in two primary forms: hide from target browsers and hide from conformant browsers.

The first is to hide the correct CSS from the target browsers (such as using the child combinator to hide styles from IE). With this form, it is the conformant CSS that is hidden from the target browsers, yet the patch itself is not usually hidden; it is simply overridden by subsequent styles or styles with a higher specificity, effectively causing the patch to be ignored by conformant browsers anyway. In this case, it’s important to ensure that all conforming browsers (with respect to the limitation) also support the filter, so that the correct CSS is applied.

The second form is to hide the patch from newer, conformant browsers (such as using star html or a conditional comment to hide from everything but IE). In this case, the conforming CSS will be seen by all browsers, yet the patch should only be seen by the target browser. In such cases, the filter usually depends on an additional limitation in the target browser and, as a result, you must be careful that all browsers suffering from the filter limitation also suffer from the original limitation.

Such cases are rare, but they do happen and they need to be dealt with appropriately. For example, the “be nice to Opera” patch within the Box Model Hack is an example of where the first patch (intended for IE5) was incorrectly applied to Opera – a conforming browser with respect to the original limitation, but non-conforming with respect to the filter – and thus needed an additional work around.

Lastly, some filters are actually a combination of both types, whereby the patch is hidden from conformant browsers and the conformant CSS is hidden from the target browsers. For example, there’s an @import hack which will load one CSS file in conforming browsers and another in non-conforming browsers.

One thought on “Anatomy of a CSS Hack

  1. I try to avoid hacks as much as possible because who knows what future versions of what will support what standard and in what combination (for example, browser x supporting child selectors but not :hover psuedo-classes on all elements, etc). I don’t want to back myself into a wall.

    For IE, I stick to IE conditional statements and for other browsers, I can usually find a similar work around.

Comments are closed.