HTML5 & CSS3: Graceful Degradation

Webkit animations and transitions are a nifty way to add sexy effects and animations to browsers that use the webkit engine. As the list of browsers that implement these becomes longer (Safari, Chrome, Mobile Safari, Blackberry 6.0, Android, webOS, et al)—especially in the mobile space—the more you can rationalize using some of these features in production. The key words for things like this are "graceful degradation" i.e., use it, but provide a fallback solution for other browsers that will still engage the user. The idea is for the user to not think that something is missing or broken—they are still provided an engaging experience.

An example of this we created recently is a mobile-optimized HTML5 site for First Bank. At the moment it's a place to download free books in PDF form. We'll be adding more activities soon ;) On the books page, though, we use @webkit-keyframes to create a 3D transition effect when changing pages and a Mac-style "bouncing icon in the dock" effect when a book is clicked (try it in Chrome or Safari). In other browsers (Firefox, IE, etc), Javascript is used to create a simple tween animation to show the next page of books. So we give "fancy-ass" browsers some cool effects, but still give the average crowd a nice animation as well.

Here's how we created the Mac icon dock bounce. First, you define a series of keyframes that will define our animation. This is just in your CSS file—I know it looks kind of crazy when you're thinking CSS:

@-webkit-keyframes book-active {
    from {top:0px;}
    to {top:-10px;}
}

Pretty simply, we've defined an "animation name" called book-active. This animation would move whatever element it is applied to from 0 to -10. To run the animation, create a class with a similar name (I find it helps organize) and add the animation-name to it:

.book-active {
     -webkit-animation-name:book-active;
     -webkit-animation-duration:0.2s;
     -webkit-animation-iteration-count:10;
     -webkit-animation-direction:alternate;
}

You can see above we gave it an "animation-name" of "book-active". The animation will take 0.2 seconds to complete and it'll repeat the animation 10 times. The animation-direction parameter tells it to reverse the animation for each interaction. Hence, we bounce.

To apply the animation, you simply add the class to the element:

<article class="book-active"></article>

On the First Bank micro-site, we applied two separate bounce animations to the books. One to bounce the book element, and one to scale the shadow underneath the book on the same bounce to give it some depth. Here's the whole CSS section for the book bounce, shadow, and the 3D transform during the page change animation:

@-webkit-keyframes book-active {
    from {top:0px;}
    to {top:-10px;}
}

@-webkit-keyframes shadow-bounce {
    from {-webkit-transform:scale(1.0);}
    to {-webkit-transform:scale(1.05);}
}

@-webkit-keyframes book-transition {
    from {
        -webkit-transform:scale(1.0);
        -webkit-animation-timing-function:ease-out;
    }
    20% {
        -webkit-transform:scale(0.9);
        webkit-animation-timing-function:ease-in;
    }
    to {
        -webkit-transform:scale(1.0);
        -webkit-animation-timing-function:ease-out;
    }
}

.book-active {
     -webkit-animation-name:book-active;
     -webkit-animation-duration:0.2s;
     -webkit-animation-iteration-count:10;
     -webkit-animation-direction:alternate;
}

.book-transition {
    -webkit-animation-name:book-transition;
    -webkit-animation-duration:1s;
    -webkit-animation-iteration-count:1;
}

.shadow-bounce {
     -webkit-animation-name:shadow-bounce;
     -webkit-animation-duration:0.2s;
     -webkit-animation-iteration-count:10;
     -webkit-animation-direction:alternate;
}

One caveat for animation with webkit, is that repeating animations is tricky. Once the class has been added, you need to remove the animation-name from the element to restart the animation. We created this simple jQuery element function to help:

$.fn.startWebkitAnimation = function(name)
{
    var element = $(this);
    if (!element.hasClass(name))
    {
        element.addClass(name);
    }
    else
    {
        element.css("-webkit-animation-name", "none");
        setTimeout(function() {
            element.css("-webkit-animation-name", name);
        }, 0);
    }
    return $(this);
}

So to start an animation, simply call $("selector").startWebkitAnimation("animation-name"). The CSS class and the keyframes should have the same name. What this does is clear the animation-name property and then reset it. This is based on Apple's own recommendation for resetting animations.

On First Bank Free, we use Modernizr to check if CSS Animations are available. If they are, we start the bouncing animation when the book is clicked and delay the transition to the link. Otherwise, we go right to the link (graceful degradation!):

$("#book-list li a").click(function(event)
{
    if ($(this).data("was-clicked") != undefined)
    {
        // prevent multiple clicks
        event.preventDefault();
        return;
    }
    if (Modernizr.cssanimations)
    {
        event.preventDefault();
        // prevent multiple clicks
        $(this).data("was-clicked", 1);
        // start the book bouncing
        $(this).startWebkitAnimation("book-active");
        // start the shadow bouncing
        $(this).parent().find(".book-shadow").startWebkitAnimation("shadow-bounce");
        // delay the page direction for a tick
        setTimeout('window.location = "' + $(this).attr("href") + '"', 1000);
    }
});

So, you can see how HART (HTML5 and Related Technologies) can be used right now with a little extra effort. The keys are fallbacks and degradation. Also—and I find this to be critical—definitely let your clients know up front what to expect for different browser vendors. This might be defined in functional specification or proposal before development begins.

October 2nd, 2010 | Permalink

Simple Rounded Buttons in Opera with SVG-infused CSS

One of the important considerations of the new typeoneerror.com was that I'd not care as much cross-browser rendering issues or, more pointedly, "how things look in IE." That being said, I used many fun CSS3-style techniques such as border-radius:

-moz-border-radius:8px;
-webkit-border-radius:8px;

Safari (and Google Chrome, being both based on Webkit) and Firefox render my buttons and highlighted navigation buttons with rounded edges beautifully, and the rest of you get squares – it degrades pretty nicely. I did want to know whether Opera had any support for these "hacks." I'd heard of -opera-border-radius and -o-border-radius but they didn't work for me. The latest alpha version of Opera has full support for border-radius, but here's how you can achieve the exact same effect using simple SVG and pointing to those files in your CSS.

Our goal is to approximate the look created by our navigation button in browsers that support border-radius:

/* part of our navigation button definition **/
#nav ul li a {
    background-color:#03FFC1;
    border-radius:8px;          /* CSS3 */
    -khtml-border-radius:8px;   /* Some linux-based browsers */
    -moz-border-radius:8px;     /* Firefox > 3.1 */
    -webkit-border-radius:8px;  /* Safari */
}

Using XML-defined SVG, you can define a shape that you can use as a background image in CSS. Here, for example, is the definition of an SVG shape for the highlighted navigation button. Here's what the file I created (nav.svg) looks like:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
    <rect x="0" y="0" rx="8" ry="8" height="100%" width="100%" fill="#03FFC1" />
</svg>

Quite simply, this defines a rectangle that will scale (width/height 100%) to fill our background and has a corner radius (rx/ry) of 8. Now if I wanted to apply this to my nav buttons above:

/* use css "hacks" to target opera */
@media all and (-webkit-min-device-pixel-ratio:10000),not all and (-webkit-min-device-pixel-ratio:0){
    #nav ul li a {
        background:url(../img/nav.svg);
    }
}

You simply have to point the background of the element to the svg file just like you would an image. Won't be long now until all major browsers support border-radius; won't that be nice. I also looked in to getting something going for Internet Explorer and found this behavior script for IE that works really well. I'd only recommend it if you only have a few elements that need round edges. On the pages where I have a lot of buttons that have round corners, it took up to 10 seconds to process. Interesting idea though.

December 21st, 2009 | Permalink