All in the <head>

– Ponderings & code by Drew McLellan –

– Live from The Internets since 2003 –

About

When Bugs Collide: Fixing Text Dimming in Firefox 2

19 June 2008

Example of the visual affect of Firefox 2 switching anti-aliasing methods When working on front end development projects, we’re finding that a great many more sites are taking advantage of small JavaScript animation effects to make interactions feel more polished. One thing you may notice if you use Firefox 2, particularly on a Mac, is that as you animate the opacity of an object the text on the whole of the page can appear to dim slightly. This, in fact, happens with any change of opacity on the page.

The effect is particularly noticeable when the page’s colour scheme uses light text on a dark background. What’s happening is that Firefox is switching from using the operating system’s text anti-aliasing to using its own internal system. As you can imagine, when you switch from one system of anti-aliasing to a completely different system this can have a noticeable impact on the appearance of any type.

This clearly isn’t a new problem, and the anti-aliasing switch is no longer apparent in Firefox 3. A common way of addressing the issue is to force Firefox to use its own anti-aliasing from the outset by setting the opacity on the entire page to something very close to the maximum value of 1. Typically 0.9999 or similar. This has no visible impact other than to kick Firefox into a text rendering mode that it can stick with throughout the animations.

I often use jQuery for quick, simple UI animation jobs, and so I would often do something like this:

$('body').css('opacity', 0.9999);

This nicely prevents the text dimming effect in Firefox 2. So job’s a gooden, right?

Enter stage left: Internet Explorer 7

One of the other techniques we’re seeing a big uptake in is the use of alpha-transparent PNG images, thanks to native support in IE7. Up until now, developers have had to make use of an awkward filter for PNG transparency in IE6. With healthy adoption rates for IE7, creative use of PNG transparency is definitely on the up.

There is, of course, a snag. IE7 appears to have a bug whereby changing opacity on an image element containing an alpha-transparent PNG causes the transparent sections of the image to turn grey (just like the default IE6 behaviour). You can view this demonstration page in IE7 to see it for yourself. Early betas of IE8 have addressed this, and to be honest you can see why IE7 might flip out a bit if you take an image with variable transparency and then try and adjust the opacity of the entire thing. I think I’d flip out too. But I digress.

The issue here is that if you’ve used something like the code above for setting the opacity for the entire page to something less that 1, any transparent PNGs in the page are going to turn grey in IE7. The solution, of course, is to be more specific in your targeting. As jQuery has some basic built-in browser sniffing, I found it easiest to modify my statement to:

if (!$.browser.msie) $('body').css('opacity', 0.9999);

This would apply the opacity change to everything except IE, which in this instance works just fine as IE is the only browser that appears to have a problem with it. There may well be a better way to target FF2 specifically (update: see some great suggestions in the comments below). If you’re using an IE specific stylesheet with conditional comments, you could correct the opacity change in there equally well.

As so often is the case with cross-browser and cross-platform development, fixing one issue can lead to another. Our job as web developers certainly isn’t easy.

- Drew McLellan

