3

I'm having an animated underline effect when user points the links on my website. The underline is a bit wider than the text itself, as there's a bit of horizontal padding.

Here's the effect I wanted to achieve and I did:

Underline animation

I was thinking if it was possible to simplify my code. After some trial and error, I used negative margin-left on the underline element and calc() to calculate its width as 100% + 2 * padding. It looks to me like an overcomplicated solution. Can the same effect be achieved without calc() and, perhaps, without negative margin?

Of note, adding a wrapper element is not an option. It needs to be a plain <a> element.

:root {
    --link-color: #f80;
    --link-underline-padding: .5em;
}

a {
    color: var(--link-color);
    display: inline-block;
    padding: 0 var(--link-underline-padding);
    text-decoration: none;
}

a:after {
    background-color: var(--link-color);
    content: '';
    display: block;
    height: .1em;
    margin-left: calc(var(--link-underline-padding) * -1);
    margin-top: .2em;
    transition: width .5s;
    width: 0;
}

a:hover:after {
    width: calc(100% + var(--link-underline-padding) * 2);
}
I find <a href="#">dogs</a> pretty cool.
Robo Robok
  • 21,132
  • 17
  • 68
  • 126

2 Answers2

2

A simple background animation can do this:

a {
  background: linear-gradient(currentColor 0 0) 
    bottom left/
    var(--underline-width, 0%) 0.1em
    no-repeat;
  color: #f80;
  display: inline-block;
  padding: 0 .5em 0.2em;
  text-decoration: none;
  transition: background-size 0.5s;
}

a:hover {
  --underline-width: 100%;
}
I find <a href="#">dogs</a> pretty cool.

Related:

How to animate underline from left to right?

How to hover underline start from center instead of left?

Robo Robok
  • 21,132
  • 17
  • 68
  • 126
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Thanks a lot, I love how simple that is now! I edited it a little bit to get rid of the padding var (because padding is now used in just one place) and I added the `--underline-thickness` var (because we use it in both normal and hover state). Thanks again <3 – Robo Robok Jul 13 '20 at 23:22
  • @RoboRobok I made another edit to avoid using the thickness twice ;) (update: same for color) – Temani Afif Jul 13 '20 at 23:22
  • OMG, I forgot it's possible to do it like that, awesome! How about removing `--underline-thickness` and `--link-color` now? And I didn't know `currentColor` existed :D – Robo Robok Jul 13 '20 at 23:39
  • @RoboRobok yes you can remove them if you want ;) – Temani Afif Jul 13 '20 at 23:57
  • Thank you for editing. I changed one more thing - I renamed `--s` to `--underline-width` to make it more clear what we do there. I think we have the perfect snippet now. – Robo Robok Jul 15 '20 at 00:01
1

If you set a to position: relative; you can then use position: absolute; and left: 0px; to push it past the padding and then just use width: 100% to have it extend the entire length.

:root {
    --link-color: #f80;
    --link-underline-padding: .5em;
}

a {
    position: relative;
    color: var(--link-color);
    display: inline-block;
    padding: 0px var(--link-underline-padding);
    text-decoration: none;
}

a:after {
    position: absolute;
    left: 0px;
    background-color: var(--link-color);
    content: '';
    display: block;
    height: .1em;
    margin-top: .2em;
    transition: width .5s;
    width: 0;
}

a:hover:after {
    width: 100%;
}
I find <a href="#">dogs</a> pretty cool.
Jack
  • 1,893
  • 1
  • 11
  • 28
  • Hey, your answer is really great! I hope you don't mind me choosing another one, as it makes things even easier with `background-size`. I love your approach too though! <3 – Robo Robok Jul 13 '20 at 23:18
  • @RoboRobok, definitely the better answer, thanks for considering – Jack Jul 14 '20 at 01:10