Go back to home page of Unsolicited Advice from Tiffany B. Brown

Testing for CSS 3D Transforms Support

Support for 3D CSS Transforms has made it into most browsers in an experimental form. Opera's support, however, is still busy baking.

Unfortunately, 2D and 3D transforms share at least one property in common: transform (though prefixes are still required for now). Testing for it won't exactly tell you whether the browser supports 3D transforms. 3D support is possible to detect, however. I'll describe one approach in this post.

If a browser supports transforms, the value of getComputedStyle(el).getPropertyValue('transform') will be a matrix. Otherwise, it will be undefined.

A 2D transform, or its equivalent*, will return a matrix such as matrix(1, 0, 0, 1, 0, 0). On the other hand, a successful 3D transform will result in a 3D matrix such as matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1). If a 3D transform can't be calculated — say, if the browser doesn't support them, or the values in the matrix are somehow invalid — the value of getComputedStyle(el).getPropertyValue('transform') will be none.

Now, here's one way we can test for 3D transforms support using this knowledge. For clarity and readability's sake, the example below does not use prefixed properties. please don't copy-and-paste this example. Use the Gist instead.

function has3d(){
   var el, has3d;

   /* Create a new element */
   el = document.createElement('p');

   /* Apply a transform */
   el.style['transform'] = 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)';

   /* 
   Add it to the body to get the computed style (for Opera mostly).
   Since it's empty, it shouldn't interfere with layout in modern browsers. 
   */
   document.body.insertBefore(el, document.body.lastChild);

   /* Get the computed value */
   has3d = window.getComputedStyle(el).getPropertyValue('transform');

   /* 
   If it's not undefined, tell us whether it is 'none'.
   Otherwise, return false.
   */
   if( has3d !== undefined ){
      return has3d !== 'none';
   } else {
      return false;
   }
}

In a production environment, you'll also need to determine which prefixed property we need to use. I published a Gist that includes such code. I also published a quick-and-dirty example of how it works.

*matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) and matrix(1, 0, 0, 1, 0, 0) are equivalent. They are both identity matrices, which means applying them has the same effect as not applying a transform. See Understanding the CSS Transforms Matrix for more.