CSS3 linear gradients and Internet Explorer

This post is more than 14 years old.

CSS3 has been tempting me with linear gradients for a while now. They don’t work in Internet Explorer, but there are ways and means with a little script magic. Now that Opera has finally joined the party, I figured it was time to ditch those ever pervasive linear gradient background images and start using CSS3 for linear gradients. But it’s not all rosy, especially when you need to position your background.

I guess the first question someone might ask is, “what’s wrong with using images?” The problem is that they have a fixed size, and when you need to fit a top-to-bottom linear gradient to an area that can change in size, you face some problems. Scalable linear gradients get around all that. Besides, they’re fiddly, and one more thing to send down to the browser.

But Internet Explorer, even IE9, doesn’t implement CSS3 linear gradients, so one must resort to IE-specific hacks — again. There are three ways that I know of to create linear backgrounds on Internet Explorer without loading a fixed-size image:

Now, I love css3pie; it does an excellent job of rendering rounded corners, box shadows and linear gradients on IE 6-8. But it has its limitations, and I hit them recently on a website we’re building.

example of linear gradient offset from top and leftFirst and foremost, it can’t position the linear gradient. This is something we needed to do for the example shown, where we had to render WordPress widgets with little banners offset from the linear gradient backgrounds. With CSS3, we accomplished that by positioning the linear gradient offset from the top and left.


#sidebartwo p.borders, #sidebartwo ul {
    list-style-type:none;
    margin:0 0 19px 0;
    padding:0 0 0 6px;
    text-align:left;
    border-left:1px solid #D6D0C2;
    border-bottom:1px solid #D6D0C2;
    display:table;
    width:200px;

    /* default background colour, for all layout engines that don't implement gradients */
    background-color:#E4E4D9;

    /* gecko based browsers -- Firefox from 3.6 */
    background:Transparent -moz-linear-gradient(top, #C1C688, #ffffff) no-repeat 9px 6px;

    /* opera from 11.10 */
    background:Transparent -o-linear-gradient(top, #C1C688, #ffffff) no-repeat 9px 6px;

    /* webkit based browsers */
    background:Transparent -webkit-gradient(linear, left top, left bottom, from(#C1C688), to(#ffffff)) no-repeat 9px 6px;

    /* standard */
    background:Transparent linear-gradient(top, #C1C688, #ffffff) no-repeat 9px 6px;
}

Secondly, and more dramatically, I discovered that IE6 just can’t take too much complexity before it crashes in a heap. The more complexity in your css3pie and page content, the more likely IE6 is to crash. Maybe this problem is resolved with some Microsoft hotfix or other, because it doesn’t happen for the developer of css3pie, but it does happen for others using basic Windows XP installs with service pack 3, which is the best you can hope for “in the wild”.

So, for IE, I had to resort to using DirectX filters and some script to place a DIV element behind the widget content. An added benefit of using DirectX is that my linear gradients also work in IE9, something that the css3pie crew are still working on at the time of writing. Here are the gory details in vibrant JavaScript.

var i, len, xoxo = /bxoxob/, divs, zone, widget, widgets, widgetStyle, bg, bgStyle,
    widgetbar = document.getElementById("sidebartwo");

// If we have a widget sidebar and we're running in IE, then add a div for each widget
// so that we can have positioned 2D gradient backgrounds
if (widgetbar && detectClass.ie) {
    try {

        // look for div with class "xoxo", that's the widget zone
        divs = widgetbar.getElementsByTagName("div");
        for (i = 0, len = divs.length; i < len; i++) {
            if (xoxo.test(divs[i].className)) {
                // got the widget zone
                zone = divs[i];
                zone.style.zIndex = "1";

                // process the widgets, each being a UL element directly under the widget zone element
                widgets = zone.childNodes;
                for (i = 0, len = widgets.length; i < len; i++) {
                    if (widgets[i].tagName == "UL") {

                        // make the widget position relative, so can be z-indexed
                        widget = widgets[i];
                        widgetStyle = widget.style;
                        widgetStyle.position = "relative";
                        widgetStyle.zIndex = "20";
                        widgetStyle.background = "none";

                        // when using the DirectX approach, must also set 100% opacity for some unknown reason!
                        widgetStyle.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=100)";

                        // create a div element for positioning the background, and position it "just so" to match the main CSS
                        bg = document.createElement("div");
                        bgStyle = bg.style;
                        bgStyle.position = "absolute";
                        bgStyle.top = (widget.offsetTop + 6) + "px";
                        bgStyle.left = (widget.offsetLeft + 8) + "px";
                        bgStyle.height = (widget.offsetHeight - 6 * 2) + "px";
                        bgStyle.width = (widget.offsetWidth - 8 * 2) + "px";
                        bgStyle.zIndex = "10";

						// give it a gradient to match the gradient backgrounds in the main CSS, using DirectX
						bgStyle.filter = "progid:DXImageTransform.Microsoft.Gradient(startColorstr='#C1C688', endColorstr='#ffffff', gradientType='0')";

                        // add the new gradient background div to the widget zone
                        zone.appendChild(bg);
                    }
                }
                break;
            }
        }
    }
    catch(e) {
        // NOP
    }
}

Oh, but IE6 still crashed. So I’ve had to ditch css3pie box shadow on IE6 in favour of the equivalent DirectX filters there. I still use css3pie for IE7 and IE8 because it renders properly, and IE9 purports to do box shadow (badly, IMHO), but IE6 has to go it alone with DirectX. It ain’t as pretty, but it’s close enough, and it’s only IE6 after all…

#page {
    filter: progid:DXImageTransform.Microsoft.Shadow(color='#999999', Direction=45, Strength=4)
            progid:DXImageTransform.Microsoft.Shadow(color='#999999', Direction=135, Strength=4)
            progid:DXImageTransform.Microsoft.Shadow(color='#999999', Direction=225, Strength=4)
            progid:DXImageTransform.Microsoft.Shadow(color='#999999', Direction=315, Strength=4);
}

So. Job is done. But not without a serious dose of mental anguish. I guess CSS3 linear gradients might be worth it, but it does feel a little like hard work right now.