Hover Zoom Effect with jQuery and CSS

Recently I’ve been prototyping some interactive thumbnail effects for various projects. I’d like to share one of the effects with you and how I built it. It’s a nice looking effect that I’m calling “hover zoom” because it’s pretty self explanatory. I’ve seen similar effects to this one on a few sites here and there, and they were always built in Flash. I wanted a CSS and Javascript solution so I whipped up the Hover Zoom effect.


The hover zoom effect basically reverse zooms an image while fading in a label on top of it when the mouse hovers over it. It makes for a pretty slick effect which could be used on thumbnails. As always, you can check out a demo or grab the source right here if you don’t want to read the entire tutorial.


The Approach

It’s no surprise that I am a visual learner, so I thought the easiest way to illustrate the approach I’m taking for this effect is to mock up a little diagram.

Hover Zoom Diagram

We will build a div (the viewport) which will be slightly smaller than the size of the image or thumbnail. We’ll hide any overflow with the css property overflow: hidden;. The image will be the bottom layer, then on top of that we’ll have the label. We’ll default the label to be hidden with the CSS property of display: none;. Then we’ll use jQuery to build the interactions of scaling (zooming) the image down and fading in the overlay on top. Simple.


The HTML is very simple for each thumbnail. We have the viewport which again is slightly smaller than the image. This allows the image to be scaled down to the viewport size and create a reverse “zoom” effect. Our label sits on top of the image and that’s about all there is to it!

<div class="viewport">
    <a href="http://www.flickr.com/photos/matt_bango/3479048548/">
        <span class="dark-background">Northern Saw-whet Owl <em>Photo by Matt Bango</em></span>
        <img src="http://farm4.static.flickr.com/3641/3479048548_e58742bd46.jpg" alt="Northern Saw-Whet Owl" />


The CSS is also very straight forward, you won’t find any surprises. You’ll notice that the image is positioned with negative top and left values. Let me explain the reasoning for this. When you scale the image, it scales from the bottom right corner. If we were to leave the image’s origin sit at 0,0; then when we scale the image it doesn’t have a “zoom” feel to it since only two sides are moving. To fake this, we push the origin of the image up and to the left, out of the view of the viewport. Now when we scale, we need to move the origin of the image to animate to 0,0. This is what gives us the “zoom” effect.

/* --- Container configuration ---------------------------------------------------------- */
.viewport {
    border: 3px solid #eee;
    float: left;
    height: 299px;
    margin: 0 9px 9px 0;
    overflow: hidden;
    position: relative;
    width: 450px;

/* This is so that the 2nd thumbnail in each row fits snugly. You will want to add a similar
   class to the last thumbnail in each row to get rid of the margin-right. */
.no-margin {
    margin-right: 0;

/* --- Link configuration that contains the image and label ----------------------------- */
.viewport a {
    display: block;
    position: relative;

.viewport a img {
    height: 332px;
    left: -20px;
    position: relative;
    top: -20px;
    width: 500px;

/* --- Label configuration -------------------------------------------------------------- */
.viewport a span {
    display: none;
    font-size: 3.0em;
    font-weight: bold;
    height: 100%;
    padding-top: 120px;
    position: absolute;
    text-align: center;
    text-decoration: none;
    width: 100%;
    z-index: 100;
    .viewport a span em {
        display: block;
        font-size: 0.45em;
        font-weight: normal;

/* --- Dark hover background ------------------------------------------------------------ */
.dark-background {
    background-color: rgba(15, 15, 15, 0.6);
    color: #fff;
    text-shadow: #000 0px 0px 20px;
    .dark-background em {
        color: #ccc;

 * You could create multiple hover background classes for different looks depending on the
 * image type. Use your imagination!

It is also worth noting that rgba() doesn’t work in IE (surprised?). There is a workaround for this that works very well.

The Javascript (jQuery)

The Javascript for this effect couldn’t get simpler. All we need to do is add “mouseenter” and “mouseleave” events to the viewport. Once the mouse enters the viewport, animate the background image to scale down to the viewport size and fade in the label on top of it. Simple.

$(document).ready(function() {
    $('.viewport').mouseenter(function(e) {
        $(this).children('a').children('img').animate({ height: '299', left: '0', top: '0', width: '450'}, 100);
    }).mouseleave(function(e) {
        $(this).children('a').children('img').animate({ height: '332', left: '-20', top: '-20', width: '500'}, 100);


So there you have it, a nice little effect that’s very easy to implement. Are there better ways to do this? Sure, there are probably quite a few improvements you could make, but as I stated early, this was just a prototype I put together. Hopefully you learned something from it and by all means expand and improve on it. If you see a better way to do something, share it in the comments!