Animating Colors: Using CSS3 Transitions with jQuery Fallback

Animating Colors Using CSS3 Transitions with jQuery Fallback

Have you ever wanted to animate the color of an element (e.g. font color) on mouse hover? You might think that it can be easily done with jQuery, but jQuery doesn’t support animating color changes at all unless a plugin or jQuery UI is used. What you need is a quick and easy way to add simple color animation to an element, and that’s where CSS Transitions come into the picture. Transitions are a part of the draft CSS3 specification and provide a means to animate changes in CSS properties rather than having those changes take effect instantaneously.

It’s an easy way, but it has one drawback, it’s only supported by modern browsers, however you can’t leave users on old browsers alone, so what you need is some graceful degradation for browsers that don’t support CSS transitions. In this posting we will be taking a look at how we can animate colors using CSS3 transitions and jQuery’s .animate() method as a fallback where transitions aren’t supported by the browser.

Animating Colors Using CSS3 Transitions with jQuery Fallback

[tut demo=”” download=””]

Why Not Use jQuery?

You might be wondering why not just stick with jQuery for animating? For years, web designers have relied on JavaScript for in-page animations but this is far from ideal. The reason that we are going use CSS3 transitions is that unlike regular JavaScript transitions, CSS3 transitions are hardware accelerated and therefore smoother, Especially on mobile devices with limited computing power, this can be a lifesaver.

According to W3C, CSS Transitions allow property changes in CSS values to occur smoothly over a specified duration. This smoothing animates the changing of a CSS value when triggered by a mouse click, focus or active state, or any changes to the element (including even a change on the element’s class attribute).

For example, you may define a transition for the position and background-color properties and then set both property values of an element at the same time. The element’s old color and position transition to the new color and position over time as illustrated in this image:

Image credit: Apple

So, let’s create an element with a certain CSS and then use the :hover pseudo-class to change the font color. we’ll use a transition to smooth out that change—an effect previously only possible using JavaScript or Flash, but now possible with a few simple lines of CSS.

The markup is a simple hyperlink, like so:

<a href="" class="test" >Transition me!</a> 

Next, we’ll add a declaration for the normal link state followed by the font color swap to blue on hover:

