nicyuvi
nicyuvi

Reputation: 17

How to animate a border-bottom over an existing border-bottom on hover without using pseudo-states

I want to animate a border-bottom on hover over an existing border bottom like so: Gif showing border-bottom transition

I have tried doing this using pseudo states ::before and ::after and this only animates a border bottom out of nothing. And I've also tried to include a border-bottom on the original element but this does not work since the transition happens on the ::after element and the two don't overlap like in the gif example: https://jsfiddle.net/nicyuvi/6xem7zgp/3/

h1 {
  color: #666;
  display: inline-block;
  margin: 0;
  text-transform: uppercase;
  border-bottom: 3px solid lightgray; 
}

h1:after {
  display: block;
  content: '';
  border-bottom: solid 3px #019fb6;
  transform: scaleX(0);
  transition: transform 250ms ease-in-out;
}

h1:hover:after {
  transform: scaleX(1);
}

h1.fromLeft:after {
  transform-origin: 0% 50%;
}
<h1 class="fromLeft">Expand from left</h1>

Any answers using html/css/vanilla javascript?

Upvotes: 0

Views: 1478

Answers (2)

Temani Afif
Temani Afif

Reputation: 273688

A simple background animation can do it:

h1 {
  color: #666;
  display: inline-block;
  margin: 0;
  text-transform: uppercase;
  background:
    linear-gradient(#019fb6 0 0),
    linear-gradient(lightgray 0 0); 
  background-size:0% 3px,100% 3px; /* we make the top one 0% width */
  background-position:bottom left;
  background-repeat:no-repeat;
  transition:0.5s;
}


h1:hover {
  background-size:100% 3px; /* 100% width on hover */
}
<h1 class="fromLeft">Expand from left</h1>

Considering your code, a negative margin is all what you are missing:

h1 {
  color: #666;
  display: inline-block;
  margin: 0;
  text-transform: uppercase;
  border-bottom: 3px solid lightgray; 
}

h1:after {
  display: block;
  content: '';
  border-bottom: solid 3px #019fb6;
  margin-bottom:-3px; /* here */
  transform: scaleX(0);
  transition: transform 250ms ease-in-out;
}

h1:hover:after {
  transform: scaleX(1);
}

h1.fromLeft:after {
  transform-origin: 0% 50%;
}
<h1 class="fromLeft">Expand from left</h1>

Upvotes: 2

Simplicius
Simplicius

Reputation: 2095

You need to create to elements stacked on top of each other, then add pointer-events: none to the upper one, which will make it "transparent" to pointer-events, so you can still acces the input below it. Using the ~ selector we can now edit the width of the upper element, while hovering the input, since its placed "phyisically" after the input.

/* optional */

input[type=text] {
  background-color: #ECEFF1;
  border: 0;
}

/**/

*,
::after,
::before {
  box-sizing: border-box;
}

.wrapper {
  display: inline-block;
  position: relative;
}

input[type=text],
.onHover {
  height: 2rem;
  border-bottom-style: solid;
  border-bottom-width: 1px;
}

input[type=text] {
  border-bottom-color: #B0BEC5;
}

input[type=text]:hover ~ .onHover {
  width: 100%;
}

.onHover {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  border-bottom-color: #000;
  pointer-events: none;
  transition: width 400ms ease-in;
}
<div class="wrapper">
  <input type="text">
  <div class="onHover"></div>
</div>

Upvotes: 1

Related Questions