Situation: I wanted to have a card element shift up slightly when hovered over. Seemed simple enough: just apply a vertical translate on hover, right?

Sure. But if you happen to be pointing at the lower edge of the card—the region that the card moves away from on hover—the hover state no longer applies, and the card snaps back down. At this point, it's under the cursor again, so it moves up...out from under the cursor. And down again. And up. And down. All in less than a second.

Behold—Jitter Man!

The fix: make it so that even if the element moves away from the cursor, it maintains its hover state. But how?

One suggestion I saw: add a border to the bottom of the element, set the width to the amount the card shifts up, and set the color to whatever's behind the card. That way, you're actually pointing at the border even if it looks like empty space. Problem: the border becomes painfully obvious if the item has a box shadow, or if the area behind the item isn't a solid color—say, an image.

Other suggestion: put the element in a container, target the container's hover state, and apply the transition to the child. That way, you'll always be hovering over the parent element, no matter where the child goes. (There's a joke about overbearing grandparents here, but I'll let it be.)

Problem: It'll work, but it means extra markup. Who wants that?

Solution: pseudo-elements!

I love pseudo-elements. They're like the CSS version of Thneeds. In this case, we can use them to make sure that even if the visible part of the element shifts away from the cursor, there's an invisible edge that remains under the cursor, preserving the hover state.

The basic technique looks like this (I'm using Sass):

  .tile:hover {
  /* We need this for the absolutely-positioned pseudo */
  position: relative;
  transform: translateY(-1em);

  &:after {
    /* Basic setup */
    position: absolute;
    content: "";
    width: 100%;
    top: 0;
    left: 0;

    /* Bottom is positioned to cover the area the hovered element is leaving */
    bottom: -1em;

    /* We need to place the pseudo-element under everything else. Without 
       specifying the placement, it will sit on top of the parent element (our card)
       and block the user from selecting text or clicking links */
    z-index: -1;
  }
}

Note: This code assumes you want the card to move up. I've built it into a mixin, which allows both directions. Get it fresh from the pen below!


2,176 1 21