CSS Sub-pixel Background Misalignments
Update: and now, IE8 adds even more odd behavior to the mix!
A while ago, John Resig pointed out some issues with sub-pixel positioning in CSS. The problem he used is one of percentage-sized columns inside a container, where the resulting column widths don't round evenly to whole pixels or don't sum to the correct total. His conclusion is that browsers each have their own way of dealing with the problem.
I've recently been bumping into a related issue however, that shows the situation is even worse: rounding is inconsistent even inside a single browser.
Take the following scenario: a fixed width element that is horizontally centered in a viewport using
margin-left: auto; margin-right: auto;. The viewport has a horizontally centered background image, having
background-position: 50% 0. This is an extremely common page structure.
You'd logically expect the background image and the element to line up, and move as one when the viewport is resized. However, this is not the case. Depending on the viewport width, the background can be offset one pixel to the left or right. This obviously wreaks havoc on many designs. I decided to investigate this more closely and the results are not pretty.
My test case consists of the basic structure described above, repeated in a bunch of mini-viewports. Each background image contains a black box of a certain size, and is overlaid with a grey element that covers this box exactly. If the two pieces align, there should be no black peeking through on the sides, and each box should be fully gray.
For full coverage, I vary the following parameters:
- The width of the viewport
- The odd/even size of the box/element
- Background image is bigger/smaller than the viewport
- Background image is padded evenly/unevenly around the box (1px difference). (*)
I tested this in IE6, IE7, Safari 3.1.2, Firefox 3.0.4 and Opera 9.6.2.
The result is quite baffling: not a single browser out there rounds background image positions the same as element positions, resulting in misalignments. The tell-tale black lines show up in every browser:
IE6 and IE7:
Safari 3.1 and Opera 9.6:
What's worse is there isn't a single case (across viewport sizes) that is handled consistently between all the browsers. So this CSS technique should in fact be considered broken.
Of course this brings up the question: is it really a browser bug or just an implementation quirk? I would argue that at least in the case where the image's width parity matches the element's, you'd expect perfectly matching rounding (i.e. for the first four rows of test cases). The other test cases are more ambiguous, and all you could hope for is consistent behaviour in each browser individually.
I wonder why this hasn't been brought up more though. A quick sampling of designers around me shows that they have all encountered this bug, but don't really know a fix and just tweak the design or layout structure to mask the effect.
If you really do need to align a background image properly, there is an ugly work-around: place your background image on an additional fixed-width element layered behind the center column. Center the background's element using margins rather than the background image itself, and clip it off at the sides using
overflow: hidden on an additional wrapper. This causes the background's position to be rounded the same way as the column on top.
(*) Note that there is a choice whether to pad more on the left or on the right. I chose the left. This means that the last 4 rows of test cases are inherently ambiguous: a browser that misaligns all of these in the same fashion is in fact being consistent, just in the opposite direction.