Why does this link hover effect start from the middle and not the left?

Viewed 212

I don't understand why in the code below, the link hover effect starts from the middle instead of going from left to right. Can someone please explain why this happens?

.orange-link {
  color: orange;
  position: relative;
  text-decoration: none;
}

.orange-link:before {
  content: "";
  position: absolute;
  width: 100%;
  height: 3px;
  background: orange;
  bottom: 0;
  border-radius: 5px;
  transform: scaleX(0);
  transition: .25s linear;
}

.orange-link:hover:before,
.orange-link:focus:before {
  transform: scaleX(1);
}
<p>Visit the official rules <a class="orange-link" href="#">here</a> on how to send in a write-in entry.</p>

4 Answers

It's because the default origin of CSS transforms is the center of the element.

"By default it is at the center of the element and can be moved. It is used by several transforms, like rotations, scaling or skewing, that need a specific point as a parameter." — https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transforms/Using_CSS_transforms

The line spans the full width, but is scaled to 0 (from the center) to start. Then, on hover, the line is scaled back up to it's original full width.

You need to add transform-origin: top left; to .orange-link:before

Fiddle

Unless you specify the transform-origin, it will default to the center.

It is happening because you are scaling the element. It scales from the middle because the origin is in the middle (by default).
As seen here

By default, the origin of a transform is "50% 50%", which is exactly in the center of any given element.

The possible fix can be to use the width

.orange-link {
  color: orange;
  position: relative;
  text-decoration: none;
}

.orange-link::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 3px;
  left: 0 !important;
  background: orange;
  bottom: 0;
  width: 0%;
  border-radius: 5px;
  transition: 0.25s linear;
}

.orange-link:hover::before,
.orange-link:focus::before {
  width: 100%;
}

.orange-link {
  color: orange;
  position: relative;
  text-decoration: none;
}

.orange-link::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 3px;
  left: 0 !important;
  background: orange;
  bottom: 0;
  width: 0%;
  border-radius: 5px;
  transition: 0.25s linear;
}

.orange-link:hover::before,
.orange-link:focus::before {
  width: 100%;
}
<p class="read-or-listen-to-excerpt">
  Visit the official rules <a class="orange-link" href="#">here</a> on how to send in a write-in entry.
</p>

Or you can even shift the origin by transform-origin: bottom left; and do this

.orange-link {
  color: orange;
  position: relative;
  text-decoration: none;
}

.orange-link:before {
  content: "";
  position: absolute;
  width: 100%;
  height: 3px;
  background: orange;
  bottom: 0;
  transform-origin: bottom left;
  border-radius: 5px;
  transform: scaleX(0);
  transition: .25s linear;
}

.orange-link:hover:before,
.orange-link:focus:before {
  transform: scaleX(1);
}
<p class="read-or-listen-to-excerpt">
  Visit the official rules <a class="orange-link" href="#">here</a> on how to send in a write-in entry.
</p>

You need to change the transform-origin so that it starts from the left:

.orange-link {
  color: orange;
  position: relative;
  text-decoration: none;
}

.orange-link:before {
  content: "";
  position: absolute;
  width: 100%;
  height: 3px;
  background: orange;
  bottom: 0;
  border-radius: 5px;
  transform: scaleX(0);
  transition: .25s linear;
    transform-origin:left bottom;
}

.orange-link:hover:before,
.orange-link:focus:before {
  transform: scaleX(1);
}
<p class="read-or-listen-to-excerpt">
  Visit the official rules <a class="orange-link" href="#">here</a> on how to send in a write-in entry.
</p>

Related