Hover effect : expand bottom border

133,434

Solution 1

To expand the bottom border on hover, you can use transform:scaleX'(); (mdn reference) and transition it from 0 to 1 on the hover state.

Here is an example of what the border hover effect can look like : Expand border hover effect

The border and transition are set on a pseudo element to prevent transitioning the text and avoid adding markup.
To expand the bottom border from left or right, you can change the transform-origin property to the left or right of the pseudo element:

h1 { color: #666;display:inline-block; margin:0;text-transform:uppercase; }
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.fromRight:after{ transform-origin:100% 50%; }
h1.fromLeft:after{  transform-origin:  0% 50%; }
<h1 class="fromCenter">Expand from center</h1><br/>
<h1 class="fromRight">Expand from right</h1><br/>
<h1 class="fromLeft">Expand from left</h1>

Note : You need to add vendor prefixes to maximize browser support (see canIuse).

Expand bottom border on hover with 2 lines

You can achieve this effect when the text spans on 2 lines. The before pseudo element is absolutely positioned to make underline of the first line with bottom:1.2em;:

h1 { position:relative;color: #666;display:inline-block; margin:0;text-transform:uppercase;text-align:center;line-height:1.2em; }
h1:after, h1:before {
  display:block;
  content: '';
  border-bottom: solid 3px #019fb6;  
  transform: scaleX(0);  
  transition: transform 250ms ease-in-out;
}
h1:before{
  position:absolute;
  bottom:1.2em; left:0;
  width:100%;
}
.ef2:hover:after {
  transition-delay:150ms;
}
  
h1:hover:after, h1:hover:before { transform: scaleX(1); }
<h1>Expand border<br/>on two lines</h1>
<br/>
<br/>
<h1 class="ef2">Expand border<br/>effect two</h1>

Different transition direction on hover in and out :

The point is to change the transform-origin position from one side to the other on the hover state. This way the bottom boder enters from one side on hover and exits on the other when the element isn't hovered anymore.
Here is a demo :

h1 { color: #666;display:inline-block; margin:0;text-transform:uppercase; }
h1:after {
  display:block;
  content: '';
  border-bottom: solid 3px #019fb6;  
  transform: scaleX(0);  
  transition: transform 250ms ease-in-out;
}
h1.fromLeft:after{ transform-origin: 100% 50%; }
h1.fromRight:after{  transform-origin:   0% 50%; }
h1.fromLeft:hover:after{ transform: scaleX(1); transform-origin:   0% 50%; }
h1.fromRight:hover:after{ transform: scaleX(1); transform-origin: 100% 50%; }
<h1 class="fromRight">Expand from right</h1><br/>
<h1 class="fromLeft">Expand from left</h1>

Solution 2

We can do this with only background. No pseudo-element needed. This is more flexible.

h1 {
  /* you can change these variables to control the border */
  --border-color: purple;
  --border-width: 5px;
  --bottom-distance: 0px; /* you can increase this */
  
  color: #666;
  display: inline-block;
  background-image: linear-gradient(var(--border-color), var(--border-color));
  background-size: 0% var(--border-width);
  background-repeat: no-repeat;
  transition: background-size 0.3s;
  margin: 5px 0;
}

.fromCenter {
  background-position: 50% calc(100% - var(--bottom-distance));
}

.fromRight {
  background-position: 100% calc(100% - var(--bottom-distance));
}

.fromLeft {
  background-position: 0 calc(100% - var(--bottom-distance))
}

h1:hover {
  background-size: 100% var(--border-width);
}
<h1 class="fromCenter">Expand from center</h1><br/>
<h1 class="fromRight">Expand from right</h1><br/>
<h1 class="fromLeft">Expand from left</h1>

enter image description here

Multiple line animation:

h1 {
  /* you can change these variables to control the border */
  --border-color: purple;
  --border-width: 5px;
  --bottom-distance: 0px; /* you can increase this */
  
  color: #666;
  display: inline; /* should be 'inline' for multiple line animation */
  background-image: linear-gradient(var(--border-color), var(--border-color));
  background-size: 0% var(--border-width);
  background-repeat: no-repeat;
  transition: background-size 0.5s;
}

.fromCenter {
  background-position: 50% calc(100% - var(--bottom-distance));
}

.fromRight {
  background-position: 100% calc(100% - var(--bottom-distance));
}

.fromLeft {
  background-position: 0 calc(100% - var(--bottom-distance))
}

h1:hover {
  background-size: 100% var(--border-width);
}
<h1 class="fromLeft">Expand from <br>left with <br>multiple line</h1>

enter image description here

Solution 3

simple and lightweight version

li {
    margin-bottom: 10px;
}

.cool-link {
    display: inline-block;
    color: #000;
    text-decoration: none;
}

.cool-link::after {
    content: '';
    display: block;
    width: 0;
    height: 2px;
    background: #000;
    transition: width .3s;
}

.cool-link:hover::after {
    width: 100%;
    //transition: width .3s;
}
<ul>
    <li><a class="cool-link" href="#">A cool link</a></li>
    <li><a class="cool-link" href="#">A cool link</a></li>
    <li><a class="cool-link" href="#">A cool link</a></li>
</ul>

Solution 4

I know this is an old post and it is already answered but you might like the following effect too.

<div class="cd-single-point">
    <a class="cd-img-replace" href="#0"></a>
</div>

   .cd-single-point {
    position: absolute;
    list-style-type: none;
    left: 20px;
    top: 20px;
  }

  .cd-single-point>a {
      position: relative;
      z-index: 2;
      display: block;
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: #0079ff;
      -webkit-transition: background-color 0.2s;
      -moz-transition: background-color 0.2s;
      -o-transition: background-color 0.2s;
      transition: background-color 0.2s;
  }

  .cd-single-point::after {
    content: '';
    position: absolute;
    border-radius: 50%;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    animation: cd-pulse 2s infinite;
  }

  @keyframes cd-pulse
  {
  0%  {box-shadow:0 0 0 0 #0079ff}
  100%{box-shadow:0 0 0 20px rgba(255,150,44,0)}
  }

DEMO

Solution 5

h1 {
  color: #666;
  display:inline-block;
  margin:0;
  text-transform:uppercase;
 }
h1:after {
  display:block;
  content: '';
  border-bottom: solid 3px #92a8d1;  
  transform: scaleX(0);  
  transition: transform 800ms ease-in-out;
}
h1:hover:after {
 transform: scaleX(1);
}
<h1 class="fromCenter">Hover Over Me</h1><br/>
Share:
133,434

Related videos on Youtube

Vivekraj K R
Author by

Vivekraj K R

Open to new challenges. React | Redux | webpack | Jest | WCAG | Git | Jira Linkedin Codepen

Updated on October 23, 2021

Comments

  • Vivekraj K R
    Vivekraj K R over 2 years

    I'm trying to get a transition hover effect on border that the border expands on hover.

    h1 {
      color: #666;
    }
    
    h1:after {
      position: absolute;
      left: 10px;
      content: '';
      height: 40px;
      width: 275px;
      border-bottom: solid 3px #019fb6;
      transition: left 250ms ease-in-out, right 250ms ease-in-out;
      opacity: 0;
    }
    
    h1:hover:after {
      opacity: 1;
    }
    <h1>CSS IS AWESOME</h1>

    I've tried this on Jsfiddle

  • Christiaan Maks
    Christiaan Maks almost 8 years
    With scaleX(0.0001) I'm getting some artifacts but setting it to scaleX(0) seems to solve this.
  • web-tiki
    web-tiki almost 8 years
    @ChristiaanMaks you are right. I am updating the answer
  • Philip
    Philip almost 8 years
    Is there anyway to do this if the sentences span multiple lines?
  • web-tiki
    web-tiki almost 8 years
    @Philip you can achieve this effect when te text has 2 lines (see my edit in this answer).
  • Philip
    Philip almost 8 years
    What if you have <span>This is a sentence that spans one line.</span><span>This is a sentence that spans two lines but starts on the first line</span>
  • LSM
    LSM over 5 years
    how to implement in menu dropdown?
  • web-tiki
    web-tiki over 5 years
    @LidyMonteiro it depends on your markup and context. I can't answer with such little information, sorry.
  • JBis
    JBis over 5 years
    How can you do on inputs? Doesn't seem to work. jsfiddle.net/vxm3b4fe (Also if possible, instead of hover I would like to do :focus)
  • web-tiki
    web-tiki over 5 years
    @JBis unfortunatly inputs don't support pseudo elements so this technique can't be use to underline inputs
  • JBis
    JBis over 5 years
    @web-tiki That's unfortunate :(. Thanks for the quick reply. +1 anyway :)
  • web-tiki
    web-tiki over 5 years
    @JBis you could do something similar though like this : jsfiddle.net/vxm3b4fe/1 click in the name input.
  • JBis
    JBis over 5 years
    @web-tiki Perfect! Thanks. Just don't use a pseudo element, instead use a real one.
  • JBis
    JBis over 5 years
    @web-tiki Maybe add that as an option in the answer. Same example, just without a pseudo element :)
  • web-tiki
    web-tiki over 5 years
    @JBis I would rather keep the answer simple as it is now. It works for the vast majority of use cases.
  • Evan
    Evan almost 5 years
    Thanks! The accepted answer didn't work for me, but this did. If anyone else is having issues give this a try.
  • Alex Banman
    Alex Banman over 3 years
    I'm trying to use this on the bottom of a div, but it's positioning the line separately from the div and making it shorter than the width of the div. How do I control the layout so that it maintains the same length, position as a standard bottom-border style?