Fixing Typography Inside of 2-D CSS Transforms

May 4th, 2014 by zoltan · 2 Comments

This article has been translated into Spanish by Ramajero Argonauta and has also been reposted on Flippin' Awesome.

If you’ve been using CSS3 Transforms, you have probably seen that sometimes, transformed text that is not spaced correctly, is rendered with jagged edges, and letters are placed correctly on the transformed baseline. I have seen these quirks a lot since playing CSS transforms were introduced in Firefox 3.6 was released, and they were documented on the web back then by people such as Nicolas Gallagher. If you don’t know what I am talking about, take a look at the following example in Firefox, Chrome, Safari or Opera:

I have cherished the ideal of a democratic and free society in which all persons will live together in harmony and with equal opportunities. It is an ideal for which I hope to live for and to see realised. But, My Lord, if it needs be, it is an ideal for which I am prepared to die.

Nelson Mandela

If you hover over the green block, you will see that transformed text becomes smoother when animating, but about a second after the animation stops, you’ll see it reverts back to its former jaggedy self.

As seen from the screenshots below this effect is really pronounced in Windows. It happens to a much lesser extent on OS X and Linux, and is almost unnoticable on iOS and Android mobile devices that I have tested with. (Note that this effect doesn’t seem to happen at all in Internet Explorer 9+Yes kids! For once, IE is doing something right for a change!)

Firefox Chrome / Opera IE 9+
chrome-windows-before chrome-windows-before chrome-windows-before

In addition, all Mac, Windows and Linux version of Firefox, Chrome and Opera, as well as OSX Safari will “flash” into a smooth font while animating, and will “flash” out of the smooth font when the animation is finished (the font’s color in Safari is also much brighter when it isn’t animating). In all cases, it’s quite distracting and really noticeable!

So how do we fix this?

Solution 1: Add Some Perspective!

For WebKit/Blink browsers (i.e. Chrome, Safari and Opera): the solution is easy — throw it on the GPU. Let’s say you are rotating am element -10°. The CSS to do would be as follows:

#rotated-element {
  -webkit-transform: rotate(-10deg);
  -moz-transform: rotate(-10deg);
  -ms-transform: rotate(-10deg);
  -o-transform: rotate(-10deg);
  transform: rotate(-10deg);
}

A lot of developers would proceed the -webkit-transform transform value with a translateZ(0) in order to force the GPU to render the element

#rotated-element {
  -webkit-transform: translateZ(0) rotate(-10deg);
  -moz-transform: rotate(-10deg);
  -ms-transform: rotate(-10deg);
  -o-transform: rotate(-10deg);
  transform: rotate(-10deg);
}

Developers have been doing this for a while to speed up animating the element (especially on mobile devices). This also seems to improve text rendering as well. Unfortunately, this does not work with Firefox, even when swapping -webkit-tranform for -moz-transform or just plain transform. Fortunately, there is a fix that will work with both Firefox and Webkit browsers: instead of using translateZ(0), use perspective(1px) (which still throws rendering of the object to the GPU). Here is the code:

#rotated-element {
  -webkit-transform: perspective(1px) rotate(-10deg);
  -moz-transform: perspective(1px) rotate(-10deg);
  -ms-transform: rotate(-10deg);
  -o-transform: rotate(-10deg);
  transform: perspective(1px) rotate(-10deg);
}

Note that I did not use the perspective(1px) fix for all the vendor prefixes, since it actually breaks IE9 and Opera 12.10 and lower (which uses the Presto layout engine), due to their lack of 3D Transform support (perspective() is used to determine the intensity of the 3D effect, but since we are using only 2D transforms, it doesn’t really have any other effect besides improving font-rendering). If you look at the example below in Safari, Chrome, Opera and Firefox, you will see the difference in typography with the first example:

I have cherished the ideal of a democratic and free society in which all persons will live together in harmony and with equal opportunities. It is an ideal for which I hope to live for and to see realised. But, My Lord, if it needs be, it is an ideal for which I am prepared to die.

Nelson Mandela

Here are a bunch of screenshots to show the rendering difference in the various browsers:

Firefox Chrome / Opera IE 9+
Before chrome-windows-before chrome-windows-before chrome-windows-before
After chrome-windows-after chrome-windows-after chrome-windows-after

