How to prevent Webkit text rendering change during CSS transition

69,648

Solution 1

I think I found A solution:

-webkit-transform: translateZ(0px);

Forcing hardware acceleration on the parent element seems to solve the problem...

EDIT As commented, this hack disables font-smoothing and can degrade text rendering depending on your fonts, browser and OS!

Solution 2

UPDATE August 2020

You no longer need to target Safari with a media query to enable subpixel font smoothing. The default is fine.

However, although it uses subpixel font smoothing by default, there's a significant fly in the ointment in Chrome's font smoothing, for anyone looking for a consistent rendering of text.

  1. This is Chrome's rendering of light text on a dark background light on dark
  2. This is Chrome's rendering of dark text on a light background dark on light

Look at the size of the whole in the letter e above. The Light text on a dark background is rendered with a perceptibly heavier weight than the dark text on a light background (with identical css font styling).

One solution, for sites respecting the user's dark / light theme setting, is to target Chrome with a media query that is limited to dark mode and switch it to non subpixel smoothing like so:

@media screen
and (-webkit-min-device-pixel-ratio: 0)
and (min-resolution: 0.001dpcm)
and (prefers-color-scheme: dark) {
  body {
    -webkit-font-smoothing: antialiased;
  }
}

The result :

dark on lightlight on dark antialiased

A far more consistent text weight regardless of whether rendering light on dark or dark on light.

Check out the side-by-side comparison of before & after: light on darklight on dark antialiased

--

UPDATE May 2018

-webkit-font-smoothing: subpixel-antialiased now has no effect in Chrome but in Safari it still improves things a great deal BUT ONLY ON RETINA. Without it in Safari on retina screens text is thin and insipid, whereas with it, text has the proper weight. But if you use this on non retina displays in Safari, (especially at light weight values) text is a disaster. Strongly recommend using a media-query :

@media screen and (-webkit-min-device-pixel-ratio: 2) {
  body {
    -webkit-font-smoothing: subpixel-antialiased;
  }
}

Explicitly setting -webkit-font-smoothing: subpixel-antialiased is the best current solution if you wish to at least partially avoid the thinner antialiased text.

--tl;dr--

With both Safari and Chrome where the default font rendering uses subpixel-antialiasing, any CSS that forces GPU based rendering, like the suggestions above to use a transform using translateZ or even just a scale transition, will cause Safari and Chrome to automatically "give up" on subpixel-antialiased font smoothing and instead switch to just antialiased text, which looks a lot lighter and thinner, especially on Safari.

Other responses have focused on maintaining a constant rendering by simply setting or forcing font-smoothing to the thinner antiailiased text. To my eye using translateZ or backface hidden significantly degrades the quality of the text rendering and the best solution if you want the text to just stay consistent and you're OK with the thinner text is just to use -webkit-font-smoothing: antialiased. However, explicitly setting -webkit-font-smoothing: subpixel-antialiased does actually have some effect - the text does still change slightly and is just about visibly thinner during transitions rendered on the GPU but not as thin as it goes without this setting. So it looks like this at least partially prevents the switch to straight antiailiased text.

Solution 3

I've noticed that pretty much every time I'm having graphics issues (flickering/stuttering/choppiness/etc) due to a transition, using -webkit-backface-visibility: hidden; on the elements that are acting up tends to solve the problem.

Solution 4

To prevent text rendering changes due to hardware-acceleration, you can either:

  1. Set all text to -webkit-font-smoothing: antialiased. This means text is thinner and not sub-pixel antialiased.

  2. If you want text which is being affected by hardware-acceleration to be sub-pixel antialiased (the default kind of font smoothing), then putting that text inside an input, without borders and disabled, will keep that sub-pixel antialiased (at least on Chrome on Mac OS X). I have not tested this on other platforms, but if sub-pixel antialiasing is important, you can at least use this trick.

Solution 5

If you're using Firefox on a Mac you'll want to use the following css to fix the issue.

-moz-osx-font-smoothing: grayscale;

More on this at Webfont Smoothing and Antialiasing in Firefox and Opera

Share:
69,648

Related videos on Youtube

RussellUresti
Author by

RussellUresti

I'm a front-end developer at an edTech startup in NYC.

Updated on April 12, 2022

