Home

CSS Sub-pixel Background Misalignments

Nov 18, 2008

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:

Firefox 3.0:

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.

There IS a sort-of work around to this..

Nov 19, 2008 Corey Dutson

I found it a while back when I was running into the same thing.

I found a solution somewhere ( can't for the life of me remember) and it works out to be something like this:

body {
  background: url('whatever') top center repeat-y;
  /* Set up your repeating background shim. For this I'm assuming it's a border that wraps around an 800 pixel wide container*/

  padding:0px!important; /*this padding hack fixes things */
  padding:0px 0px 0px 1px;
  text-align:center;
}

#container {
  width: 800px;
  margin: 0px auto;
  text-align: left;
}

This will align a container in the center of the page (800 px wide), as well as adjust the background to always be in the correct center.

The issue itself is called the "Jogging Background bug" and from what I have found, this generally fixes it. I haven't used it in a while, but my old example still works, and it's in Firefox 3 so...

In any case it's a silly bug that shouldn't even exist. I mean really, how hard can it be for everyone to round to the same point?

@Corey: Are you sure?

Nov 19, 2008 Steven

Your fix should only affect IE6, due to the use of the !important trick, and even then, it only adds a one pixel offset (thus simply inverting the shift/no-shift bit). I just tried it myself and I still get the same pattern of pixel jogs that my test case revealed.

I tried both Standards and Quirks mode too. Same deal.

I found the following page that mentions your fix, and in fact it shimmers in Firefox 3 for me (look at the red header):
http://www.pmob.co.uk/temp/onepxjog.htm

In Safari 3, it doesn't shimmer except when the viewport is smaller than the image size.

This is all on OS X. Are you getting different results on the test cases?

This is really curious. Good

Nov 21, 2008 Carlos Hermoso

This is really curious. Good post

Hmm,

Nov 26, 2008 Alexa Booth

Interesting case, Steven. Hm, I'll have to think on if I have done anything to fix this in the past. I've noticed this before, but just passed it off as being a quirk, and not really attempted to fix.

This worked for me

Nov 26, 2008 Joe

Great topic, it has bugged me for a long time. I started thinking about it and thought if it is a rounding problem, then maybe the browsers would have trouble rounding odd width background images and fix itself. Well, for Firefox it worked, and IE 7 it worked too. I guess it has to do with the fact you cannot split a pixel.

Anyway, I hope it works for everybody else.

@Joe: covered in the test case

Nov 26, 2008 Steven

The test cases I used cover all combinations of odd/even width, and show no consistent behaviour for any of them. What numbers did you plug-in exactly for the different variables (i.e. column width, image width, viewport width)? Did you use browser specific tweaks? Also, did you test Safari/Opera?

I looked at your site and its structure differs from this problem: your vertically repeating background image is set on a fixed-width element, not on the viewport, and consequently your page cannot shrink beyond the width of the background.

The point of this blog post is that when the background container's width needs to be variable, rounding behaviour becomes inconsistent and impossible to harmonize.

I know...

Nov 26, 2008 Joe

Sorry, I didn't give you the home page of the site I actually am using it on as it is still on our testing server. It is a repeat-y graphic in the body. Background image width is 961px (meant to be 960px). The page overlaying it is 920px in width as the background image is the shadow and columns of the page. It definitely works for me, but I have not tested it on Opera or Safari. I had a similar problem with my previous site, which is now http://www.2005.spicy.com.au. I am not sure now if it is the "oddness" about the image, or just the fact that if it occurs it is fixed by adding a pixel.

All I know it is working here for me and I am stoked that I can continue to finish the design. I did notice you did the testing with different widths, so I am clueless as much as you seem to be.

Decimals?

Nov 27, 2008 Joe

Just as a thought, has anyone tried using eg: 49.99% for the left positioning? Maybe that way it always keeps it to the left. I will test and keep you informed when I get a spare moment.

Padding or Margin!?

Dec 02, 2008 John

Joe inspired me to try tweaking the number being used to center the "wrapper". Instead of 49.99% I simply put a pixel of padding on EITHER the left or right side of the wrapper, and it worked (a) for my site (and b) in IE7.

I'd be curious to know if this works for other designs/browsers -- I wanted to post this idea before I completely forgot rather than wait until I can validate it.

Curiously, it doesn't matter whether I use a pixel of padding or margin or which side I add it to -- worked in all instances. If somebody would like to explain that to me...

...works to a point

Dec 03, 2008 John again

Hmmm. Today I look at the same code and I see the pixel offset when I use margin or padding on the right (or none at all). It's pretty obvious so I don't understand why I didn't see it yesterday.

Adding a pixel to left margin or padding seems to work until the browser window becomes narrower than the background image. Then it appears to offset 50% of the time, implying that the rounding hack stops working when the background image's LEFT position would be a negative number.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <b> <dd> <dl> <dt> <i> <li> <ol> <u> <ul> <img> <em> <p> <br> <span> <div> <h2> <h3> <abbr> <small> <table> <tr> <td> <strong> <acronym> <th> <blockquote>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options

Recent comments

Images