Grow your CSS skills. Land your dream job.

A Really Nice Way To Handle Popup Information

Published by Chris Coyier

Doug Neiner gave a fantastic talk at this year's Front End Design Conference. He covered a lot of stuff there, which you can see in his slides, but I'd like to highlight one thing in particular: the way he handled popup information in the demo. I'm going to try and re-explain it...

Let's say our goal is this: hover over an image to display extra information (like the title of the image and a URL).

Pretty darn simple design pattern right? Well, not really. There are a lot of subtle things to consider. Let's start from the simplest possible way and improve it.

Simplest way: hide/show with CSS

If our markup is:

   <img src="image.jpg" alt="cool image">
        <h3>Cool Image</h3>
        <a href=""></a>

We'll use the <figcaption> as the popup info. We'll hide it by default, then display it when the image is rolled over:

figcaption {
   display: none;
figure:hover figcaption {
   display: block;

But... it's so abrupt.

Fade in/out

We can chill it out with CSS transitions.

ficaption {
   opacity: 0;
  -webkit-transition: opacity 0.3s ease-out;  
     -moz-transition: opacity 0.3s ease-out;  
      -ms-transition: opacity 0.3s ease-out;  
       -o-transition: opacity 0.3s ease-out;  
          transition: opacity 0.3s ease-out;  
figure:hover figcaption {
   opacity: 1; 

We could get even fancier there if we wanted by applying different durations.

But... popups come up even if we are just quickly moving our mouse past and likely not interested in seeing popups.

"Hover Intent"

If we get JavaScript involved, we can get a little fancier. There is a jQuery plugin called hoverIntent which can be useful in preventing unwanted hover behavior. It imparts a bit of a delay before an event is fired, so quickly mousing over things won't trigger a popup but slowing down and stopping on an image will.

You could apply that like this:

$("figure").hoverIntent(function() {
   $("figcaption", this).fadeTo(400, 1);
}, function() {
   $("figcaption", this).fadeTo(400, 0);

But... now all the images use hoverIntent, which means that the slight delay to see the popup can be very annoying if you are specifically trying to browse the images and see the information. As Doug put it "my brain can think faster than that."

And finally: The Really Nice Way (doTimeout)

We like first part of hoverIntent, where quick mouseovers don't trigger the popup, but once we trigger one popup we want subsequent popups to happen quickly with no delay. If the user leaves the image area for a little bit, the delay is imparted again.

This makes use of the doTimeout plugin by Ben Alman. Admittedly, this gets a bit complex:

var li_cache, over = false;

$( "figure" )
	.delegate( "figcaption", "mouseenter", function ( e ) {
		var $li = $( this ), speed;

		if ( li_cache === this && over ) {
			$.doTimeout( "hoverOut" );

		if ( over ) {
			$.doTimeout( "hoverOut", true );
			speed = 0;
		} else {
			$.doTimeout( "hoverOut" );
			speed = 500;

		$.doTimeout( "hoverIn", speed, function () {
			over = true;
			$li.find( "div" ).fadeTo( 200, 1.0 );
	.delegate( "figcaption", "mouseleave", function ( e ) {
		var $li = $( this );

		$.doTimeout( "hoverIn" );
		$.doTimeout( "hoverOut", 500, function () {
			over = false;
			$li.find( "div" ).stop( true ).fadeOut();

Super nice. This improves the popup information reveal in such a natural and subtly enjoyable way that it's kind of a shame it's so difficult to implement. But it's worth it.

Doug's GitHub Repo

Go grab the GitHub Repository for this. Doug did a great job putting this together and it's educational as heck. You can see the progression through all the different hover functionalities just by commenting/uncommenting some function names. Beyond that, this example covers using jQuery templating, "Mockjax" (hijacking AJAX requests for local development) and more.

Seriously, I'm not even going to post a live demo, go download his repo and open the index.html file in a browser.


  1. Even if i understand that it’s simplier to do it in JS, you can also

    #1. put a timeout to add a classname after a few hundreds milliseconds.

    #2. use CSS animations with 0% to 30% to do nothing and 30% to 100% to do the needed animation (which could then be hardware accelerated, which JS is not)

  2. #3. User transition-delay.

  3. hehehe…
    may be this is false…

  4. Tom
    Permalink to comment#

    You have writen twice ficaption.

  5. Permalink to comment#

    Great tutorial and some good ideas.

    I’m a little conflicted on implementing hover conventions because of the touchscreen interaction model.

    Before the iPad I was a big proponent of hover menus and other hover conventions, but now I find that hover causes the user experience to be less fluid for touchscreen users.

  6. Dave
    Permalink to comment#

    Nice, thanks Chris.

    As someone who is pretty amateur at jQuery, it’d be cool if in some of your future code snippets you could include one or two inline comments around the important bits to help understand what those lines are doing.


  7. Nice! Note you can produce the same effect as hoverIntent with the transition-delay property, e.g.:

    ficaption {
       opacity: 0;
      -webkit-transition: opacity 0.3s ease-out 0.1s;
         -moz-transition: opacity 0.3s ease-out 0.1s;
          -ms-transition: opacity 0.3s ease-out 0.1s;
           -o-transition: opacity 0.3s ease-out 0.1s;
              transition: opacity 0.3s ease-out 0.1s;
    • I mentioned transition delay briefly in the article but it suffers from the same problem that hoverIntent does: it applys on every single mouseover.

      The really cool thing that Doug did was apply the delay for the first hover, then remove the delay for subsequent hovers.

  8. peter
    Permalink to comment#

    Chris, YOUR last name should be “Rock”. Cause you do!
    On another note and completely off-topic. Is their some way to chain pseudo-classes. Kinda like this:

    p:first-line:after {
      content: "...";
    // in this context
    p {
  9. Excellent post!
    Thank you, you have been most helpful.

  10. Permalink to comment#

    Can you please provide a demo.


    • Download the GitHub repo linked to at the bottom of the article. Open the index.html file in a browser. This is Doug’s work, I want to make sure he has 100% control over what people see.

  11. Permalink to comment#

    Nice technique, and great timing for me:

    I’ve been struggling trying for pretty much this effect for a client’s site. This looks both simpler to implement and much more “finished” and professional that other techniques I’ve tried.

    Thanks for this, Chris.

  12. Permalink to comment#

    This was really easy to follow, cheers

    Couldn’t actually get it to work for a while but that was just me being special

    Thanks again

  13. I like that you provided some alternative techniques for doing that. I used a lot in some of my projects hoverIntent but it’s not most suitable for this situation.

  14. Permalink to comment#

    I really like this site :

    So sick idea ! <3 i love it

  15. Cool Stuff Chris, thanks for sharing.

    You have a typo under the first CSS Section:

    ficaption { /*figcaption */
    display: none;

    Feel free to delete this comment, just wanted to let you know.


  16. Ada
    Permalink to comment#

    Or, you see, you could use this:

    $('figure figcaption').hover(function(){
      $(this).stop().animate({opacity: 1}, 400);
      $(this).stop().animate({opacity: 0}, 400);

    Add some easing and you have basically very same thing, with much less code.

    • Thanks for sharing code =)

      But for the record it’s not really the same. That code, when figcaptions are hovered it stops and current animations then animates them in or out.

      The final code presented here does much more. It doesn’t trigger the animation right away, there is a delay. But that delay is only present the first time you mouse into that area. Subsequent hovers aren’t subject to the delay, unless you leave the area for a while and came back.

      I just wanted to make this very clear, as the whole point of this article is improving this simple interaction in subtle but important ways.

  17. Very interesting article Chris. This one’s definitely going in my bookmarks for future reference.

  18. Permalink to comment#

    Great post, thank you.

    I’m implementing this for a featured image grid in WordPress – my pop-ups are wider than the image used as the trigger, and absolutely positioned off to the right a bit (intentionally to match a design). Used as a non-moving tooltip, if you will. What’s happening though, is the pop-up for the image furthest to the right side of the grid, gets cut off (partially hidden) and then adds a horizontal scrollbar by the image grid container element.

    It seems to be caused by using overflow:auto; in the container element, but that’s required for the sticky footer to function.

    Any tips on how I can force the pop-up to show above all content, including the container?

    I’ve tried overflow and z-index, but it’s still inside / underneath / cropped.

    Thank you for your thoughts.

  19. Dennis Hall
    Permalink to comment#

    If you want to do it even more right, or if you were thinking that the basic mechanism would be a good fit for things like menus — and especially menus with submenus — then you need to use this:

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".