Graceful Degradation With CSS3

Graceful Degradation With CSS3

With IE9 in development and Opera 10.5 released, CSS3 is a few steps away from being supported by all modern browsers. But users may take a while to upgrade, and responsible developers will support legacy browsers for years to come.

For some, cross-browser development means making websites that look exactly the same in all browsers. But if developers continue to cater websites to inadequate browsers such as IE6, then they’re just holding back modern browsers from performing to their potential.

Graceful degradation is not about allowing websites to look bad in older browsers, but about making them look great in modern ones. It means taking advantage of CSS3’s useful features to progressively enhance web pages for the vast majority of users.

Deciding What’s Disposable

Fortunately, most CSS3 properties degrade fairly nicely on their own. Rounded corners become square, gradients become flat colors, and transitions become hard changes.

Peripheral elements in a web design can generally afford to degrade naturally, while core elements cannot. Deciding which CSS3 features are expendable is a three-step process:

  1. Determine which elements are visually essential. Could text-shadows drop off or rounded corners be made square?
  2. If styling is going to drop off, determine in which browsers degradation is acceptable. Make this decision based on the website’s usage across different browsers and versions. This is one of those instances when analytics (or historical data in the absence of analytics) should inform website design.
  3. Finally, pay particular attention to UI elements. Are CSS3 properties being used to convey crucial interface elements, such as clickable buttons and active states?

Supporting What’s More Important

Websites usually have at least a few visual elements that are essential to their design. Fortunately, most CSS3 features are easily supported across all major browsers. While image support is always an option, CSS3 styles can usually be supported without relying on images.

Background Gradients

CSS3 Gradients in Webkit
Background gradients can be rendered in IE using the IE gradient filter.

First, let’s build a button with a CSS background gradient that works in Firefox, Chrome and Safari:

