See the Pen Elastic Hover Effect with Animated Underline by Francois Marais (@francoismarais) on CodePen.

In the above code pen you can see the fun little menu effect we are about to create. The whole effect is created using only HTML and CSS (SCSS), in addition we will be using Psuedo elements.

When we look at the effect we can see that:

  1. The effects all trigger on hover
  2. Text underlined from the center of word outwards
  3. Letter spacing increases (distance between the individual letters)
  4. Colour changes
  5. There is a nice “bouncy” animation to the whole effect
  6. Items stack on top of each other on smaller screens

Setting up the document structure

First off we create the navigational HTML structure, wrap an unsorted list element within a nav element and the populate a list of links.

<nav>
    <ul class="underline-nav">
        <li><a href="#" >About</a></li>
        <li><a href="#" >Portfolio</a></li>
        <li><a href="#" >Social</a></li>
        <li><a href="#" >Tutorial</a></li>
    </ul> 
</nav>

Take note that all the code can be found in the codepen, and if you don’t understand SCSS you can use the drop down arrow in codepen to view the compiled CSS.

Creating the bounce / elastic animation

Next up we create the underline effect using CSS. It’s important to note that the line animates from the center of the link outwards.

We create the animation using the cubic-bezier function in CSS, this sets the timing for the animation and is really easy to create when you use the developer tools in Firefox and/or one of the online tools like cubic-bezier.com. Below you can see the cubic-bezier as seen when edited in fire-fox.

Elastic-Curve-Graphic
cubic-bezier animation

Timing is arguably the most important aspect of animation, and spending time to get it right will greatly enhance the “feel” of your designs. To achieve the effect we are going for we make sure that we overshoot the end position both in the beginning I loosely based this on of the 12 principles of animation, #5 Follow through and overlapping.

You could potentially create a more controlled animation using @keyframes, I am however satisfied with the result from the cubic-bezier function. Because we are using SCSS we set a variable that we can simply reuse throughout our code when defining the “timing” value in our transition property, this is what it looks like:

$bounce: cubic-bezier(.68,-0.55,.27,1.55);

Creating the underline decoration

The underline decoration is not actually an underline, it merely looks like one. If you have a look at the code you will noticed that we use the psuedo element: before with an absolute positioning.

It’s really important to note that the link element needs to be relative and the pseudo before element needs to be absolute, this allows it to sit over the element and size itself to the appropriate size. See the examples below:

Here we can see the pseudo before element (in this case the orange outline) applied to a list element.

  • Relative Positioning

With absolute positioning applied we can see how it lays on top of the parent li element.

  • Absolute Positioning

Now that we understand how this works, we simply set the background for the before element to a 100% width, position it to the bottom and give it a small height value so it looks like a line.

.underline-nav{
  padding: 0px;
  margin-top: 0px;
  list-style-type: none;
  li {
    display: inline;
    a{
      color: #353541;
      text-decoration: none;
      padding: 10px;
      position: relative;
      &:before{
        content: "";
        position: absolute;
        width: 100%;
        height: 2px;
        bottom: 0;
        background-color: #35384c;       
      }
    }
  }    
}

Please note that in the above code I have also included the basic styling for margins, list styling and text alignment, these are rather trivial with the exception of text-align: center;

Animating the line

Next we add the hover state that triggers the line animation, hover over the example to see it in action.

Update your SCSS with the following code:

.underline-nav{
  padding: 0px;
  margin-top: 0px;
  list-style-type: none;
  li {
    display: inline;
    a{
      color: #353541;
      text-decoration: none;
      padding: 10px;
      position: relative;
      &:before{
        content: "";
        position: absolute;
        width: 100%;
        height: 2px;
        bottom: 0;
        background-color: #35384c;       
        transform: scaleX(0);
        transition: all 0.6s $bounce;
      }
      &:hover{
        &:before{ 
          transform: scaleX(1)
        }
      }

    }
  }    
}

I need to bring your attention to the transform origin property at this point. We have not defined it or used it as it defaults to the middle of the items x and y (50%), which is what we needed for our effect. Take a couple of minutes to add it and play with the different values and see how it changes the effect.

Animating the text

To get the text to spring outwards we recycle the timing from the $bounce cubic-bezier we set up earlier and add the letter-spacing property to the a tag and the a:hover tag. We also need to center the text in the nav or the underline-nav class to get the text expanding from the center.

.underline-nav{
  padding: 0px;
  margin-top: 0px;
  list-style-type: none;
  text-align: center;
  li {
    display: inline;
    a{
      user-select: none;
      color: #353541;
      text-decoration: none;
      padding: 10px;
      position: relative;
      letter-spacing: 3px;
      text-transform: uppercase;
      transition: all 0.5s $bounce;
      &:before{
        transition: all 0.6s $bounce;
        content: "";
        position: absolute;
        width: 100%;
        height: 2px;
        bottom: 0;
        left: 0;
        background-color: #ffffff;       
        transform: scaleX(0)   ;
      }
      &:hover{
        color: #fff;
        letter-spacing: 6px;
        &:before{ 
          transform: scaleX(1);
        }
      }

    }
  }    
}

Stacking the items for a responsive design

@media (max-width: 768px) {
  .underline-nav{
    li {
        padding: 10px;
        display: block;
      }
  }
}

Conclusion

We created a smooth, fun animated elastic effect with the help of pseudo elements, the cubic bezier function, transform and exploring one of the 12 principles of animation. Whilst I haven’t tested the effect in all the browsers, the code should work in all popular browsers.

Feel free to review the code on codepen.io, and mess around with it a little. If you are unfamiliar with code pen or SCSS, simply hit the dropdown arrow on the SCSS panel and select “view compiled css”, in addition tweak the zoom options in the code pen above to see the responsive versions (horizontal and vertical layouts).

I hope this tutorial helped someone, somewhere, feel free to share, use and customise if you do end up using it drop me a message and show me how you ended up using it.

All the best, Francois.

Leave a comment

Leave a Reply