Link Underlines Grow to Backgrounds on Hover

Avatar of Chris Coyier
Chris Coyier on (Updated on )

Reader Jaime Morrison emailed in:

I’ve been mulling the idea of going with a link style in which a link’s underline “slides-up” to become a background highlight on hover.

I started mulling over a few ideas on how to do this myself. Ultimately, I tried two different things but neither one of them is absolutely perfect.

Take One

We know we’re going to be using jQuery here (or some JavaScript library), because of the animation. My first though was to set relative positioning on the links, then use jQuery to append empty <span>’s to each link. The span would be 1px tall (like an underline), be absolutely positioned 1 pixel down from the bottom, and have a width of 100% (as long as the link). Then on hover, you would animate the height of the span to the line-height.

$(function() {

    var $el = $(),
        linkHeight = parseInt($("p").css("line-height")),
        speed = 175;  //  1000 = 1 second
    
    $("#first a").each(function() {
    
        $el = $(this);
        
        // If the link spans two lines, put a line break before it.
        if ($el.height() > linkHeight) {
            $el.before("<br />");  
        }
        
        $el.prepend("<span></span>");
    
    }).hover(function() {
        
        $el = $(this);
                
        $el.find("span").stop().animate({ height: linkHeight, opacity: 0.3 }, speed);
    
    }, function() {
    
        $el = $(this);
                
        $el.find("span").stop().animate({ height: "1px", opacity: 1 }, speed);
    
    });

});

As it turns out, this kinda works, but it’s littered with little weird problems and cross browser inconsistencies. For example, what if a link breaks into two lines? The span certainly wouldn’t work in that case. So I tried to fix it by first measuring the height of the link and if that was higher than the line-height (like it would be if it’s two lines), then insert a break before it, so it’s all on one line. Works, but it can create some weirdly ragged paragraphs.

Other more major problems: no version of IE will animate the span up properly. The span will always sit on top of the text, so it needs to be have opacity for the text to be readable. I tried wrapping the text in an <em> and fighting it with z-index, but no dice. And even in Firefox, a link that begins a new line, for whatever reason, doesn’t want to work.

So, Take One is a flop…

Take Two

The biggest problem it seemed to me with Take One was the multi-line links. In trying to think of a way to solve that, it seemed like using the actual background property of the link would be the best way to go.

In this take, I created a big block of red color as an image. I set that as the background to the link, but pushed it down using background positioning. I set the overflow on the links to hidden, and pushed down that background just far enough that only a sliver showed through. Then on hover, the background position is animated to pull it back up underneath the entire link.

$(function() {

    var $el = $(),
        speed = 175;  //  1000 = 1 second

    // SECOND TECHNIQUE
    $("#second a").hover(function() {
        
        $el = $(this);
                
        $el.stop().animate({ backgroundPosition: "(0px 0px)", color: "white" }, speed);
    
    }, function() {
    
        $el = $(this);
                
        $el.stop().animate({ backgroundPosition: "(0px 15px)", color: "#900" }, speed);
    
    });

});

One of the concerns with this technique is how different browsers handle line-height. If I set up the underline to be 1px tall in Firefox, many browsers (e.g. WebKit) show no underline at all, as apparently the line-height is one less pixel tall and thus the entire background image is hidden. As such, I made it 2px tall in Firefox, and then WebKit showed it as 1px. Inconsistant, but it works.

This would be a “perfect” solution, except IE 7 cludges links that are multi-line or the begin a new line, covering the entire link in background from the start. IE 8 gets it right.

So in the end, neither one of these is totally perfect. If you have an alternate solution to this “underline animates up to be a background on hover”, let me know.

You can see the demos here.

UPDATE

As suggested by a few people, I added a Technique #3, which is an adaptation of #2. The links now have bottom borders of 1px (border color and background color match), and the background slides up from a greater distance away, which should solve the IE 7 issue.