.button {
    background-color: #ED9C47; /* fallback flat color */
    background-image: -webkit-gradient(linear, left top, left bottom,
    from(#EFB67B), to(#DB7306) ); /* for Chrome + Safari */
    background-image: -moz-linear-gradient(  top,
    #EFB67B, #DB7306 ); /* for Firefox */
}

Next, let’s extend this example to IE. We’ll want to call this next bit of CSS for IE only; by using a browser conditional, for instance.

/* call this in IE only */
.button {
    filter: progid:DXImageTransform.Microsoft.gradient(
        startColorstr='#EFB67B', endColorstr='#DB7306');
}

Although useful, IE’s gradient filter has a couple of drawbacks: it doesn’t support color stop, and it doesn’t support angled or radial gradients. For these, you’ll have to stick to image support. Additionally, this method works only for backgrounds, not borders, text, etc.

Beyond IE, background gradients can be supported in Opera via canvas.

RGBa Transparency

RGBa transparency vs. opacity

In most cases, gracefully degrading RGBa colors simply means including fallback RGB or Hex colors:

.button {
    background-color: rgb( 50, 200, 150 ); /* fallback color */
    background-color: rgba( 50, 200, 150, .8); /* RGBa color */
}

But a number of tricks are available for supporting alpha transparency as well. First and probably most obvious is CSS opacity. But opacity has a number of drawbacks, namely that it is inherited by child elements.

For example, let’s support RGBa using opacity in IE:

.button {
    background-color: rgb( 50, 200, 150);
    filter: alpha(opacity = 80);
}

This gives our button’s background color the correct opacity of 80%, but this opacity is in turn inherited by all of its descendants, such as the button’s text.

Fortunately, we can get alpha transparency working properly in IE using the same IE filter that we applied to background gradients:

/* call this in IE only */
.button {
       background: transparent;
       filter: progid:DXImageTransform.Microsoft.gradient(
           startColorstr='#8032c896', endColorstr='#8032c896');
       zoom: 1;
}

The trick here is that IE’s gradient filter accepts color inputs with alpha values, so we simply set both the start and end colors to our desired RGBa color. But the color format is a bit strange: it starts with 80 for 80% transparency, followed by the Hex value 32c896 for rgb( 50, 200, 150).

Finally, make sure to set the background to transparent, otherwise the background color will show beneath the alpha transparency.

Rounded Corners

CSS rounded corners will soon be available in all modern browsers
Rounded corners can be achieved in IE using a simple .htc hack.

Let’s start by rounding corners in Firefox, Chrome, Safari and Opera:

.button {
    -webkit-border-radius: 10px; /* for Chrome + Safari */
    -moz-border-radius: 10px; /* for Firefox */
    -khtml-border-radius: 10px; /* for Konqueror */
    border-radius: 10px; /* for Opera 10.5, IE9 and future-proofing */
}

Next, let’s incorporate the .htc hack for IE.

First, upload Curved Corner to your website, and include this extra bit of CSS for IE only:

/* call this in IE only */
.button {
    behavior: url(/your-path-to/border-radius.htc);
}

This hack is handy but comes with a major caveat: it can only round all four corners equally. To round each corner differently, sticking to image support is best. Image support is also best for elements whose dimensions change dynamically.

Finally, be warned that this .htc hack runs in JavaScript and has performance disadvantages compared to CSS support.

Box Shadows and Text Shadows

Firefox vs. IE shadow implementations
Box shadows can be supported in IE using the shadow filter.

Let’s start with a box-shadow for Firefox, Chrome, Safari and Opera:

.button {
    -webkit-box-shadow: 5px 5px 5px #777; /* for Chrome + Safari */
    -moz-box-shadow: 5px 5px 5px #777; /* for Firefox */
    box-shadow: 5px 5px 5px #777; /* for Opera 10.5, IE9
        and future-proofing */
}

Now, let’s extend this with some CSS for IE specifically:

/* call this in IE only */
.button {
    zoom: 1;
    filter: progid:DXImageTransform.Microsoft.Shadow(
        color='#777777', Direction=135, Strength=5);
}

In this filter, color lines up with box-shadow’s color setting, but Direction and Strength are a bit different.

Direction needs to be calculated in degrees from the two offsets. In this example, we used 135 for 135° from the twelve o’clock position, which falls off to the bottom right. Unfortunately, the angle isn’t very precise and rounds to the 45° mark. So, 150 becomes 135°, and 170 becomes 180°.

Strength is the shadow offset distance in pixels, with an equal blur value. In our example, the shadow falls off 5 pixels to the bottom-right, with a blur of 5 pixels.

The differences in the Direction and Strength settings present a major drawback for IE’s shadow filter: the settings remain attached to the element and can’t be used with more advanced offset or blur values.

The same shadow filter can also be used to apply text shadows by simply setting the element’s background to transparent. But the lack of proper offset values tends to make these look harsh.

A more subtle technique for text shadows relies on IE’s glow and blur filters, but unfortunately they still fall short visually.

Transitions

Transitions are one of the most useful features available in CSS3, and luckily they degrade pretty nicely on their own. Let’s use transitions to build a hover animation for our button:

.button {
    width: 200px;
    height: 50px;
    -webkit-transition-property: width, height; /* Chrome + Safari */
    -webkit-transition-duration: .5s;
    -webkit-transition-timing-function: linear;
    -moz-transition-property: width, height; /* FF4 future-proofing */
    -moz-transition-duration: .5s;
    -moz-transition-timing-function: linear;
    -o-transition-property: width, height; /* Opera 10.5+ */
    -o-transition-duration: .5s;
    -o-transition-timing-function: linear;
}
.button:hover {
    width: 300px;
    height: 75px;
}

This animation works in Chrome, Safari and Opera, but not in current versions of IE or Firefox. But even in the unsupported browsers, the transition degrades automatically to the basic hover states.

We’ll have to use more than simple CSS if we want to extend the animation to IE and Firefox. Let’s use jQuery to build the animation, and let’s start by determining whether the current browser supports transition:

<script src="http://code.jquery.com/jquery.js" type="text/javascript">
</script>
<script type="text/javascript">
// make sure to execute this on page load
$(function() {
    // determine if the browser supports transition
    var thisStyle = document.body.style,
    supportsTransition = thisStyle.WebkitTransition !== undefined ||
        thisStyle.MozTransition !== undefined ||
        thisStyle.OTransition !== undefined ||
        thisStyle.transition !== undefined;
}
</script>

Here, we’re checking the document style object to see whether transition is supported (in any of its various browser prefixes). We could have used the jQuery browser object, but feature detection is a much better approach than browser sniffing.

Next, let’s attach a hover event to our button that triggers the transition animation:

// make sure to execute this on page load
$(function() {
    // determine if the browser supports transition
    var thisStyle = document.body.style,
    supportsTransition = thisStyle.WebkitTransition !== undefined ||
        thisStyle.MozTransition !== undefined ||
        thisStyle.OTransition !== undefined ||
        thisStyle.transition !== undefined;
    // assign jQuery transition if the browser doesn't support
    if ( ! supportsTransition ) {
        var defaultCSS = {
            width: 200,
            height: 50
        },
        hoverCSS = {
            width: 300,
            height: 75
        };
        
        // loop through each button
        $('.button').each(function() {
            var $thisButton = $(this);
            
            $thisButton.hover(function() {
                // execute this on mouseover
                $thisButton.css(defaultCSS)
                    .animate(hoverCSS, 500, 'linear' );
            }, function() {
                // execute this on mouseout
                $thisButton.animate(defaultCSS, 500, 'linear' );
            });
        });
    }
});

Here, we’re applying our hover and default CSS using jQuery’s animate() API. To match our CSS3 transition, we’ve set the animation duration to 500 milliseconds (or .5s) and assigned the “linear” easing method.

One thing to notice is that on mouse-over, we apply the default CSS first using jQuery’s css() API, and then we call animate() with the hover CSS. We do this to override the :hover pseudo-class that we applied in our CSS.

Now, our CSS transition works across all major browsers. This script can be modified to build transitions for any CSS property that is supported by jQuery.animate().

This technique works with any numeric CSS attribute. Additionally, jQuery UI extends the animate() function to support non-numeric attributes, such as color.

Final Thoughts

Although still imperfect, CSS3 support is now available in all modern browsers except IE.

IE’s filters and .htc hacks fill in most of the gaps but come with their own set of drawbacks. They are generally pretty quirky, and problems arise when trying to combine multiple filters. Additionally, these browser hacks are much slower than pure CSS alternatives.

Ultimately, hacks for supporting CSS3 in unsupported browsers will always fall a bit short. But this shouldn’t hold back websites from performing as well as they should in more modern browsers.

Simply put, websites don’t need to be experienced exactly the same way in every browser.



Jon Raasch

Jon Raasch is a UX nerd and free-lance web designer / developer who loves jQuery, Javascript & CSS.




You Might Also Like:


14 Comments to “Graceful Degradation With CSS3”

  1. thanks, this is a really nice post.

  2. Yes nice post and the way to go actually when dealing with multiple browsers using CSS3.

  3. Marcus Tucker says:

    Great post, and with regard to animating position, width/height, and opacity, there’s a great jQuery plugin that provides an alternative approach, automatically using CSS3 where animation support is present:
    http://playground.benbarnett.net/jquery-animate-enhanced/

  4. Nada says:

    Agreed. Developing for newer browsers might also encourage people to upgrade.

  5. Ben Wilson says:

    In the “RGBa” section, in discussing “filter: progid:DXImageTransform.Microsoft.gradient”, the 80 for the alpha value is NOT 80%, but is actually 0x80, where 0x00 is transparent and 0xff is opaque.

    See http://msdn.microsoft.com/en-us/library/ms532930%28VS.85%29.aspx

  6. Abu farhan says:

    Thanks for share about css3, I like this.

  7. Jeremy says:

    Nice…

  8. SPINX INC. says:

    Bingo !! You have included the perfect concept for creating a better web design and added feasible web style for common internet users. It’s not possible that every internet user works on a modern browser hence , a web designer has to build a web design by including degradation CSS3 that can run on normal versions of major web browsers. I really appreciate the information that you have shared here.



Comments are closed.