Percentage rounding and sub pixel perfection

Posted by – February 22, 2009

I have been working on a grid design framework for able2know, and have been wrestling with the inconsistencies between browsers when it comes to percentage rounding. There are good static grid CSS frameworks out there (e.g. Blueprint, 960gs) but the attempts to convert them into liquid grids (e.g. Liquid Blueprint, fluid960gs) share the same bugs I was running into, where width is not correctly calculated and in IE6 and IE7 the effect was often dramatic, with columns jumping around when the browser is resized. At some widths everything would be perfect, but at others columns would wrap into the next row, breaking the layout. A couple of pixels off might not sound like much, but have a look at this video to see the effect on the layout those few pixels can have.

I set out to fix this problem, and began writing my own grid CSS framework, trying all sorts of different mathematical approaches to the problem (I was working with three columns so my initial suspicion was that the browsers were having a hard time dealing with fractions like 1/3). I also tried every IE CSS hack to try to coax the floats the grid uses into compliance, but no dice. So after more investigation, I learned that this is a rendering problem with no perfect solution for the browser manufacturs. The limitation is your monitor.

To make a very long story short, your monitor needs your browser to display objects sized in pixels. Browsers have no easy way around this problem, and there is no perfect solution. This, of course, doesn’t mean that the various browsers will find a consistent way to handle it, and as per usual IE managed to settle on the least optimal approach of the bunch.

First of all, let’s explain the problem. Let’s say you want to divide your layout into 4 columns. The pixels this area will represent may not be divisible by four. Let’s say you are dividing 50 pixels (no matter what you choose, the browser may end up with an indivisible problem, so I’m just using an easy example that John Resig used in his comparison of how each browser handles the rounding), each column should be 12.5 pixels wide according to your CSS telling the browser to divide the 50 pixels into 25% columns. The problem is that your browser can’t do this, and needs to round to an integer. No matter what your browser does at this point, there will be imperfections. If your browser rounds down there may be gaps and if you round up, then there can be an overflow.

Here is a test you can use to see this error in effect in your browser. It should display a black box, but drag your browser around resizing it, and at some sizes you should see gaps (in Firefox 3 you may not be able to do so due to an innovative way they are handling the rounding rounding the layout to 1/60th of a CSS pixel).

Now I understand the limitations each browser faces, and how they can’t possibly get it all perfect, but what IE does that breaks layouts as you resize the browser, causing your divs to jump around is round up. By rounding up they may cause your columns to exceed the width of their container, and wrap. There’s not a great way around this, and the only easy solution is to not let your columns add up to 100%, leaving room for rounding up without breaking your layout. It’s not a great solution, and it ruins the pixel perfect grid I was trying to implement on able2know with more space on the right than on the left but I thought I’d write up the problem in case others need it to fix their floats.

Other sources:

https://bugzilla.mozilla.org/show_bug.cgi?id=177805

https://bugzilla.mozilla.org/show_bug.cgi?id=63336

https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334118

http://www.satzansatz.de/cssd/geckogaps.html

http://www.ojctech.com/content/css-jumping-columns-and-ies-percentage-rounding-algorithm