a.test {color:#AAA;}  
a.test:hover {color:#F00;}

Now let’s add a transition to that font color change. This will smooth out and animate the difference over a specified period of time.

a.test {
  color: #AAA;
  -moz-transition-property: color;  /* FF4+ */
  -moz-transition-duration: 1s;
  -webkit-transition-property: color;  /* Saf3.2+, Chrome */
  -webkit-transition-duration: 1s;
  -o-transition-property: color;  /* Opera 10.5+ */
  -o-transition-duration: 1s;
  -ms-transition-property: color;  /* IE10? */
  -ms-transition-duration: 1s;
  transition-property: color;  /* Standard */
  transition-duration: 1s;

a.test:hover {
  color: #F00;

Now let’s combine all those values into a shorthand declaration to simplify the declaration:

/* shorthand notation for transition properties */
transition: [transition-property] [transition-duration] [transition-timing-function] [transition-delay];

a.test {
  color: #F00;
  -moz-transition: color  1s;
  -webkit-transition: color 1s;
  -o-transition: color  1s;
  -ms-transition: color 1s;
  transition: color 1s;

a.test {
  color: #AAA;

It’s essential to use the appropriate vendor prefixes therefore our transitions will work in more browsers as support is rolled out. You’ll notice the two parts of a transition in the declaration:

transition-property: Specifies the name or names of the CSS properties to which transitions should be applied.

transition-duration: Specifies the duration over which transitions should occur. You can specify a single duration that applies to all properties during the transition, or multiple values to allow each property to transition over a different period of time.

There are also two other properties which we didn’t use in the example:

transition-delay: Delay before the transition should start, specified in seconds, e.g. 1s

transition-timing-function: The value of timing function allows you to change the speed of the transition over time by defining one of six possibilities: ease, linear, ease-in, ease-out, ease-in-out, and cubic-bezier (which allows you to define your own timing curve).

Simply try each of these timing function values to see how they differ. The default value is ease and should work just fine for short transitions. They’re a relatively recent feature, This works great for people with modern browsers but users who are still on older browsers won’t be able to view them as animations. CSS3 transitions are implemented on the following browsers:

  • Safari 3.2+ (introduced 2008)
  • Firefox 4+ (2009)
  • Google Chrome 7+ (introduced 2010)
  • Opera 10.50+ (introduced 2010)

FireFox 3.7 was proposed to launch with CSS transition support, But For some reason, FireFox canceled the launch and announced 3.7 would enter production as version 4.

Although not all browsers support CSS transitions, the fact that they’re declared separately from the properties that are changing means that those changes will still be apparent in browsers without support for transitions. The browsers that lack support for CSS transitions will still apply the :hover (or other) state just fine, except that the changes will happen instantly rather than being transitioned over time.

To provide the same effect as CSS3 transitions on older browsers, we are going to use JavaScript to provide support only for those browsers that lack native support, but instead of handling all of the cross-browser challenges of smooth animation ourselves, we can make use of jQuery’s .animate() features to provide the same animation effect if it can’t be done in the current browser using CSS3.

Here is our jQuery’s function:

    $(".test").hover(function () {
	$(this).stop().animate({ color: "#F00" },700)
    }, function() {
    $(this).stop().animate({ color: "#AAA" },700)}	 

As I said earlier, jQuery doesn’t support animating color changes, for this to work we will use the jQuery UI to add this functionality, but we don’t need all the code and it seems like overkill at +200kb. We can grab just what we need, so if we want to animate color, all we’ve got to include is the following code. I got if from the latest jQuery UI (currently 1.8.14)

// override the animation for color styles
$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor',
	'borderRightColor', 'borderTopColor', 'borderColor', 'color', 'outlineColor'],
function(i, attr) {
    $.fx.step[attr] = function(fx) {
	if (!fx.colorInit) {
	    fx.start = getColor(fx.elem, attr);
	    fx.end = getRGB(fx.end);
	    fx.colorInit = true;
	}[attr] = 'rgb(' +
	    Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + ',' +
	    Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + ',' +
	    Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ')';

// Color Conversion functions from highlightFade
// By Blair Mitchelmore

// Parse strings looking for color tuples [255,255,255]
function getRGB(color) {
    var result;

    // Check if we're already dealing with an array of colors
    if ( color && color.constructor == Array && color.length == 3 )
        return color;

    // Look for rgb(num,num,num)
    if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
	return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];

    // Look for rgb(num%,num%,num%)
    if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
	return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];

    // Look for #a0b1c2
    if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
	return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];

    // Look for #fff
    if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
	return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];

    // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
    if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
	return colors['transparent'];

    // Otherwise, we're most likely dealing with a named color
	return colors[$.trim(color).toLowerCase()];

function getColor(elem, attr) {
    var color;
    do {
        color = $.curCSS(elem, attr);

	// Keep going until we find an element that has color, or we hit the body
	if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") )

	attr = "backgroundColor";
    } while ( elem = elem.parentNode );

    return getRGB(color);

It’s only 1.43kb after compressing with YUI:

$.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","borderColor","color","outlineColor"],function(b,a){$.fx.step[a]=function(c){if(!c.colorInit){c.start=getColor(c.elem,a);c.end=getRGB(c.end);c.colorInit=true}[a]="rgb("+Math.max(Math.min(parseInt((c.pos*(c.end[0]-c.start[0]))+c.start[0],10),255),0)+","+Math.max(Math.min(parseInt((c.pos*(c.end[1]-c.start[1]))+c.start[1],10),255),0)+","+Math.max(Math.min(parseInt((c.pos*(c.end[2]-c.start[2]))+c.start[2],10),255),0)+")"}});function getRGB(b){var a;if(b&&b.constructor==Array&&b.length==3){return b}if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b)){return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)]}if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b)){return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55]}if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b)){return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16)]}if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b)){return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)]}if(a=/rgba\(0, 0, 0, 0\)/.exec(b)){return colors.transparent}return colors[$.trim(b).toLowerCase()]}function getColor(c,a){var b;do{b=$.curCSS(c,a);if(b!=""&&b!="transparent"||$.nodeName(c,"body")){break}a="backgroundColor"}while(c=c.parentNode);return getRGB(b)};

Next, we use an open source JavaScript library called Modernizr, it allows us to test for individual features of HTML5 and CSS3 in our users browsers. Instead of testing just for a particular browser and trying to make decisions based on that, Modernizr allows us to ask specific questions like: “Does this browser support CSS3 transitions?” and receive a clear “yes” or “no” answer.

Again we don’t need the entire library, fortunately new in Modernizr2 is the ability to customize your JavaScript download, So now if you do not care for a particular feature (let’s say Drag and Drop), you can unclick it and not have Modernizr check for it. Read more about it here.

Here is the code we need from the Modernizr library. It’s only 1kb (Modernizr 2.0.6 – Custom Build)

;window.Modernizr=function(a,b,c){function z(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1),d=(a+" "+m.join(c+" ")+c).split(" ");return y(d,b)}function y(a,b){for(var d in a)if(j[a[d]]!==c)return b=="pfx"?a[d]:!0;return!1}function x(a,b){return!!~(""+a).indexOf(b)}function w(a,b){return typeof a===b}function v(a,b){return u(prefixes.join(a+";")+(b||""))}function u(a){j.cssText=a}var d="2.0.6",e={},f=b.documentElement,g=b.head||b.getElementsByTagName("head")[0],h="modernizr",i=b.createElement(h),,k,l=Object.prototype.toString,m="Webkit Moz O ms Khtml".split(" "),n={},o={},p={},q=[],r,s={}.hasOwnProperty,t;!w(s,c)&&!w(,c)?t=function(a,b){return,b)}:t=function(a,b){return b in a&&w(a.constructor.prototype[b],c)},n.csstransitions=function(){return z("transitionProperty")};for(var A in n)t(n,A)&&(r=A.toLowerCase(),e[r]=n[A](),q.push((e[r]?"":"no-")+r));u(""),i=k=null,e._version=d,e._domPrefixes=m,e.testProp=function(a){return y([a])},e.testAllProps=z;return e}(this,this.document);

Now we are able to fall back with the following code and serve up jQuery powered animations to browsers which don’t support CSS3 Transitions:

if (!Modernizr.csstransitions) {
	$(".test").hover(function () {
	    $(this).stop().animate({ color: "#F00" },700)
	}, function() {
	    $(this).stop().animate({ color: "#AAA" },700)}	 

[tut demo=”” download=””]

That’s all, folks!

Like all shiny new tools, use it with care and caution. One can simply go overboard adding color transitions to a lot of elements on the page, resulting in some sort of annoying, pulsating monster. It’s important to use CSS transitions where it makes sense, enhancing the user experience—and skip it everywhere else. Additionally, making sure the speed of the transition doesn’t slow down an otherwise snappy action from the user is crucial. It’s important to use transitions appropriately.

Read More

Between web design and development, UX freelancing, writing, and android programming, Faraz Karimian has an increasingly hard time deciding what to say in short bios.


  1. / Reply

    really helping technique you sharing in this article. thanks

  2. / Reply

    A really useful tutorial, I didn’t realize the css transitions were so simple to use. :) Many thanks.

  3. / Reply

    Thanks for your post, this is really very useful for me.

  4. / Reply

    This is one of the very useful blogs you will find. that will help in excelling the animation expertise for those who are in this industry. The blog is so elaborate that it may act as a tutorial on this subject. it is really an useful one. Thanks a lot for posting this blog!

    • _mark,
    • December 1, 2011
    / Reply

    really nice work here. probably will utilize this at some point. thank you.

  5. / Reply

    this is great. I love that you cut color script from heavy jquery ui.

    greetings from Poland.

  6. / Reply

    much wowness

Leave a Reply

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