Moving Highlight

Chris Coyier //

I recently noticed a subtle and nice effect in the Google Chrome UI. As you mouse over inactive tabs, they light up a bit, but also have a gradient highlight that follows your mouse as you move around on them.

The guys from DOCTYPE told me it was their inspiration for the navigation on their website. They are doing it just like I would have, with CSS3 gradients and jQuery. So I decided to snag their code and play around with it.

Radial Gradient

The highlight itself will be created from a radial gradient. One possible way to do that would be to create an alpha transparent PNG image and use that. We like to be all hip and progressive around here though. Radial gradients can be created through CSS3, which saves us a resource request and allows easier changing of colors.

Webkit and Mozilla based browsers (only) can do radial gradients. The syntax:

background: -webkit-gradient(
  /* radial, <point>, <radius>, <point>, <radius>, <stop>,  [<stop>, ] <stop> */
  radial, 500 25%, 20, 500 25%, 40, from(white), to(#ccc)
);

background: -moz-radial-gradient(
  /* [<position>,] [<shape> || <size>,] <stop> [, <stop>], <stop> */
  500px 25%, circle, white 20px, #ccc 40px
);

JavaScript Mouse Postion

Now we know how to apply a CSS3 gradient, but the point of this is to apply the gradient highlight where the mouse is. CSS isn't capable of giving us mouse position coordinates, we'll need JavaScript for that. So here is the plan:

  1. When the mouse moves over the area
  2. Detect mouse position
  3. Apply gradient to area in that position
  4. Remove gradient when mouse leaves

We'll be using jQuery.

var x, y;

$('#highlight-area').mousemove(function(e) {

  x  = e.pageX - this.offsetLeft;
  y  = e.pageY - this.offsetTop;

  // apply gradient using these coordinates
	
}).mouseleave(function() {			

  // remove gradient

});

The Trouble With Vendor Prefixes in Values

Vendor prefixes as properties is fine. You want to rotate something cross-browser (with a dynamic JavaScript value), you need to use -webkit-transform, -o-transform, and -moz-transform. If you need to do it with jQuery, you could do:

var angle = 30;

$(".rotate-me").css({
  "-webkit-transform" : "rotate(" + angle + "deg)",
  "-moz-transform" : "rotate(" + angle + "deg)"
  "-o-transform" : "rotate(" + angle + "deg)"
});

That works because each of the properties is different. With gradients, the property is always the same, the background-image property. So just like this won't work:

$(".wont-make-purple").css({
  "color" : "red",
  "color" : "blue" // overrides previous
});

This won't work either:

$("#highlight-area").css({
  "background-image" : "-webkit-gradient(radial, " + xy + ", 0, " + xy + ", " + gradientSize + ", from(" + lightColor + "), to(rgba(255,255,255,0.0))), " + originalBG;
  "background-image" : "-moz-radial-gradient(" + x + "px " + y + "px 45deg, circle, " + lightColor + " 0%, " + originalBGplaypen + " " + gradientSize + "px)"
});

But somehow, inexplicably (and thankfully) this does work:

var bgWebKit = "-webkit-gradient(radial, " + xy + ", 0, " + xy + ", " + gradientSize + ", from(" + lightColor + "), to(rgba(255,255,255,0.0))), " + originalBGplaypen;
var bgMoz    = "-moz-radial-gradient(" + x + "px " + y + "px 45deg, circle, " + lightColor + " 0%, " + originalBGplaypen + " " + gradientSize + "px)";
					
$(this)
	.css({ background: bgWebKit })
	.css({ background: bgMoz });

There must be some magic smarts worked in there somewhere were it doesn't override the previously set values.

Highlighted Tabs

So let's give ourselves the actual tabs in which to highlight. Nothing special in the markup:

<ul class="nav clearfix">
   <li><a href="#">Home</a></li>
   <li><a href="#">About</a></li>
   <li class="active"><a href="#">Clients</a></li>
   <li><a href="#">Contact Us</a></li>
</ul>

And the CSS to make them tab-like:

.nav { 
	list-style: none; 
	border-bottom: 1px solid #a0a0a0; 
	padding: 10px 10px 0 10px;	
}
.nav li { display: inline; }
.nav li.active a {
	position: relative;
	z-index: 1; 
	bottom: -2px;
	margin-top: -2px;
	background: #eee;
	padding-top: 8px;
	padding-bottom: 8px;
}
.nav li a { 
	float: right; 
	text-decoration: none;
	position: relative;
	padding: 7px 50px; 
	margin: 0 0 0 -8px; 
	color: #222;
	background: #d8d7d8; 
	-webkit-border-top-right-radius: 20px 40px;
	-webkit-border-top-left-radius: 20px 40px;
	-moz-border-radius-topleft: 20px 40px;
	-moz-border-radius-topright: 20px 40px;
	-webkit-box-shadow: inset 1px 1px 0 white;
	-moz-box-shadow: inset 1px 1px 0 white;
	border: 1px solid #a0a0a0;
}

So now each of those tabs is the area in which we plan to apply the highlight. For every non-active tab, we'll put together all the things we've covered:

var originalBG = $(".nav a").css("background-color");

$('.nav li:not(".active") a')
.mousemove(function(e) {

    x  = e.pageX - this.offsetLeft;
    y  = e.pageY - this.offsetTop;
    xy = x + " " + y;
	   
    bgWebKit = "-webkit-gradient(radial, " + xy + ", 0, " + xy + ", 100, from(rgba(255,255,255,0.8)), to(rgba(255,255,255,0.0))), " + originalBG;
    bgMoz    = "-moz-radial-gradient(" + x + "px " + y + "px 45deg, circle, " + lightColor + " 0%, " + originalBG + " " + gradientSize + "px)";

    $(this)
      .css({ background: bgWebKit })
      .css({ background: bgMoz });
		
}).mouseleave(function() {
	$(this).css({ background: originalBG });
});

View Demo   Download Files