Underlines are hard.
Complications quickly arise if you want to do anything fancier than the good ol' CSS text-decoration: underline
.
There are a lot of different techniques. Unfortunately, they nearly always come with significant drawbacks.
I ran into some of these drawbacks when I wanted to "borrow" the styling from the links in a Cassie Evans blogpost.
The links there have this awesome effect when you hover over them: The underline retreats and gets replaced by a new one, leaving a bit of space between the two while the transition happens.
The issue I ran into: Links on my blog often wrap to a different line and that means part of the link would not be underlined 😢.
Not only the links on Cassie's website are cool. The entire website is, from code to content.
Goal
A colored underline beneath links that has a hover effect where the line retreats and is replaced by a differently colored line.
The lines should not touch during this animation, leaving some space between them.
Links that wrap onto new lines should have the underline beneath all lines.
Use the background
There are many different ways to underline a piece of text.
The method I ended up using that met all of the requirements was: Using the background-image
CSS property.
A background-image
can be a solid color by defining it as a linear-gradient that transitions from one color to the same color.
Why am I using
background-image
and notbackground-color
if I intend to use a solid color?Because many properties to manipulate the background only work if
background-image
is used.- insert CSS is hard meme here -
The size of the background is limited in height and takes up the full width of the anchor element by setting the background-size
to 2px
and 100%
respectively.
This still ends up covering the entire background, because now it repeats over and over until it covers the entire background. So I stopped it from being naughty by setting background-repeat
to no-repeat
.
The line is at the top of the anchor element! Positioning it with background-position
set to 0 100%
places it at the left edge, and 100% from the top edge of the anchor element.
In other words, at the bottom... It's at the bottom now.
Two backgrounds
To use and manipulate multiple background images, set multiple values for the background-*
properties, seperated by a comma.
The first entry in a comma seperated list is on top, with each following entry a layer behind it.
The background of the following anchor element will be entirely black (#000000
). The white (#FFFFFF
) background is there, but it's not visible because it's covered by the black one.
a {
background-image: linear-gradient(#000000, #000000), linear-gradient(#ffffff, #ffffff);
}
In the example below, two backgrounds are set. Both at the bottom, making one overlap the other.
a {
color: #dfe5f3;
text-decoration: none;
background-image: linear-gradient(rgb(176, 251, 188), rgb(176, 251, 188)),
linear-gradient(#feb2b2, #feb2b2);
background-size: 100% 2px, 100% 2px;
background-position: 100% 100%, 0 100%;
background-repeat: no-repeat, no-repeat;
}
Transitioning the background-size
Notice how the background-position
is different, while it makes no visible difference?
One is anchored to the left side, the other is anchored to the right side.
Next, I'll be transitioning between one background taking up the full width normally and no width on hover while the second background does the opposite.
That anchoring will affect which point each background moves from/towards.
a {
color: #dfe5f3;
text-decoration: none;
background-image: linear-gradient(rgb(176, 251, 188), rgb(176, 251, 188)),
linear-gradient(#feb2b2, #feb2b2);
background-size: 100% 2px, 0 2px;
background-position: 100% 100%, 0 100%;
background-repeat: no-repeat;
transition: background-size 2s linear;
}
a:hover {
background-size: 0 2px, 100% 2px;
}
Three backgrounds
This almost satisfies the goals. The only thing missing is the space between the two lines.
That space can be faked by moving a block with the same color as the background.
What is that block? You guessed it: another background.
What is better than 2 background? Three backgrounds!
Three backgrounds .. ah ah ah 🦇
I'll place this background on top of the other two by listing it first in the comma seperated value for background-image
.
Don't forget!
The first value for otherbackground-*
properties now also points to this newly addedbackground-image
.
The width and height are set by background-size
. While the height is set to the same size as the other backgrounds (2px
in this example). This time, the width is set to be a fairly small 20px
.
Transitioning the background-position
To make the background-colored block invisible before hovering over the anchor element, the background is given a negative background-position
that places it to the left of the element, and thus, completely off the screen.
After hovering on the anchor, the block should move to the opposite side of the underline until it is completely offscreen again.
The calc()
function is used to calculate both of these positions.
a {
color: #dfe5f3;
text-decoration: none;
background-image: linear-gradient(#222b40, #222b40), linear-gradient(
rgb(176, 251, 188),
rgb(176, 251, 188)
), linear-gradient(#feb2b2, #feb2b2);
background-size: 20px 2px, 100% 2px, 0 2px;
background-position: calc(20px * -1) 100%, 100% 100%, 0 100%;
background-repeat: no-repeat;
transition: background-size 2s linear, background-position 2s linear;
}
a:hover {
background-size: 20px 2px, 0 2px, 100% 2px;
background-position: calc(100% + 20px) 100%, 100% 100%, 0 100%;
}
Tada 🎉
The anchor tag works and fits all the goals!
A big thank you to Jhey "Jh3y" Tompkins!
He is a magician with all things CSS/animation and I'm really glad I reached out to him.
I asked him a question when I was trying to figure this out. He not only answered it and taught me about the background-position
technique mentioned above.
He took it as a fun challenge and made an awesome proof of concept!
Jhey 🐻@jh3yyWant some cool CSS link underlines on your site? 🌶️
@NMeuleman challenged me to create something after seeing @cassiecodes' new site 😎
Here's what I made! 🤓
Powered by CSS variables, you can adjust color, width, etc. 💪
👉 codepen.io/jh3y/pen/gOPjB… via @CodePen21:43 PM - 14 Jul 2020
I got to know Jhey in the party corgi discord.
It's an awesome place that is filled with talented people, come hang out!
Top comments (2)
That's a really nice effect.
Thank you!