<div class="select-wrapper">
  <label class="label" for="animation-options">Hover animation style:</label>
  <select class="select" id="animation-options">
    <option>bold</option>
    <option>bold delay</option>
    <option>buoy</option>
    <option>front flip</option>
    <option>jump</option>
    <option>reflection pool</option>
    <option>shadow</option>
    <option>smile</option>
    <option>step sequence</option>
    <option>wave</option>
  </select>
  <svg class="icon" fill="currentColor" viewBox="0 0 320 512" width="100" title="angle-down">
    <path d="M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z" />
  </svg>
</div>

<nav id="nav">
  <ul class="menu">
    <li class="menu-item"><a data-splitting class="menu-link" href="#0">Full menu</a></li>
    <li class="menu-item"><a data-splitting class="menu-link" href="#0">Place an order</a></li>
    <li class="menu-item"><a data-splitting class="menu-link" href="#0">Our story</a></li>
    <li class="menu-item"><a data-splitting class="menu-link" href="#0">Contact us</a></li>
  </ul>
</nav>
@import url('https://fonts.googleapis.com/css2?family=Public+Sans:wght@300;700&display=swap');

:root {
  --normal: 300;
  --bold: 700;
  --duration: 250ms;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: "Public Sans", sans-serif;
  font-size: 1rem;
  padding: 4rem 1rem;
  background-color: whitesmoke;
  
  > * + * {
    margin-top: 4rem;
  }
}

.select-wrapper {
  position: relative;
  width: 100%;
  max-width: 250px;
}

.icon {
  pointer-events: none;
  position: absolute;
  top: 50%;
  right: 1rem;
  width: 0.75em;
  transform: translateY(-50%);
}

.label {
  position: absolute;
  bottom: 100%;
  margin-bottom: 0.5rem;
}

.select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 100%;
  padding: 0.75rem 1rem;
  cursor: pointer;
  outline: none;
  border: 2px solid lightgray;
  border-radius: 0.25rem;
  background-image: linear-gradient(to bottom, white, whitesmoke);
  font-family: inherit;
  font-size: inherit;
  font-weight: 700;
  text-transform: capitalize;

  &:focus {
    border-color: dodgerblue;
  }
}

.menu {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  font-size: 1.5rem;
}

.menu-link {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.5rem;
  color: black;
  font-variation-settings: "wght" var(--normal);  
  text-decoration: none;

  &:hover {
    font-variation-settings: "wght" var(--bold);
  }
  
  .char {
    --delay: calc(var(--char-index) * 0.024s);

    display: inline-flex;
    flex-direction: column;
    letter-spacing: -0.05rem;
    transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    transition:
      font-variation-settings var(--duration) var(--ease),
      opacity var(--duration) var(--ease);
  }

  .whitespace {
    width: 4px;
  }

  .char::before {
    position: relative;
    height: 0;
    width: 100%;
  }

  .char::before,
  .char::after {
    pointer-events: none;
    font-variation-settings: "wght" var(--bold);
  }
}

// -------------------------------
$animation: bold-delay;
// -------------------------------

[data-animation="#{$animation}"] {
  .menu-link .char {
    transition-delay: var(--delay);
  }
}

// -------------------------------
$animation: buoy;
// -------------------------------

[data-animation="#{$animation}"] {
  .menu-link:hover .char {
    transition-delay: var(--delay);
    animation: #{$animation} calc(var(--duration) * 6) var(--delay) ease-in-out infinite;
  }

  @keyframes #{$animation} {
    25% {
      transform: translateY(-6px) rotate(-5deg);
    }
    50% {
      transform: translateY(0px);
    }
    75% {
      transform: translateY(-6px) rotate(5deg);
    }
  }
}

// -------------------------------
$animation: front-flip;
// -------------------------------

[data-animation="#{$animation}"] {
  .menu-link:hover .char {
    transform-origin: 50% 0;
    animation: #{$animation} calc(var(--duration) * 6) calc(var(--char-index) * 0.02s) var(--ease) forwards;
  }

  @keyframes #{$animation} {
    20% {
      transform: translateY(0) scale(0.8);
      animation-timing-function: linear;
    }
    25% {
      transform: translateY(2px) scale(0.8, 0.6);
      animation-timing-function: linear;
    }
    30% {
      transform: translateY(-10px) scale(0.8, 1.2);
      animation-timing-function: linear;
    }
    40% {
      transform: translateY(-10px) scale(1, -1);
      animation-timing-function: linear;
    }
    50% {
      transform: translateY(6px);
    }
    70% {
      transform: translateY(0);
    }
  }
}

// -------------------------------
$animation: reflection-pool;
// -------------------------------