Comments

  1. § Natalie Downe:

    This fix works like a dream! thanks for writing it up Drew. I have noticed this strange rendering behavior in firefox two even when I am not animating the opacity, on occasion when I am only doing very minor changes in Javascript on the page, this fix works then too. I have it set now on the body and over-ridden as opacity:1; for IE.

  2. § Julik:

    Well you do have -moz-opacity for all it’s worth

  3. § Scott Schiller:

    Shaun Inman has an interesting tweak for Safari, addressing a similar rendering issue (high-contrast fonts, it seems?) – basically applying an invisible text shadow fixes things there.
    http://24ways.org/2006/knockout-type

    This sounds like the same approach/result (OS anti-aliasing vs. browser aliasing?) as with Firefox, but I’m not certain that opacity:0.99 for example would produce the exact same result on Safari.

    I’ve noticed Firefox 3 has made the font rendering more approximate to Safari’s behaviour as well, the fonts seem to be “fatter” than previously.

    I don’t have any hard data, but I’m a bit wary of applying these effects on large production sites as it may have performance implications (for example to render the whole document at 99% opacity.) I am a fan of nicely-rendered text though, so don’t get me wrong – I use the shadow technique on my own site for Safari, and the difference in rendering is highly noticeable. Sometimes I use “font-weight:lighter” which seems to work quite nicely in Firefox.

    I rather prefer the non-native text aliasing in the “after animation” image, is that wrong? ;)

  4. § Adrienne Travis:

    Alternatively, since the proprietary renderer-specific extensions are still supported by both Safari and Firefox, you could do this in your reset:

    body {
    -moz-opacity: 0.999999;
    -webkit-opacity: 0.999999;
    }

    These are the old ways of doing opacity, before both browsers started actually supporting it without the prefix . They’ve been left in for compatibility reasons. This way you fix the rendering only for the browsers that need it, and with no actual “hacks”.

  5. § Drew McLellan:

    Great suggestions, all.

  6. § Paul Irish:

    Jonno Riekwel just wrote about about the details of this issue in Safari.
    It looks like the css opacity fix that Adrienne suggested is the answer.

  7. § Nathan Smith:

    Drew: This will target Firefox, version 2.0 and lower, and only on OS X…

    if ($.browser.mozilla && parseInt($.browser.version, 10) <= 2 && navigator.appVersion.indexOf(‘Mac’) !== -1) {$(‘body’).css(‘opacity’, 0.9999);}

  8. § saachi:

    In relation to Firefox 2+ on Mac however, using css opacity vallue less than 1 essentially kills Flash, making it act “peek a boo”-like.

  9. § Philipp Bosch:

    We’ve been experiencing similar problems in one project and tried to fix it with $(‘body’).css(‘opacity’, 0.9999); too. What we noticed was that Opera had severe problems with that (can’t quite remember what it was), which made us prefix the above code with if ($.browser.mozilla) …

  10. § Wilson Miner:

    To add another quirk to the mix, Firefox 3 now appears to introduce yet another anti-aliasing method when opacity is set. It doesn’t apply to all elements on the page the way Firefox 2 did, but it’s definitely an undesirable effect. It looks like it’s just making the anti-aliasing too strong and the edges get extra chunky.

    Safari 3 actually exhibits similar behavior when using opacity without the text-shadow trick, but the method it reverts to, while definitely too strong (and chunky) uses sub-pixel anti-aliasing (colored pixels) if your system-wide settings are set to LCD. The Firefox 3 glitch appears to use monochrome anti-aliasing no matter what, but it’s just too strong.

    Now if we can just figure out a way to trick Firefox 3 into using something closer to what FF2 and Safari are using with the opacity and text-shadow tricks, we’ll be set. Unless Safari 4 “fixes” the text-shadow switch, then I’m quitting the Internet.

  11. § Wilson Miner:

    Here’s a screenshot of the Firefox 3 glitch in action. It only crops up when you’re using one of the LCD-optimized system-wide settings, so I suspect it’s a conflict between the anti-aliasing applied by the Cairo graphics engine and the system.

  12. § Wilson Miner:

    Nathan: Actually, jQuery’s browser.version returns the Gecko version for Firefox, not the Firefox version (which is weird). Firefox 3 is 1.9, and Firefox 2 is some variant of 1.8. So you have to use parseFloat to get the second digit as well:

    if ($.browser.mozilla && parseFloat($.browser.version) < 1.9 && navigator.appVersion.indexOf(‘Mac’) !== -1) {$(‘body’).css(‘opacity’, 0.9999);}

Photographs

Work With Me

edgeofmyseat.com logo

At edgeofmyseat.com we build custom content management systems, ecommerce solutions and develop web apps.

Recent Links

Affiliation

  • Web Standards Project
  • Britpack
  • 24 ways

About Drew McLellan

Photo of Drew McLellan

Drew McLellan has been hacking on the web since around 1996 following an unfortunate incident with a margarine tub. Since then he’s spread himself between both front- and back-end development projects, and now is Director and Senior Web Developer at edgeofmyseat.com in Maidenhead, UK (GEO: 51.5217, -0.7177). Prior to this, Drew was a Web Developer for Yahoo!, and before that primarily worked as a technical lead within design and branding agencies for clients such as Nissan, Goodyear Dunlop, Siemens/Bosch, Cadburys, ICI Dulux and Virgin.net. Somewhere along the way, Drew managed to get himself embroiled with Dreamweaver and was made an early Macromedia Evangelist for that product. This lead to book deals, public appearances, fame, glory, and his eventual downfall.

Picking himself up again, Drew is now a strong advocate for best practises, and stood as Group Lead for The Web Standards Project 2006-08. He has had articles published by A List Apart, Adobe, and O’Reilly Media’s XML.com, mostly due to mistaken identity. Drew is a proponent of the lower-case semantic web, and is currently expending energies in the direction of the microformats movement, with particular interests in making parsers an off-the-shelf commodity and developing simple UI conventions. He writes here at all in the head and, with a little help from his friends, at 24 ways.