Image Replacement Without Flickering 3D Transforms

Try the demo!

Image replacement techniques improve a web page’s accessibility by allowing you to write nice semantic HTML and progressively enhance it with CSS. If you are writing a PhoneGap application, you may not care about the accessibility and “semanticness” of your HTML, but if you do, read on!

The idea behind image replacement is to use CSS to hide the text of an HTML element and instead show a background image. This improves accessibility by keeping the text available to screen readers, while also rendering pretty pictures in web browsers.

A standard technique for image replacement that is still quite prevalent works by setting a large, negative text-indent. For example suppose you have this anchor tag in your HTML:

<a class="html5logo"
  href="javascript:void(0);">HTML5</a>

Here is how you can replace the anchor’s text with a background image using CSS:

.html5logo {
  display: block;
  width: 128px;
  height: 128px;
  background: url(/img/html5-badge-128.png) no-repeat;
  text-indent: -9999px;
}

This hides the element’s text far off-screen to the left. Provided the element does not have a lot of text, causing the text to reach back into the element, this will work fine. However, when you combine this technique with hardware accelerated 3D transforms, transitions, or animations on a mobile device, you will notice an ugly flicker whenever a 3D transform on the element is initiated.

The issue is that the large indent effectively makes the entire element 10,000 pixels wide, and GPUs have a maximum texture size that is generally far less than 10,000 pixels. Fortunately, rather than unceremoniously crashing your application, WebKit will break the element up into tiles before uploading the texture to the GPU. This process is not cheap, and so you see that ugly flicker.

So clearly you need an image replacement technique that keeps texture size below the device maximum. A good solution can be found here. The approach is similar, but rather than a large indent to the left, you indent to the right by the width of the element and clip the element’s content. For example:

.html5logo {
  display: block;
  width: 128px;
  height: 128px;
  background: url(/img/html5-badge-128.png) no-repeat;
  text-indent: 100%;
  overflow: hidden;
  white-space: nowrap;
}

Now when you initiate a 3D transform on the element there will be no flicker! Try it out and see for yourself!

comments powered by Disqus