css3 background transitions/and why it's a hack

I've never been a big fan of css animation (and I still have my doubts), but when it comes to purely presentational animation I feel it has a sound purpose. Used sparingly and in a way that enhances user feedback, I feel it can give your site a more polished edge. With the current redesign I've been experimenting left and right, but one thing I found lacking is support for background (image) transitions. Not satisfied with this obvious void I tried to find some ways around.

css transition

css transitions are a special kind of (automated) animation where css will animate between the different values of a single css property, triggered by a change in state of its base element. The most common and obvious use lies with :hover states (or related), applying small animations for hover colors and the like.

These transitions rely on numeric values to work though, which sadly is quite a limiting factor. It's still possible to transition between colors (as they can be presented by hexadecimal values) and the like, but transitioning between different background images is not an option yet. That's a real shame, since I wanted a small color fade effect on the icons in the main navigation of this blog. Lucky for me, there were some other options to explore.

introducing: opacity transitions

Re-using one of the older tricks (changing hmtl images on hover) it's actually quite easy to reach the intended result. The property that's going to help us with that is css opacity.

The idea is to place two layers right on top of each other. On hover, we'll use a css transition of the opacity value of the top layer, so the bottom layer will gradually begin to shine through. When the animation is complete the opacity of the top layer will be 0 and all we'll be able to see is the bottom layer (in other word, the hover state).

method 1: two structural elements

.outer {background:(hover-bg); position:relative;} .outer .inner {transition:opacity 0.2s linear; background:(bg);} .outer .inner {position:absolute; left:0; right:0; top:0; bottom:0;} .outer:hover .inner {opacity:0;}

As you can see in the above code, the hover background is set on the lowest plane. The inner element is positioned on top of the outer element so that both backgrounds will match exactly. On hover the opacity of the inner element will be reduced to 0, finally leaving us with the image of the hover background. Mind though that if you have other content nested inside the inner element this will disappear as well, so to be safe it's best to make a self-closing, empty element of .inner. Not very clean (I know), but it does the trick. Mind that you still need to add all the vendor prefixes on -transition to make this work in the different browsers.

This is 2011 though, so it wouldn't be quite alright to not try the :after/:before option for cleaner code.

method 2: one structural element

.base {background:(hover-bg); position:relative;} .base:after {transition:opacity 0.2s linear; background:(bg);} .base:after {position:absolute; left:0; right:0; top:0; bottom:0;} .base:hover:after {opacity:0;}

Ain't that lovely, a working example without the need for any extra structural elements. No hover support for ie7 or lower (of course), but a little javascript can easily fix that. Together with ie8 you'll also need the ie transparency filter to make it work (or just use visibility:hidden if you don't care to animate it).

But ... you know this was coming, right? Apparently Webkit (oh yeah, we develop for state of the art browsers now) does not support css transitions on pseudo-elements. There is probably a good reason (and this is not really a Webkit bash, which in turn supports quite a few things other browsers don't), but somehow I can't help but feel a sharp sting of disappointment that after all these years we're still dealing with partial and/or buggy implementations even in the latest browsers. Because let's face it, css transitions have been around for some time and are hardly experimental anymore.

conclusion

So if you're looking at this site through Chrome or Safari, you won't see the color fade in the logos (main navigation navigation). I could've opted to include an extra structural element but was too stubborn to go through that length for something that calls itself a modern, A-grade browsers. So for now Webkit users have a simple hover effect until Webkit fixes its lacking support for css transitions.

For more professional projects I would suggest using the first method though. It may not be very clean using an empty structural element (you can still insert it with javascript if you want), but at least it gets the job done across most modern browsers.