Cross-Browser CSS3 (yes, even in IE6 and IE7), with a Little Help from MS Filters

What is the purpose of this article?
To show how you can use (some) CSS3 styles right now, across all browsers, even IE6 and IE7, without the limitations and penalties of graphic headers and graphic backgrounds.

Why is this important?
Graphic headers and graphic backgrounds are time-consuming to create, limiting in their inflexibility, and a pain in the behind to maintain.  Plus, every image the user has to download is another HTTP Request and additional bandwidth our servers have to send to them.

Introducing the Microsoft Internet Explorer Filter

What is a filter?
A filter is a Microsoft Internet Explorer proprietary CSS effect that can be applied via CSS or JS.  I will discuss the CSS applications, hopefully if you need them, you can figure out the JS applications pretty easily.  Filters were first introduced with IE4 and can be applied to pretty much any HTML element (that hasLayout… :-).  There are also a slew of “transition” filters (time-varying filters that create a transition from one visual state to another), but they all remind me too much of the horrendous PowerPoint transitions and, besides, we would most likely implement any form of transition via one JS action across all browsers, so I will be ignoring those too.

What can filters do?
More than I expected!  A lot of things you can’t natively do with CSS3, and a lot of things that no one in their right mind should ever do…  As such, there are some filters that will not be represented here.  Remember: With great power comes great responsibility…

What CSS3 effects can I mimic using IE filters?
Here are the only CSS3 effects I have been able to (pretty well) mimic using IE filters:

  • opacity
  • background gradients (horizontal, vertical, and radial)
  • element rotation, including text
  • drop shadows, for both block elements and text

Below are the filters and their CSS3 counterparts.

AlphaImageLoader: http://msdn.microsoft.com/en-us/library/ms532969%28v=VS.85%29.aspx
(I actually only included this one because it is probably the one filter that every CSS person knows, because this is how you get PNGs with transparency to work in IE6/7.)
Displays an image within the boundaries of the object and between the object’s background and its content, with options to clip or resize the image.

filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled='true', src='image.png', sizingMethod='crop');
  • enabled: toggle effect
    • true (default)
    • false
  • src: url of image
  • sizingMethod:
    • crop (clips the image to fit the dimensions of the object)
    • image (default, enlarges or reduces the border of the object to fit the dimensions of the image)
    • scale (stretches or shrinks the image to fill the borders of the object)

Modern counterpart:

background: url('image.png') 0 0 no-repeat;

Alpha: http://msdn.microsoft.com/en-us/library/ms532967%28v=VS.85%29.aspx
Adjusts the opacity of the content of the object. Through combinations of starting and ending positions of linear opacities, you can create gradients that affect the object itself and everything inside the object, including “star burst” affects.  For linear gradients, however, the Gradient filter, below, is easier.

filter: progid:DXImageTransform.Microsoft.Alpha(opacity='50');
filter:  progid:DXImageTransform.Microsoft.Alpha(style='1', opacity='25', finishOpacity='100', startX='100', finishX='100', startY='100', finishY='0');
filter: progid:DXImageTransform.Microsoft.Alpha(style='2', opacity='25', finishOpacity='100');
filter: progid:DXImageTransform.Microsoft.Alpha(style='3', opacity='25', finishOpacity='100');
  • enabled: toggle effect
    • true (default)
    • false
  • style: type of effect to apply
    • 0 (default, applies uniform Opacity value evenly across the object)
    • 1 (applies a linear opacity gradient, beginning with the Opacity value on a line from StartX to StartY and ending with the FinishOpacity value on a line from FinishX to FinishY)
    • 2 (applies a radial opacity gradient, beginning in the center with the Opacity value and ending at the middle of the sides of the object with the FinishOpacity value. The corners of the object are not affected by the opacity gradient)
    • 3 (applies a rectangular opacity gradient, beginning at the sides of the object with the Opacity value and ending at the center of the object with the FinishOpacity value)
  • opacity: percentage of opacity to affect object
  • finishOpacity: percentage of opacity at the end of the gradient
  • startX: for linear style only, horizontal starting position of the opacity
  • finishX: for linear style only, horizontal ending position of the opacity
  • startY: for linear style only, vertical starting position of the opacity
  • finishY: for linear style only, vertical ending position of the opacity

