Animated Multiline Link Underlines with CSS
577 words · ~3 minutes to read
One benefit of building my personal website from scratch instead of using a theme made by someone else is that I can start from the browser’s defaults and gradually add my own flourishes. I strive to keep my site lean, but making it personal is also kind of the point. There is a spectrum of gratuitous touches between the spartan pages of Hacker News and Craigslist on one end and the sensory overload of old MySpace on the other.
I ran across a site that had fancy, animated underlines for links, and I wanted to add a similar effect to my site. It was important to me to use a pure CSS solution. For something as frivolous as this, I didn’t want to add JavaScript that has a risk of causing a performance issue or frustrating behavior (see scroll hijacking).
Here’s what the effect looks like now.
Implementation
The topic of drawing lines under text on the web can be surprisingly
complicated,
depending on how far you are willing to
stray from text-decoration: underline
and which details (like clearing
descenders) you care about.
I investigated a few approaches:
Both of them essentially remove the default text-decoration and add a simulated border using pseudo-elements. The border is then animated by CSS transitions. Unfortunately, these solutions have one drawback: they don’t work properly if the link spans more than one line. The underline only appears on the first line.
I eventually found a CodePen by Shaw that doesn’t have this flaw. I modified the CSS until I got to a solution that looks good to me.
Here is the relevant code. You can use this repl to play around with it.
a {
text-decoration: none;
background-image: linear-gradient(currentColor, currentColor);
background-position: 0% 100%;
background-repeat: no-repeat;
background-size: 0% 2px;
transition: background-size .3s;
}
a:hover, a:focus {
background-size: 100% 2px;
}
Let’s go through this approach part by part. First, we turn off the default
text-decoration
for links.
We want to use
background-image
because it can span multiple lines. While we could supply an actual image, we
only want to draw a line, so we use
linear-gradient,
which generates an image for us. Normally, it’s used to create a gradient
between two different colors, but I want the underline to just be the same color
as the link, so I use
currentColor
for both the beginning and the end of the gradient. currentColor
tells the
browser to use the element’s computed
color property.
We use
background-position
to place the image in the bottom left corner. 0%
sets the horizontal position,
and 100%
sets the vertical position.
We turn off background-repeat to prevent it from creating multiple instances of the image to fill the entire background of the link.
We use background-size to make the image zero pixels wide and two pixels tall. It has zero width because the underline should only appear on hover.
We set a transition
on background-size
, so any change to the property will take 0.3
seconds
to complete.
On link hover, we
change the width of the image to 100%
, creating a full underline, and
transition
takes care of the animation. As nickels55
suggests,
we also want the effect to happen on focus for those who use keyboard
navigation. Thanks to nickels55 for keeping
accessibility in mind.
And that’s it! I was very happy with how small the commit ended up being. If you’d like to add something similar to your site, feel free to take this implementation, or check out some other animated underline effects for inspiration.