11

Situation:

View the jsFiddle

Inside of a div I have an image followed by text which has a font-weight of 900 on it. In my locally hosted environment I'm using a custom font, but for the fiddle above I chose the "ever-so-stylish" Comic Sans to illustrate my point. Before anything happens I set the opacity of the entire div to 0.7. However, on hovering over the div, I want the opacity of everything to come to full opacity.

Problem:

I've noticed that in Webkit browsers only (more apparent on Chrome than it is on Safari however), upon hovering on and off the of the div tag, the text's weight will appear to change. In actuality, there's no change at all of course in the weight of the text. However, upon closer review you'll see the text appears at it's desired weight only on hover, but not in a non-hovered state.

Things I've Done:

  • I've tested this in all the latest versions of Chrome, Firefox, and Safari.
  • I am testing this currently on the new MacBook Pro which, in my case, is a retina screen. However, the colleague next to me tested the fiddle on her iMac (non-retina display) only to find the issue still apparent.
  • Perhaps I'm just crazy, but I feel this actually may be how webkit browsers choose to render out elements with different opacities. Then again, that may be just me trying to avoid admitting I did something wrong.

And naturally I thought I could lighten the mood with Comic Sans. Here's a screen capture to help explain the issue:

Embrace the Comic Sans!

cereallarceny
  • 4,913
  • 4
  • 39
  • 74
  • 4
    The effect exists. One solution might be to replace opacity `1` with `.999` - http://jsfiddle.net/ErVYs/2/ – Zoltan Toth Jul 09 '12 at 20:42
  • Zoltan, you're absolutely right. If you'd like to turn your comment into an answer, then I'll upvote and mark it correct! – cereallarceny Jul 09 '12 at 20:48
  • I've also noticed this when using the Jquery Cycle plugin and the fade transition. I assumed it was a Chrome bug as it works fine in IE and firefox – Turnip Jul 09 '12 at 20:48
  • 1
    3rror404, you're also correct. I've seen this problem in the past and never quite known why, thanks for reminding me though. Good point. – cereallarceny Jul 09 '12 at 20:54
  • Other solution: http://stackoverflow.com/a/12820319/1491212 – Armel Larcier Oct 10 '12 at 13:20

2 Answers2

24

It's not an issue with the opacity itself (in fact, turning it back to 1 in @Zoltan's example doesn't change anything for me).

The issue is with the transitions, there are two anti-aliasing modes that webkit can use:

  1. Subpixel (the default, but not supported during animations, transitions or transforms) or
  2. Grayscale

This means that when an element is rendered using subpixel antialiasing and an animation is applied to it, webkit temporarily switches to grayscale for the duration of the animation and then back to subpixel once finished.

Given that subixel antialiasing results in a slightly heavier font face you get the unwanted artifact.

To solve the issue, add this to your css:

html {
    -webkit-font-smoothing: antialiased;
}

This forces grayscale antialiasing and all the text and you won't see the switching.

(end result: http://jsfiddle.net/ErVYs/9/)

GaretJax
  • 7,462
  • 1
  • 38
  • 47
  • 3
    wait, you should not apply font-smoothing to the entire html. It can be worse to legibility in some cases. Just a tip. http://www.usabilitypost.com/2012/11/05/stop-fixing-font-smoothing/ – Luccas Nov 20 '12 at 19:09
  • @Luccas: This is true, but it's on a case-to-case basis. I'm fine with it on most every website I do. – cereallarceny May 09 '13 at 17:19
13

A possible solution would be to make the opacity transition not to 1, but .999 - http://jsfiddle.net/ErVYs/2/

div {
    width: 200px;
    text-align: center;
    opacity: 0.7;
    transition: opacity ease-in 0.25s;
    -webkit-transition: opacity ease-in 0.25s;
    -moz-transition: opacity ease-in 0.25s;
    -o-transition: opacity ease-in 0.25s;
}

div:hover {
    opacity: .999;
}
Zoltan Toth
  • 46,981
  • 12
  • 120
  • 134
  • While this technically works and looks "the same" as `opacity: 1;`, it's not as correct as the answer I chose to change to. However, it's certainly a good answer, just not the absolutely "best" answer. +1 – cereallarceny Aug 26 '12 at 01:49