Most people would agree that IE9 and up still renders the text better than the other browsers under Windows 7, but it is better than what it was without it, and it also doesn’t trigger a sudden change of anti-aliasing when the object animates using CSS animations, transitions, or JavaScript.

Solution 2: Use an SVG filter and backface-visibility

If you take a look at the border with the perspective(1px) fix in Firefox only, you’ll see an unintended side-effect: the border is now jaggedy:

Firefox/Win 7 Close up
firefox-border-jagged firefox-border-jagged-detail

After a bit of playing around, I noticed that using SVG filters on the HTML content instead of the perspective() fix smooths the font the same way and fixes our jaggedy border issue. In this instance, I chose to blur it with a blur radius of 0 — this doesn’t actually blur the element, but it does give us the desired effect. I use data URI directly in the CSS to apply the SVG filter so that I don’t have to make the browser download a separate SVG object.

The combined code looks like this:

#rotated-element {

  /* Perform the rotation */
  -webkit-transform: rotate(-10deg);
  -moz-transform: rotate(-10deg);
  -ms-transform: rotate(-10deg);
  -o-transform: rotate(-10deg);
  transform: rotate(-10deg);

  /*
   * The fix for WebKit/Blink browsers.
   */
  -webkit-backface-visibility: hidden;

  /*
   * The fix for Firefox ... yes, it has to be on one, huge line.
   */
  filter: url('data:image/svg+xml;utf8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><filter id="gaussian_blur"><feGaussianBlur in="SourceGraphic" stdDeviation="0" /></filter></defs></svg>#gaussian_blur');
}

Yeah, the Firefox CSS is a rather large line, but it works! Take a look at the live example below. (Note that I used -webkit-backface-visibility: hidden; instead of prepending perspective(1px) in the -webkit-transform property — it is just another way of throwing rendering of a DOM element to the GPU in WebKit and Blink).

I have cherished the ideal of a democratic and free society in which all persons will live together in harmony and with equal opportunities. It is an ideal for which I hope to live for and to see realised. But, My Lord, if it needs be, it is an ideal for which I am prepared to die.

Nelson Mandela

Here are some screenshots, in case you are viewing this page in something other than Firefox:

Firefox/Win 7 Close up
perspective(1x) fix firefox-border-jagged firefox-border-jagged-detail
filter fix firefox-border-jagged firefox-border-jagged-detail
See a clean room example of this fix in action

An outline for another fix:

Note that you can also use the CSS outline property to fix the blocky border as well:
#rotated-element {
  -webkit-transform: perspective(1px) rotate(-10deg);
  -moz-transform: perspective(1px) rotate(-10deg);
  -ms-transform: rotate(-10deg);
  -o-transform: rotate(-10deg);
  transform: perspective(1px) rotate(-10deg);
  outline: 1px solid transparent;
}
This fix may be better if performing animations, since SVG filters in Firefox are currently pretty CPU intensive. I have, however, not done any performance testing on this yet, and will post an update to this post if I find any information either way.

Other Notes

  1. Fonts don't seem jaggedy as in Firefox and IE in Windows 8 without these fixes on some computers. However, there may be cases where all font rendering may look worse. For more information about Windows 8 font rendering, read ClearType takes a back seat for Windows 8 Metro by Long Zheng and Users keep reporting blurry text in Windows 8 and 8.1 by John Callaham.
  2. Just for completeness, I have made the demos above work in IE8 and below using Visual Filters with a little help of my IE Transforms Translator. The typography is a little thicker than IE9 under Windows 7, but decent enough. Here are some screenshots to compare:
    IE8 IE 9+
    chrome-windows-after chrome-windows-after

Further Reading

Tags: Uncategorized

2 responses so far ↓
  • 1 Alan Bulpitt // Dec 16, 2014 at 2:36 pm

    For a CSS beginner this is a real gem of an article! It certainly saved what is left of my hair. Thank you!

  • 2 Daniela // Apr 16, 2015 at 4:43 pm

    Great article! My co-worker encountered this problem and he used your solution.

Give Feedback

Don't be shy! Give feedback and join the discussion.

Please Note: If you are asking for help using the information on this page or if you are reporting a bug with the code featured here, please include a URL that shows the problem you are experiencing along with the browser/version number/operating system combination where the issue manifests itself. Without this information, I may not be able to respond.

An orange star denotes a required field.