[data-animation="#{$animation}"] {
  .menu-link:hover .char {
    animation: #{$animation} var(--duration) calc(var(--char-index) * (var(--duration) / -2)) linear alternate infinite;
  }

  @keyframes #{$animation} {
    from {
      font-variation-settings: "wght" var(--normal);
    }
    to {
      font-variation-settings: "wght" var(--bold);
    }
  }
}

// -------------------------------
$animation: jump;
// -------------------------------

[data-animation="#{$animation}"] {
  .menu-link:hover .char {
    transform-origin: 50% 100%;
    transition-delay: var(--delay);
    animation: #{$animation} calc(var(--duration) * 3.5) calc(var(--char-index) * 0.04s) var(--ease) forwards;
  }

  @keyframes #{$animation} {
    0% {
      animation-timing-function: linear;
    }
    30% {
      transform: translateY(2px) scaleY(0.9);
    }
    50% {
      transform: translateY(-30px) scaleY(1.25);
    }
    60% {
      transform: translateY(3px) scaleY(0.75);
      animation-timing-function: linear;
    }
    80% {
      transform: translateY(-1px);
      animation-timing-function: linear;
    }
  }
}

// -------------------------------
$animation: shadow;
// -------------------------------

[data-animation="#{$animation}"] {
  .menu-link:hover .char,
  .menu-link:hover .char::after {
    animation: calc(var(--duration) * 3) calc(var(--char-index) * 0.024s) var(--ease) forwards;
  }
  
  .menu-link:hover .char {
    transform-origin: 0 100%;
    transition-delay: var(--delay);
    animation-name: #{$animation};
  }

  .menu-link .char::after {
    visibility: visible;
    opacity: 0;
    transform: scaleY(0);
  }

  .menu-link:hover .char::after {
    transform-origin: 0 100%;
    animation-name: #{$animation}-after;
  }

  @keyframes #{$animation} {
    to {
      transform: scaleY(1.05);
    }
  }

  @keyframes #{$animation}-after {
    from {
      opacity: 0;
      transform: scaleY(0);
    }
    to {
      opacity: 0.25;
      transform: scaleY(-0.75) skewX(-30deg);
    }
  }
}

// -------------------------------
$animation: smile;
// -------------------------------

[data-animation="#{$animation}"] {
  .menu-link:hover .char {
    --d: calc(var(--duration) * 3);
    
    transition-duration: var(--d);
    animation: #{$animation} var(--d) var(--ease) forwards;
  }

  @keyframes #{$animation} {
    to {
      transform:
        translate(
          calc(10px * var(--distance-sine)),
          calc(-10px * var(--distance-percent))
        )
        rotate(calc(-25deg * var(--distance-sine)));
    }
  }
}

// -------------------------------
$animation: step-sequence;
// -------------------------------

[data-animation="#{$animation}"] {
  --opacity: 0.4;
  
  .menu-link:hover .char {
    opacity: var(--opacity);
    font-variation-settings: "wght" var(--normal);
    animation: #{$animation} calc((var(--duration) / 1.5) * var(--char-total)) calc(var(--char-index) * calc(var(--duration) / 1.5)) steps(2, end) infinite;
  }

  @keyframes #{$animation} {
    0% {
      opacity: 1;
      transform: translateY(-2px);
      font-variation-settings: "wght" var(--bold);
    }
    10%, 100% {
      opacity: var(--opacity);
      transform: translateY(0);
      font-variation-settings: "wght" var(--normal);
    }
  }
}

// -------------------------------
$animation: wave;
// -------------------------------

[data-animation="#{$animation}"] {
  .menu-link:hover .char {
    transition-delay: var(--delay);
    animation: #{$animation} calc(var(--duration) * 3) calc(var(--char-index) * 0.04s) ease-in-out alternate infinite;
  }

  @keyframes #{$animation} {
    50% {
      transform:
        translate(
          calc(-0.125em * var(--char-index)),
          calc(-0.25em * var(--char-index))
        )
        rotate(calc(-5deg * (var(--char-index) + 1)));
    }
  }
}
View Compiled
const nav = document.getElementById("nav");
const selectList = document.getElementById("animation-options");
const options = selectList.querySelectorAll("option");
const selectAnimation = (value) => nav.setAttribute("data-animation", value);

const init = () => {
  options.forEach(option => option.value = option.value.replace(/\s/g, "-"));
  selectAnimation(selectList.options[0].value);
};

Splitting();
init();

selectList.addEventListener("change", (e) => selectAnimation(e.target.value));

External CSS

  1. https://unpkg.com/splitting/dist/splitting.css
  2. https://unpkg.com/splitting/dist/splitting-cells.css

External JavaScript

  1. https://unpkg.com/splitting/dist/splitting.min.js