No Time Dad

A blog about web development written by a busy dad

Hover Line Effect Using a Pseudo-element and Transition

Intro

I recently stumbled across a site that had a really cool effect on their nav links where an underline would appear from left to right on hover. I was curious how that worked and decided to give it a try and see if I could replicate what they were doing.

It turns out that they were using the ::after pseudo-element along with the transition property to achieve this effect.

Below is an example of what this hover effect looks like. Hover over each menu item with your mouse to see the line transition in and out from the left. Sorry mobile readers, this won’t really work for you…but trust me it is nice.

Pseudo-elements

I really have not used pseudo-elements much so I had to brush up a little bit on what they were. There are lots of different kinds of pseudo-elements, but for this example I am going to be using ::after.

Using ::after on a selected element will add content directly after the element without modifying the element itself. By default, ::after uses display: inline;.

As an example, here is a simple element without an ::after selector:

<div class="After__Demo">
  Hey this is my cool element!
</div>
.After__Demo {
  background-color: whitesmoke;
  color: darkslateblue;
  padding: 2rem;
  font-size: 20px;
  font-weight: 500;
}
Hey this is my cool element!

Now we can add an ::after selector and see how things look. The html will be exactly the same as before, but we’ll update our css to include the new ::after selector.

<div class="After__Demo">
  Hey this is my cool element!
</div>
.After__Demo {
  background-color: whitesmoke;
  color: darkslateblue;
  padding: 2rem;
  font-size: 18px;
  font-weight: 500;
}

.After__Demo::after {
  content: " Wow, I am after the cool element!";
  color: darkred;
}
Hey this is my cool element!

One interesting thing to note here is that pseudo-elements will not show up in the DOM as you might expect them to. They aren’t actually elements on the page at all. You won’t be able to highlight the text “Wow, I am after the cool element”. Here is what you’ll see in the html of the rendered page:

<div class="After__Demo">
  Hey this is my cool element!
  ::after
</div>

Hover Line Demo

Onto the hover line demo itself. I am going to pretend that our links are in a navigation bar, so I’ll drop them into a div with a Nav__Container class that uses flexbox. This step isn’t absolutely necessary, but it ensures that your links line up nicely and gives you a little more practice with flexbox (which everyone probably needs).

.Nav__Container {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  padding: 2rem;
  background-color: whitesmoke;
}
<div class="Nav__Container">
  ...
</div>

Now we can add our links, which will each have their own class creatively called Nav__Link. The class just adds a little bit of styling to the links themselves.

.Nav__Link {
  text-decoration: none;
  font-size: 22px;
  font-weight: 500;
  color: darkslateblue
}
<div class="Nav__Container">
  <a class="Nav__Link" href="#home">Home</a>
  <a class="Nav__Link" href="#about">About</a>
  <a class="Nav__Link" href="#contact">Contact</a>
</div>

From this point on our html will remain the same. An important thing to note here is that the content property is an empty string. We aren’t technically going to be adding any content but instead just filling a 3px space under the link itself. The other important thing to note is that the width is set to 0, which means that the link line won’t be visible when the page loads or if we aren’t interacting with it.

However, when we hover over the link we’ll set the width to 100%. Using the transition property against the width property, the line is effectively growing from 0 to 100% on hover. This is what gives the line the “left to right” effect.

The ::after pseudo-element will always exist in the DOM, but we don’t want it to be visible or do anything under we hover over it. We can achieve this by creating two selectors: .Nav__Link::after for the pseudo-element itself, and .Nav_Link:hover::after for the link hover that triggers the transition on the pseudo-element.

Here is how the new selectors will look:

...
.Nav__Link::after {
  /* Instead of content we'll create a 3px line */
  content: "";

  /* Display the line under the link, not next to it. */
  display: block; 

  /* Keep the line from being visible until we hover on the link */
  width: 0;

  /* The line will be 3px high */
  height: 3px;
  background: darkslateblue;

  /* Gradual transition from width: 0 to width: whatever */
  transition: width 0.3s ease-in-out;
}

.Nav__Link:hover::after {
  /* The line will span 100% of the width of the link text */
  width: 100%;
}
...

Code & Demo

I think that is a fun little link effect that didn’t require too much work. It can modified lots of different ways. It might be cool to have two links on top of each other where the top link has a line that comes in from the left and the bottom link comes in from the right. You could also make it expand out from the center.

Below is the full code and demo (again).

.Nav__Container {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  padding: 2rem;
  background-color: whitesmoke;
}

.Nav__Link {
  text-decoration: none;
  font-size: 22px;
  font-weight: 500;
  color: darkslateblue;
}

.Nav__Link::after {
  content: "";
  display: block;
  width: 0;
  height: 3px;
  background: darkslateblue;
  transition: width 0.3s ease-in-out;
}

.Nav__Link:hover::after {
  width: 100%;
}
<div class="Nav__Container">
  <a class="Nav__Link" href="#home">Home</a>
  <a class="Nav__Link" href="#about">About</a>
  <a class="Nav__Link" href="#contact">Contact</a>
</div>