Transition Delay Delays

Avatar of Chris Coyier
Chris Coyier on (Updated on )

UGURUS offers elite coaching and mentorship for agency owners looking to grow. Start with the free Agency Accelerator today.

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.