Underline Link Animation

Two CSS-only underline link variants built on pseudo-elements. The default version draws a line from left to right on hover. The alt version uses two pseudo-elements with staggered transition-delay to erase the existing underline from right to left then draw a new one from left to right.

csshoverlinkunderlineanimationpseudo-elements

Code

index.html
html
<div class="link-group">
  <a data-underline-link href="#" class="underline-link">Underline Link</a>
  <a data-underline-link="alt" href="#" class="underline-link">Animation</a>
</div>
styles.css
css
.link-group {
  gap: 1em;
  justify-content: center;
  font-size: 3em;
  display: flex;
}

.underline-link {
  color: inherit;
  font-size: 1em;
  line-height: 1.25;
}

[data-underline-link] {
  text-decoration: none;
  position: relative;
}

[data-underline-link]::before,
[data-underline-link="alt"]::before,
[data-underline-link="alt"]::after {
  content: "";
  position: absolute;
  bottom: -0.0625em;
  left: 0;
  width: 100%;
  height: 0.0625em;
  background-color: currentColor;
  transition: transform 0.735s cubic-bezier(0.625, 0.05, 0, 1);
  transform-origin: right;
  transform: scaleX(0) rotate(0.001deg);
}

[data-underline-link="alt"]::before {
  transform-origin: left;
  transform: scaleX(1) rotate(0.001deg);
  transition-delay: 0.3s;
}

[data-underline-link="alt"]::after {
  transform-origin: right;
  transform: scaleX(0) rotate(0.001deg);
  transition-delay: 0s;
}

@media (hover: hover) and (pointer: fine) {
  [data-hover]:hover [data-underline-link]::before,
  [data-underline-link]:hover::before {
    transform-origin: left;
    transform: scaleX(1) rotate(0.001deg);
  }

  [data-hover]:hover [data-underline-link="alt"]::before,
  [data-underline-link="alt"]:hover::before {
    transform-origin: right;
    transform: scaleX(0) rotate(0.001deg);
    transition-delay: 0s;
  }

  [data-hover]:hover [data-underline-link="alt"]::after,
  [data-underline-link="alt"]:hover::after {
    transform-origin: left;
    transform: scaleX(1) rotate(0.001deg);
    transition-delay: 0.3s;
  }
}

Guide

Default Variant

Add data-underline-link to any link. A single ::before pseudo-element sits at scaleX(0) with transform-origin: right at rest. On hover it switches to transform-origin: left and scales to 1, drawing the line left to right.

Alt Variant

Add data-underline-link="alt" for a two-phase swap. ::before starts at scaleX(1) (visible) and collapses immediately on hover with no delay. ::after starts at scaleX(0) and draws in after a 0.3s delay — creating an erase-then-redraw effect.

Parent Hover

Both variants also respond to a data-hover attribute on a parent element. Add data-hover to a wrapper and the underline will animate when the wrapper is hovered, not just the link itself — useful for card hover states.

Customisation

The underline thickness is 0.0625em and offset is -0.0625em below the baseline — both scale with font-size. Adjust the transition duration (0.735s) and transition-delay (0.3s on the alt variant) to control the timing.