14 Comments on Percentage rounding and sub pixel perfection

  1. chuck says:

    You know, with JavaScript, it’s dog-easy to do this. Granted, certain browsers don’t run JS (and redraw) continually during window resizes (Firefox is one), but the ease, control, and peace of mind that comes from manually calculating the positions of your boxes is totally worth it. Plus, if you’re feeling anal about having the window resize action in Firefox actually render properly at every point during the resize, you could temporarily revert to the CSS-only approach, avoiding rounding error and overflow as you describe above. (My test I use for whether the user “is resizing” or not is whether an onresize has fired in the last 3/10 of a second; it seems to work pretty durned well.) Even IE renders during resize. Don’t know about 8, but 6 and 7 do. Safari is close to flawless at it.

    Absolute positioning is epic!

    If you’re wanting to make it REALLY perfect, then you can do only what a crazy person does, and put one-pixel COLUMNS on the left- and right-hand sides of each column box. Make sure you do all your JavaScript-based width divisions without doing a Math.floor or .ceil or .round anywhere to round to an integer (i.e. keep the decimal.) Make the DIVs the exact same color as the background of the column div, and set their opacity to the decimal fraction that is left over after rounding the position down or up to the nearest integer. That way, the outsides of the boxes themselves are actually rendered down to subpixel precision, and the world is a happier place for it! (Granted, the contents of the DIV columns will still nonetheless be jumping pixel by pixel, but while the page is in motion during a window resize, everything will be beautiful!)

    JavaScript forever.!

  2. I want to note that the problem isn’t specifically the jumping during resizing. In those examples, if the user’s viewport is a particular width the layout would break without any resizing. But yes, JavaScript is one way to address this, but it has some downsides that render it unacceptable for this project.

  3. chuck says:

    Sorry for the comment spam, but one more thing…. I love this stuff…. sorry :-P

    I forgot to mention that taking the absolute positioning approach and calculating pixel coordinates and widths dynamically will actually produce a layout that is actually more precise than making divs that are sized according to a percentage of the width.

    If the first column ends up wanting to be at 110.2 px from the left edge, stick it at 110; if the second one wants to be at 462.8px, stick it at 463… if the third column then wants to be at 815.4, and you naturally put it at 815px from the left, then the distance from the left edge of the first column horizontally to the second column (353px) is different than the distance from second to third (352px).

    This means that the layout is actually more accurate than ever could have achieved using percentages, woot! Some nicer, newer browsers may do this rounding for you (maybe Firefox 3?), but I really don’t know, because I never need to care, since I can make sure it happens all the time anyway. And yeah, if you want, you can make it degrade to best-effort CSS when JS is unavailable…

    And don’t worry about it being slow, either; if you write decent JS code, you can get something like a column layout done in negligible time, say, well under 1ms worth of execution. Take the MobileMe Gallery grid layout as an example; it’ll manually lay out 100+ photos (each with width, height, left, and top) in under 10ms in Safari, and under 50ms in Firefox. Though IE is horrendously slow in other ways, the actual photo layout loop even works pretty well in IE when resizing the window with the album list on the left collapsed. I ramble too much… my point all along in is that laying out a few columns with absolutely calculated pixel coordinates would never be a hit in any browser or on any machine.

    So… may as well go for the manually implemented “anti-aliasing” !! :-D

  4. chuck says:

    Oh, hey, you wrote back! Sorry I missed it; I was out and then typed the next one without refreshing.

    Cool enough; whatever gets the job done for you! I’m a JS junkie myself. I was indeed quite surprised to see that resizing video with the rounded-up widths; I knew I didn’t like using CSS width percentages as a production-ready layout solution, but I didn’t know I had that much reason to stay away. Ouch!

    Kick ass post, by the way, thanks. :)

  5. The requirements include liquid layout, which precludes such static positioning. Any of the CSS frameworks I mentioned would work out-of-box if there were not the liquid requirements. However this is a popular site with enough users at 800X600 to make a move to a 960 grid unacceptable, and enough users at high resolutions to make the “lowest common denominator” approach unacceptable.

  6. This is tongue in cheek, but why not use a layout table ;)

  7. Funny you should mention that, I was discussing its merits seriously with the lead developer last night. Ultimately, it has its own downsides that we find unacceptable (e.g. IE table rendering speed).

  8. Stubbornella says:

    Hi Robert,

    I saw your tweet re: IE rounding errors. I hadn’t capitalized the “lastUnit” class name in the AG test grids. Oops.

    Thanks for the link to this article. I resolved the problem for better browsers (IE7+, FF, Safari, Chrome) by having no set width on the last unit in any row. This means that the last unit absorbs the subtle rounding differences between those browsers. overflow:hidden is the trick, if you read the spec it is a subtle effect of this property when no height is specified.

    On the other hand, IE6. :) My grids used to have gutters. I recently removed them, because I felt it was important that gutters live on content objects rather than grids. I used the gutter to help IE6 by floating the last unit right. That meant that between the last two units an ~10px gap absorbed the rounding errors in IE6. Ultimately, I need a way to tell IE6 that the last unit should be all the rest of the space on the line, whatever that might be. Removing the width causes the calculation to be content dependent and, like you’ve talked about above, having a width causes (at some resolution or another) the unit to wrap.

    I really don’t want to put gutters back in the grids (though they worked very well and were well tested with gutters)… so I’m searching for a solution. I’ve documented the bug here:
    http://stubbornella.lighthouseapp.com/projects/26663-object-oriented-css/tickets/1-arnaud-gueras-test-wrapping-on-ie#ticket-1-4

    Thanks again for your feedback,
    Nicole

  9. Nicole,

    I wrestled with this issue for a couple of sleepless nights back then. Here were the results of my own research:

    1) There’s no way around the issue without feeding IE lesser values.
    2) The more columns you have, the lower the value needs to be, making it pretty obscene if you have enough columns (I was shooting for a 12-column grid because of how divisible it is).

    I, for one, like the gutters on the grid level, so if you had a solution with gutters I’d personally be interested in it. So far of all the CSS grids I’ve seen I’ve liked your fledgling one best, and am interested in building off it and extending it if I don’t end up rolling my own.

  10. Stubbornella says:

    Robert,

    Check it out now. The file size is even smaller, no gutters, and the lastUnit takes all remaining space, even in IE5.5 and 6! I’m so pleased. :) Position relative on the lastUnit was the key.

    The only caveat now is that content being wider than the grid itself can cause a wrap. For example an extra long word at really narrow widths. In my tests it works perfectly for all normal scenarios.

    The IE5.5 support is really key, because quirksmode in later versions of IE is much more like 5.5.

    http://github.com/stubbornella/oocss/blob/3f0b8664446ee1f911d91370945cf948581ba484/css/grids.css

    Let me know if you manage to break anything, good use cases are always welcome!

    Cheers,
    Nicole

  11. Herve says:

    If tables were called grids would people stop saying they should only be used for tabular data?

  12. David Rivers says:

    This discussion is fascinating as I began the other night rolling my own grids solution I call gridular: http://github.com/DavidRivers/gridular. I wasn’t much aware of the “subpixel rounding” issues until I saw the term being listed as a feature of a CSS library while doing research. I haven’t even tested across browsers yet and my library is extremely alpha, but I might have to adopt Stubbornella’s solution to this issue.

    Stubbornella also forced me to consider removing gutters from units. I would like to follow the OOCSS design principles as much as possible, but I may have to make a compromise for the sake of designer sanity. I had envisioned providing a .gr.gr-m (a “Grid” with a margin “Grid Style” applied to it) as a class providing sane margin/gutter defaults (if such a thing exists) on grids, based on design theory if I can find enough theory to support specific algebra, and derivative classes to either add or remove margins/gutters if the designer wants more or less. I’m not really sure where I’m going to end up with this idea, but I would really like to ease the burden of whitespacing grids and units if possible.

    Robert, did you ever finish the CSS library you discuss in this blog entry? I am curious to see what you have come up with.

  13. Russ says:

    It’s unfortunate to see even this page is getting spammed by people hawking medications.

  14. Benjamin says:

    Hello!

    i stumbled over this as the problem teased me for several times. i tried to build kind of portfolio with a fluid grid where only pictures are shown with rectangle pictures. the basic size is something like 300 x 300 px and if i want to use pictures in a bigger scale i can use a multiple size of this in width and lenght. so far so good…everything should float to the left and so on. then i had the subpixel problem especially in firefox.
    what worked out fantastic (i am not a professional webworker) was, to put everything in a wrapper which i use for adjusting the whole page width. so now u can see its logical a matter of calculation if u use diferent percentage widths. the problem occures only on certain widths. instead of giving the wrapper a percentage width and align it via margin:auto, i gave it only a padding in pixels. i tried it in the most common browsers and its working pretty good. so maybe i only was lucky with going my trial and error method :) if somebody wants to try let me know about your experiences…

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>