Animated active menuitem background

Animated active menuitem background

by Csaba Hellinger

HTML

<div class="nav-bg">
  <nav>
    <!-- add an "underline" class after "animation" -->
    <div class="animation"></div>
    <a href="#" class="active">Home</a>
    <a href="#">About</a>
    <a href="#">Blog</a>
    <a href="#">Portfolio</a>
    <a href="#">Contact</a>
  </nav>
</div>

CSS

@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');

body {
  font-family: 'Open Sans', sans-serif;
  background: #2c3e50;
  margin: 0;
  padding: 0;
}

.nav-bg {
  background: linear-gradient(0deg, transparent, #0008);
}

nav {
  margin: 0 auto;
  width: max-content;
  position: relative;
}

nav a {
  position: relative;
  font-size: 15px;
  text-transform: uppercase;
  color: white;
  text-decoration: none;
  line-height: 100%;
  display: inline-block;
  text-align: center;
  padding: 2rem 1rem;
  text-shadow: 1px 1px 3px black;
  opacity: 0.7;
  transition: opacity 200ms;
}

nav a.active {
  opacity: 1;
}

nav .animation {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 0;
  background: #1a8dbc;
  pointer-events: none;  
  transition: all 200ms cubic-bezier(0, 0, 0.85, 0.12);
}

/* only underline */
nav .animation.underline {
  top: unset;
  height: 0.1rem;
  bottom: 1.4rem;  
}

JavaScript

const nav = document.querySelector('nav');
const animation = document.querySelector('nav .animation');

nav.onclick = ({ target }) => {
  const { tagName, offsetLeft, offsetWidth } = target;
  if (tagName === 'A') {
    animation.style.left = `${offsetLeft}px`;
    animation.style.width = `${offsetWidth}px`;    
    // simulate active class
    nav.querySelector('a.active').classList.remove('active');
    target.classList.add('active');
  }
};