Comments

  • RussellUresti
    RussellUresti about 2 years

    I'm using CSS transitions to transition between CSS transformed states (basically transitioning the scale of an element). I notice that when the element is transitioning, the rest of the text on the page (in Webkit) tends to slightly alter its rendering until the transition is done.

    Fiddle: http://jsfiddle.net/russelluresti/UeNFK/

    I also noticed that this does not occur on my headers, which have the -webkit-font-smoothing: antialiased property/value pair on them. So, I'm wondering, is there any way to have the text maintain its default look (the "auto" value for font-smoothing) and not alter rendering during a transition.

    I've tried explicitly setting the text to use the "auto" value, but that doesn't do anything. I should also note that setting font-smoothing to "none" also prevents the rendering blink during transition.

    Any help is appreciated.

    Edit 1

    I should note that I am on OS X. While looking at my test in Chrome on Parallels, I did not see the two different paragraphs behaving differently, so this may be an issue exclusive to Macs.

    • RussellUresti
      RussellUresti over 11 years
      21. And the Safari version is 6. It happens in both browsers, which makes me think it's Webkit, and not the browser.
    • Admin
      Admin over 11 years
      both antialiased and aliased paragraphs are exhibiting same behaviour. chrome Version 23.0.1270.0 canary | 21.0.1180.89 m | 5.17 safari
    • RussellUresti
      RussellUresti over 11 years
      I'm guessing you're on the dev release of Chrome. Though, OS may be playing a part in this. I'll edit to the question to note that I am using OSX.
    • Christofer Vilander
      Christofer Vilander over 11 years
      I have absolutely no idea why this works, but adding '-webkit-transform: translateZ(0);' to '.antialiased {} seem to fix it. It even works if you add it to 'p {}'. Since I can't explain why this works it didn't feel right to provide it as an answer. Hope that helps!
    • RussellUresti
      RussellUresti over 11 years
      @Christofer That makes them consistent - but it makes them all appear antialiased (they're all thinner text). I'm trying to make the un-antialiased text (the first paragraph) stay in it's default style (which is appearing a bit bolder than the antialiased text).
    • Armel Larcier
      Armel Larcier almost 11 years
      Feel free to accept any answer :)
    • RussellUresti
      RussellUresti almost 11 years
      This actually seems to have been fixed in the updated version of Chrome. Chrome no longer blinks the non-anti-aliased text.
    • ithil
      ithil over 10 years
      I find its happening on Windows browsers too so I don't think it's a Mac problem. For me the only working option now is the translateZ(0).
    • Armel Larcier
      Armel Larcier almost 9 years
      Hi! Care to accept my answser (or someone else's)? Cheers!
    • lpd
      lpd over 8 years
      Does anyone happen to know if this issue has a corresponding bug in Google's tracker?
    • thorulfr
      thorulfr about 2 years
  • RussellUresti
    RussellUresti over 11 years
    The problem with setting the font-smoothing to antialiased is that the text does not look like I want it to. I want the visual effect of setting font-smoothing to "auto" (the bolder look) - but when you do this, the text will shift during any transition. So, my goal is to just maintain the bold look of "auto" at all times.
  • Henrik
    Henrik over 11 years
    You can work around it by not using hardware acceleration. Use a timer in jQuery and do the the transition by hand (without CSS transition). I am not sure I would recommend it though, as performance and smoothness will be worse.
  • RussellUresti
    RussellUresti over 11 years
    True, I could just use jQuery to animate it... That may be the only solution if there's no other solution.
  • locrizak
    locrizak about 11 years
    Tried the font-smoothing and it didn't work. Tried this, it worked perfectly
  • Mantriur
    Mantriur over 10 years
    This is the (currently) correct answer. To my knowledge webkit-font-smoothing was removed a while ago, was supposed to be added again, but currently does not work for me in the latest version of Chrome. The translateZ trick doesn't seem to work anymore either. I guess this can change again at any time. :/
  • RussellUresti
    RussellUresti over 10 years
    This did not give me the desired result for this stated issue, but this has help fix other CSS transition problems I've had (like weird blinking text when hiding/showing content within the transitioning element).
  • F Lekschas
    F Lekschas over 10 years
    Doesn't work for me whereas -webkit-font-smoothing:antialiased; and -webkit-backface-visibility:hidden; works. Credits go here stackoverflow.com/questions/11589985/…
  • Larry
    Larry over 10 years
    +1 awesome, this is the only method that worked for the situation I found myself in
  • yunzen
    yunzen over 10 years
    This disables font-smoothing in the first place. So you have no font-smoothing at the start of transition, no font-smoothing while transition and no font-smoothing at the end. So no font-smoothing change, but also no font-smoothing at all.
  • Denis Gorbachev
    Denis Gorbachev over 10 years
    Thanks for a great writeup!
  • fregante
    fregante about 10 years
    This is the best solution. All others degrade the the text rendering, which is what OP specifically asked to avoid
  • commonpike
    commonpike about 10 years
    I stand corrected. I do see changes in font rendering in the rest of the page, also on places where translateZ(0) is not applied.
  • Bill
    Bill almost 10 years
    This is the only thing that worked for me when I had font icons that were getting blurry. Applied the transform to the sections that were being animated and it solved the problem.
  • Garavani
    Garavani over 9 years
    Life saver for me! Thanks. Avoids abruptly switching to sub pixel rendering after a css transition. At least with opacity transition this keeps the font on (theoretically always default) sub pixel rendering even during the transition. Great!
  • NiCk Newman
    NiCk Newman almost 9 years
    Dude, this was already answered 2 years ago. Right above...Exact same answer / code.
  • commonpike
    commonpike almost 9 years
    yes, my only point was you have to put the -webkit-transform on the element that has the animation, to prevent rendering changes on other elements on the page. but as commented, it worked for a while and stopped working when i changed bits of the page later.
  • kernel
    kernel over 8 years
    best solution for us! Thank you
  • Paya
    Paya over 8 years
    I have tested the option 2 (trick with input element) on Win8.1 and Chrome 47 and it does not work.
  • Iain Collins
    Iain Collins about 8 years
    Thanks, I'd come across this over a year ago and forgotten all about it. This is the only solution that resolved the described issue for me.
  • John
    John about 7 years
    This caused some text to become blurred for me.