Modern counterpart:

opacity: .5; /* FF3.5+, Saf3.1+, Chrome, Opera 10.5 */
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(opacity='50')"; /* IE8 */

BasicImage: http://msdn.microsoft.com/en-us/library/ms532972%28v=VS.85%29.aspx
Adjusts the color processing, image rotation, or opacity of the content of the object, including text.

filter: progid:DXImageTransform.Microsoft.BasicImage(rotation='3', mirror='0', invert='0', xray='0', grayscale='0', opacity='1.00');
  • enabled: toggle effect
    • true (default)
    • false
  • rotation:
    • 0 (no rotation)
    • 1 (90 degrees)
    • 2 (180 degrees)
    • 3 (270 degrees)
  • mirror: horizontal flip, stupid
    • 0 (default, false)
    • 1 (true)
  • invert: inverts all colors of anything inside the object, including images and text
    • 0 (default, false)
    • 1 (true)
  • xray: creates X-ray effect, stupid
    • 0 (default, false)
    • 1 (true)
  • grayscale: converts contents to grayscale, including images and text
    • 0 (default, false)
    • 1 (true)
  • opacity: decimal value for opacity (no, not a percentage, like Alpha…)

Modern counterpart:

-webkit-transform: rotate(270deg);  /* Saf3.1+, Chrome */
-moz-transform: rotate(270deg);  /* FF3.5+ */
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation='3')"; /* IE8 */
-o-transform: rotate(270deg); /* Opera 10.5 */
transform: rotate(270deg); /* W3C */

DropShadow: http://msdn.microsoft.com/en-us/library/ms532985%28v=VS.85%29.aspx
Creates a solid silhouette of the content of the object, offset in the specified direction.

filter:progid:DXImageTransform.Microsoft.DropShadow(color='#666666',offX='5',offY='5',
positive='true');
  • enabled: toggle effect
    • true (default)
    • false
  • color: hex color for shadow
  • offX: pixels to offset shadow on the x-axis, also dictates the “length” of the shadow
  • offY: pixels to offset shadow on the y-axis, also dictates the “length” of the shadow
  • positive: indicates whether the filter creates a drop shadow from the nontransparent pixels of the object (i wasn’t totally successful in getting this to work, but i can’t imagine wanting to use it either…)

Modern counterpart:

-webkit-box-shadow: 5px 5px 0 #666; /* Saf3.0+, Chrome */
-moz-box-shadow: 5px 5px 0 #666; /* FF3.5+ */
-ms-filter: "progid:DXImageTransform.Microsoft.DropShadow(color='#666666',offX='5',
offY='5')"; /* IE8 */
box-shadow: 5px 5px 0 #666; /* W3C */

Gradient: http://msdn.microsoft.com/en-us/library/ms532997%28v=VS.85%29.aspx
Displays a color gradient between the object’s background its and content (meaning an object’s background color will shine through the gradient).

filter: progid:DXImageTransform.Microsoft.Gradient(enabled='false', startColorstr='#666666', endColorstr='#ffffff');
  • enabled: toggle effect
    • true (default)
    • false
  • GradientType:
    • 0 (vertical)
    • 1 (horizontal)
  • startColorstr: hex color of starting gradient
  • endColorstr: hex color of ending gradient

Modern counterpart:

