skip to Main Content

I’ve got a banner that when hovered on displays an "underline" at the bottom of the container. Currently it’s achieved using ::after and the functionality is perfect, but I don’t like having to be so specific with the bottom: -65px, it feels a bit hacky and not truly responsive.

For example, if the banner was not exactly 150px, then the underline would be off.

Is there a way I can avoid using specific px values for bottom in this use case?

body {
  margin: 0;
}

.banner {
  height: 150px;
  background-color: lightpink;
  display: flex;
  justify-content: center;
  align-items: center;
}

ul {
  display: flex;
  align-items: center;
  gap: 25px;
}

ul li {
  list-style: none;
}

ul li a {
  position: relative;
  text-decoration: none;
}

ul li a::after {
  position: absolute;
  bottom: -65px;
  left: 0;
  right: 0;
  width: 0%;
  content: '';
  color: transparent;
  background: black;
  height: 3px;
}

ul li a:hover::after,
ul li a:active::after {
  transition: all 0.2s;
  width: 100%;
}
<div class="banner">
  <ul>
    <li><a href="">Option 1</a></li>
    <li><a href="">Option 1</a></li>
    <li><a href="">Option 1</a></li>
  </ul>
</div>

2

Answers


  1. One possible approach would be to make the a elements take all the available height so you can specify bottom: 0; on the pseudo element like this :

    body {
      margin: 0;
    }
    
    .banner {
      height: 150px;
      background-color: lightpink;
      display: flex;
      justify-content: center;
      align-items: center;
      margin-bottom: 10px;
    }
    .height-80 {
    height: 80px;
    }
    
    ul {
      display: flex;
      align-items: stretch;
      gap: 25px;
      height: 100%;
      margin: 0;
    }
    
    ul li {
      list-style: none;
    }
    
    ul li a {
      display: block;
      height: 100%;
      position: relative;
      display: flex; 
      align-items: center;
    }
    
    ul li a::after {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      width: 0%;
      content: '';
      color: transparent;
      background: black;
      height: 3px;
    }
    
    ul li a:hover::after,
    ul li a:active::after {
      transition: all 0.2s;
      width: 100%;
    }
    <div class="banner">
      <ul>
        <li><a href="">Option 1</a></li>
        <li><a href="">Option 1</a></li>
        <li><a href="">Option 1</a></li>
      </ul>
    </div>
    
    <div class="banner height-80">
      <ul>
        <li><a href="">Option 1</a></li>
        <li><a href="">Option 1</a></li>
        <li><a href="">Option 1</a></li>
      </ul>
    </div>

    Another approach could be to use CSS variables to specify the height of the header and adapt the positioning of the pseudo element accordingly.

    In the following example, you can change the height of the banner by changing the value of --banner-height: 150px; and the positioning of the pseudo element will adapt with margin-bottom: calc(var(--banner-height) / -2); (note that y also changed the value of bottom to 50%).

    :root {
      --banner-height: 150px;
    }
    body {
      margin: 0;
    }
    
    .banner {
      height: var(--banner-height);
      background-color: lightpink;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    ul {
      display: flex;
      align-items: center;
      gap: 25px;
    }
    
    ul li {
      list-style: none;
    }
    
    ul li a {
      position: relative;
      text-decoration: none;
    }
    
    ul li a::after {
      position: absolute;
      bottom: 50%;
      left: 0;
      right: 0;
      width: 0%;
      margin-bottom: calc(var(--banner-height) / -2);
      content: '';
      color: transparent;
      background: black;
      height: 3px;
    }
    
    ul li a:hover::after,
    ul li a:active::after {
      transition: all 0.2s;
      width: 100%;
    } width: 100%;
    }
    <div class="banner">
      <ul>
        <li><a href="">Option 1</a></li>
        <li><a href="">Option 1</a></li>
        <li><a href="">Option 1</a></li>
      </ul>
    </div>
    Login or Signup to reply.
  2. The whole ::after isn’t nessecary.

    You can use the border-bottom declaration:

    body {
      margin: 0;
    }
    
    .banner {
      height: 150px;
      background-color: lightpink;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    ul {
      display: flex;
      align-items: center;
      gap: 25px;
    }
    
    ul li {
      list-style: none;
    }
    
    ul li a {
      position: relative;
      text-decoration: none;
      border-bottom: solid 3px #00000000;
    }
    
    
    ul li a:hover {
      transition: all 0.2s;
      border-bottom: solid 3px black;
    }
    <div class="banner">
      <ul>
        <li>
          <a href="">Option 1</a>
        </li>
        <li>
          <a href="">Option 2</a>
        </li>
        <li>
          <a href="">Option 3</a>
        </li>
      </ul>
    </div>

    As you can see, the results will differ. You can change the vertical position by editing a‘s height.

    This may be not what you’re looking for, as the underline won’t slide out, it will just "appear".

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search