Grow your CSS skills. Land your dream job.

Transition Delay Delays

Published by Chris Coyier

A while ago we covered a cool "hover" technique by Doug Neiner where an informational popup was displayed when you hovered over a picture. The first time you hovered over, there was a delay. This was to prevent accidental or fly-by mouse overs, as opposed to intentionally focusing on a particular picture. Subsequent hovers showed the popup immediately. The idea being that you're already in that exploratory mode.

Doug used jQuery for the animations and some fancy dancing to keep track of the hover state and how it should be behaving. Let's do it a bit of a different way using CSS transitions with delays, and then just a touch of jQuery to keep track of the state.

Use Case

Let's say you have a row of functionality buttons. Users can click the button, or use a keyboard command that triggers the function. Power users love keyboard commands but need to learn them first and sometimes need reminders. So you decide to go with some kind of popup that will show the command when the button is hovered.

But mobile devices don't have hover!! Correct, or keyboards such that keyboard commands would be useful in this use case.

Title attribute?

One way you could go is a title attribute.

<a href="#" class="function button" title="⌘G">Do Thing</a>

This is a totally decent way to go. You'll get a standard title popup like this when hovered over:

In fact, the default behavior of title attribute is just as we described. The first hover will take a little bit to reveal the popup, but other links hovered over quickly will reveal right away, until enough time is given for the active popup to fade away completely.

Nice. But... no design control (c'mon shadow DOM!), no specific functionality control, and as you'll see in a moment, no generated content allowed.

By hand

Instead lets hand-roll this thing. We'll put the command right in the markup:

<nav>
  <a href="#" class="button">
    Cut
    <span class="command">
      <span class="screen-reader-text">Keyboard Command:</span>
      <span class="mod">X</span>
    </span>
  </a>
  ...
</nav>

This way we will:

  • Show nothing but the button for mobile
  • Be able to show the key command on hover for desktop
  • Announce what the random letter does (it's a keyboard command) for screen readers
  • Apply the correct modifier key without changing markup
  • Keep it reasonably semantic

Transition delay: delay on / instant off

Now that we have markup to work with, you can style up the look of the popup information however you need.

.button {

  /* Button styling */

  position: relative;
}
.command {
  
  /* Popup styling */

  position: absolute;
  opacity: 0; /* Not shown by default */
}

Functionally, we'll apply a one-second delay before showing the popup (keep annoyingness down) but instantly remove the popup when hovering away. We learned all about this one time.

.command {
   transition: opacity 0.2s 0 ease;  /* Mouse leave: immediate */
}
.button:hover .command {
   transition: opacity 0.2s 1s ease; /* Mouse enter: delay */
}

The above is unprefixed, but remember to use -webkit-, -moz-, -ms-, and -o- for transitions. Or use Compass and do @include transition(opacity 0.2s 0 ease);

Transition delay delay: instant only when focused

The above CSS gets us half way there, but it will apply the delay to every hovered button. That means exploring all the shortcuts is slow, tedious, and offends our faster-moving brains. We want to remove the delay once we've proven we've been inside the navigation area with our mouse long enough.

Using jQuery, we'll apply classes to the nav element depending on how we want it to behave. On entry to the navigation area, we wait one second, and then apply a class of "immediate". On exit, we remove that class.

$("nav").hover(function() {
  
  /* Mouse enter */
  var nav = $(this);
  setTimeout(function() {
    nav.addClass("immediate");
  }, 1000);
  
}, function() {
  
  /* Mouse leave */
  $(this).removeClass("immediate");
  
});
nav.immediate .command {
  transition-delay: 0s !important; 
}

We just need to get a hair fancier by applying and removing an "out" class. This is so if a user mouses on for half a second and out, the timeout will still trigger and give the immediate class, but the out class will negate it. Here's the complete JS:

$("nav").hover(function() {
  
  /* Mouse enter */
  var nav = $(this).removeClass("out");
  setTimeout(function() {
    nav.addClass("immediate");
  }, 1000);
  
}, function() {
  
  /* Mouse leave */
  $(this)
    .addClass("out")
    .removeClass("immediate");
  
});

With the "out" class, the delay is restored:

nav.out .command {
  transition-delay: 1s !important;
}

Demo and Download

I put the demo on CodePen. If you want to download a copy you can click the Share button and click "Export as .zip".

Notes

The CodePen demo has the bit of JavaScript that applies the "mac" or "pc" class that applies the modifier key. Warning: gross UA testing. But is there any other way to do it?

There is a bug (or it seems to me is a bug) in OS X Voice Over that reads the buttons as:

⌘ Cut Keyboard Command:X

rather than:

Cut Keyboard Command: ⌘X

Where the psuedo element is specifically being applied. Even better would be:

Cut [quick pause] [soft voice]Keyboard Command: ⌘X[/end soft voice]

Maybe, anyway.

Comments

  1. If only there was a state of html element, toggled by hovering it for 2s+and accessed via :long-hovered pseudoclass…

    Imagine:

    
    el:long-hovered {
        transition-delay: 0s;
    }
    

    :P

  2. Marsupial
    Permalink to comment#

    Nice.
    Just interested how voice over reads ⌘

  3. very nice Nick… i just tried that and worked like a charm! kudos to you for sharing your knowledge with us…

  4. This is pretty awesome thanks for sharing.

  5. very nice. thank you for share …
    ease-in-out has the most attractive, is not it?

    .command {
       transition: opacity 0.5s 0 ease; 
    }
    .button:hover .command {
       transition: opacity 0.5s 1s ease; 
    }
  6. Very nice, just a shame there’s so much mark up required.

  7. I don’t know why my previous link to CodePen was 404′ed, but here’s a new one: http://codepen.io/JosephSilber/pen/2/2

    It’s a pity we can’t edit comments here, Chris. Feel free to combine my two comments (you might also wanna fix my typo (mouseleave, with an ‘e’).

  8. The animations that can be achieved with css3 and jquey are fantastic. To think that not long ago, if you wanted a cute animation had to do everything in flash, etc..! Even a simple gradient had to do a picture. The css3 are translations with the best of these last years.
    I recently had to test with html5 and css3, the translations and transformations saved my life.
    Regards.

  9. Permalink to comment#

    Thanks – very cool

  10. Lithuanian
    Permalink to comment#

    what about transition delay delay delays ? :)

  11. Absolutely great work !!! Thumbs up!!!

  12. Regis

    What is the use of the !important marker in your example?

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