background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #666666), color-stop(1, #ffffff)); /* Saf4+, Chrome */
background-image: -webkit-linear-gradient(top, #666666, #ffffff); /* Chrome10+,Safari5.1+ */
background-image: -moz-linear-gradient(top, #666666, #ffffff); /* FF3.6+ */
background-image: -ms-linear-gradient(top, #666666, #ffffff); /* IE10+ */
background-image: -o-linear-gradient(top, #666666, #ffffff); /* Opera 11.10+ */
background-image: linear-gradient(to bottom, #666666, #ffffff); /* W3C */
-ms-filter: "progid:DXImageTransform.Microsoft.Gradient(startColorStr='#666666', EndColorStr='#ffffff')"; /* IE8 */

Shadow: http://msdn.microsoft.com/en-us/library/ms533086%28v=VS.85%29.aspx
Creates an extending silhouette of the content of the object, offset in the specified direction.  A slightly different effect than DropShadow, Shadow remains “connected” to the object.

filter: progid:DXImageTransform.Microsoft.Shadow(color='#666666', direction='135', strength='5');
  • enabled: toggle effect
    • true (default)
    • false
  • color: hex color for shadow
  • direction: in degrees, the direction the shadow should extend
  • strength: in pixels, the “length” of the shadow

Modern counterpart:

-webkit-box-shadow: 5px 5px 5px #666; /* Saf3.0+, Chrome */
-moz-box-shadow: 5px 5px 5px #666; /* FF3.5+ */
-ms-filter: "progid:DXImageTransform.Microsoft.Shadow(color='#666666', direction='135', strength='5')"; /* IE8 */
box-shadow: 5px 5px 5px #666; /* W3C */

How do I target CSS3 to modern browsers and Filters to IE?
There are essentially three methods:

  1. Conditional comments
  2. Child-selectors
  3. CSS hacks

Conditional comments are by far the “most acceptable” to the community, but I personally prefer not to have my CSS in multiple locations, I find debugging much easier when everything is in one place.  This method does, however, validate just fine, and you can easily target the exact version of IE to stop using filters and start using CSS3.

Child-selectors are also perfectly valid, but are a less consistent way to target specific versions of IE, when it comes to filters and CSS3.  This also often requires duplicate declarations, one for IE, then an override for modern browsers, which bloats your CSS and makes modern browsers work harder.  For example:

p a {background:url('image.gif') no-repeat;}
p > a {background:url('image.png') no-repeat;}

This makes modern browsers download the GIF then download the PNG… This is a simple example but hopefully you get my point…

I personally prefer the invalid CSS hacks for the few instances that I use IE-only code, like this:

p a {background: url('image.png') no-repeat; _background: url('image.gif') no-repeat;}

I know I can target specific versions of IE, everything is in one place, and I know modern browsers will ignore those declarations.  Sorry, but that’s my $.02.

Notes:

  • First of all, in order for filters to take effect, the object has to “have layout”…  :-)  The easiest, least intrusive way I have found to do that is to give elements a height; saying height:1; in your favorite IE-only method works well.
  • I have had varying results using “short” hex color values (#666 instead of #666666), so it is probably safer to simply use the long values
  • Obviously, with all of these various ways of declaring styles, your CSS can get pretty out of control, but if you think about being able to use raw text, and even @font-face, or dynamically-sized box-shadows, this is pretty powerful stuff!
  • To assign multiple filters, chain them all together in one filter declaration, with each filter separated by a space:
    filter: progid:DXImageTransform.Microsoft.BasicImage(rotation='3', opacity='1.00') progid:DXImageTransform.Microsoft.Gradient(startColorstr='#666666', endColorstr='#ffffff') progid:DXImageTransform.Microsoft.DropShadow(color='#666666', offX='5', offY='5');

    * When multiple filters are applied to an object, each filter is processed in cascading order. To emphasize a filter’s effect, place it last in your declaration.

  • I find DropShadow works best for box-shadows and Shadow works best for text-shadows; IE’s font rendering still sucks, so be careful with the strength of your shadow and the color you choose for it.
  • Like CSS opacity, the Alpha opacity affects all child elements, so, if you give a <div> an opacity of 80%, all of its children will also have an opacity of 80%; you cannot give the children an opacity of 120% (or whatever it would be) to rectify this…
  • It is important to consider performance when designing Web pages with filters. Processing time is required to calculate the display of filter effects, and some effects require more time than others to apply. It is useless to try to apply and change a filter on an object before the browser can even render it. If you apply and change a filter before the browser can render the object, the changes are not updated. For example, changing the Alpha filter’s Opacity property in a short looping script does not result in consecutive updates to the object. To ensure updates are performed for each cycle of the script, use an onfilterchange event handler to delay changes in opacity until the browser has rendered the last update.

Resources:

Thanks go to Snook for finally kick starting this idea I had with the above article; I had the idea some time back of exploring the CSS3/Filter similarities, but then seeing him turn his text sideways made me have to dig in, and to Paul Irish and Jonathan Neal for CSS3Please, without which collecting all the CSS3 bits would have been a real pain in the caboose…

Happy CSSing,
Atg

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.