Grow your CSS skills. Land your dream job.

Link Underlines Grow to Backgrounds on Hover

Published by Chris Coyier

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.

Comments

  1. Ohh nice. A jQuery challenge :) Will let you know if I succeed :)

  2. Permalink to comment#

    Neat effect.
    Will try :)

  3. Permalink to comment#

    I really like the effect of Take Two – looks very slick!

  4. Permalink to comment#

    Very sexy effect :)

  5. Permalink to comment#

    Not a big fan to be honest, I think it looks rather tacky and not particularly smooth. I admire your efforts though, good job.

    • When I read it, I thought it sounded pretty cool. But I agree with Sam; the (working) demo just makes it look a bit amateurish. Dunno why.

    • Permalink to comment#

      yep… it takes me back to the vernacular days of html for some reason, when animated gifs and rolling marquis governed the land and if a trick was available/discovered, it had to be used. with that said, however, clients love this stuff. so i’ll save this for one of those days when i absolutely can not convince a client that less is more.

  6. After a couple quick attempts I couldn’t come up with anything that beats either option. In the few tests I ran option #2 seems the most reliable.

    I will agree with Sam though that it’s not a very pretty effect. Maybe if the change was subtle enough and worked with the design. Not something I think I will ever find a use for.

    Anyhow it was a fun experiment and I wish I had more time to play this morning. Something to come back to for sure.

  7. choen
    Permalink to comment#

    [OOT] Chris, can you give CSS tutorials related to usability and accessibility, thanks before.

  8. Permalink to comment#

    Nice effect!!!

  9. I know the take 1 was classed as a flop but i really like the transparent effect, i think the solid image block doesn’t look very nice :/

    I think I will have a play around with this and get the same effect but too work on multilines! Im still in the mood for using the shift up 1px link atm though :p

  10. Permalink to comment#

    I’ve seen this done so elegantly by a jquery/css guy on his blog, but I’ve been looking for a half hour and can’t find his site. The effect was exactly like you described, with an underline that grew into a gorgeous rounded-cornered box.

    If anyone knows the site I’m talking about I’d love to hear from you!

  11. tully
    Permalink to comment#

    Gimmicky. Less is more when it comes to something as simple as hyperlinks. What’s next, blinking backgrounds?!?

  12. Permalink to comment#

    @Chris – hmmm.. Add some sound, then will be a perfect solution. Please think about this. Best regards!

    • Kristian
      Permalink to comment#

      And if it’s sound effects from the first Doom game it would be more than perfect!

  13. Sean
    Permalink to comment#

    you don’t need the dollar sign in front of your variables. and what is this? $el = $(),

    that variable seems to carry no meaning?

    • Austin
      Permalink to comment#

      A lot of developers prepend a “$” on any variable which has a jQuery collection of elements.

    • I declare it once at the top, so I don’t have to re-delcare it with var = in each instance below. Seems to make sense to me, but if there is a better way please enlighten.

      And Austin is correct, I like putting the $ on variables I intened to be a group of elements rather than just a value.

    • Doing $el = $() needlessly queries the DOM and returns an empty jQuery object. Since you’re redefining $el before you even use it, this doesn’t make a whole lot of sense.

      Doing something like var $el, speed = 175; is probably want you want and will be more performant.

    • Yes, but you are declaring AND assigning it to nothing. A Simple declaration will do.

      var $el;

    • Great, thanks for the clarification. I didn’t know you could delcare a variable without assigning it. Not much a programmer, you see.

    • Havvy
      Permalink to comment#

      Then you might want to read the book “JavaScript, the Good Parts”, and find out more about the cool parts of the JavaScript language, though the book does not talk of the DOM.

      Good site none-the-less.

  14. Yeah. Kinda choppy and distracting. Especially when the links fall on two lines.

    Creative though.

  15. Permalink to comment#

    I quite like the effect, maybe it looks a little tacky because of the colors used. I think subtle coloring would probably work better. Nice little experiment though Chris I can see a few people adopting it.

  16. Chris, all this talk about links lately. I wondering how you were able to get the underline on your hover events to be a different color then the text itself? Sorry this is slightly off topic.

  17. Permalink to comment#

    Has anyone tried this in the old crappy IE6? I use border-bottom on my links and when viewed in IE6 they don’t appear. Not that I am worried about the borders or anything but it seems that it wouldn’t work for this. I don’t have IE6 installed but unfortunately people are still using it.

  18. Andreas Hecht
    Permalink to comment#

    Hi Chris!

    Your script is a little bit buggy. Visited Links will be displayed in white.

    On a white Background a nice Effect :-)

    Can anyone solve the problem?

  19. Max
    Permalink to comment#

    When you increase text size (using the browser View Zoom tool) the #2 solution increases the width of the bottom border on the links which looks really ugly.

    #1 and #3 are fine though…

  20. Permalink to comment#

    Wonderful tutorial… I will tried it

  21. Permalink to comment#

    If you wanted to be completely insane, you could combine it with the CSS3 background-gradient (see http://snook.ca/archives/html_and_css/multiple-bg-css-gradients) feature to remove the need for an image.

    You’d need to set several gradient stop levels that moved the desired background colour up/down by certain pecentages.

  22. Permalink to comment#

    Please leave our link styles alone, thx.

  23. Paul Annett
    Permalink to comment#

    Interesting stuff! Technique #3 is the only one that works well when you increase the font size.

  24. Permalink to comment#

    With the easing plugin this could make for an extremely fancy main navigation. I’ll drop a post here if I try it sometime.

  25. Interesting effect, I am going to work on #3 as that one looks promising. Thnaks for the share.

  26. Sowik
    Permalink to comment#

    Good tutorial you are amazing

  27. Nice work, but remember, you should always bind to the focus event as well. Not everyone uses a mouse to navigate…

  28. Permalink to comment#

    Nice idea for an effect. I wouldn’t use it on all the links though. It could be cool for some “call to action” links etc. Joe McCann has a good point about binding it to the focus event as well.

  29. salman
    Permalink to comment#

    yes on technique e works in ie6 and firefox 2

  30. salman
    Permalink to comment#

    yes only technique 3 works in ie6 and firefox 2

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".