<script>
console.clear();

const EVENT = 'mousedown';
  
function removeRipple(event) {
  const ripple = this;
  ripple.remove();
  ripple.addEventListener('animationend', removeRipple);
}
  
function createRipple(event) {
  const target = event.currentTarget;
  const { top, left } = target.getBoundingClientRect();
  const { clientWidth, clientHeight } = target;
  const diameter = Math.sqrt(clientWidth ** 2 + clientHeight ** 2);
  const radius = diameter / 2;
  const localX = event.clientX - left;
  const localY = event.clientY - top;

  const ripple = document.createElement('span');
  ripple.setAttribute('class', 'v-ripple');
  ripple.setAttribute(
    'style',
    `
      width: ${diameter}px;
      height: ${diameter}px;
      left: ${localX - radius}px;
      top: ${localY - radius}px;
    `,
  );

  ripple.addEventListener('animationend', removeRipple);
  target.appendChild(ripple);
}
  
const ripple = {
  mounted(el) {
    el.addEventListener(EVENT, createRipple);
  },
  unmounted(el) {
    el.removeEventListener(EVENT, createRipple);
  },
};  
  
export default {
  directives: { ripple },
};
</script>

<template>
  <div class="container">
    <button v-ripple type="button" class="button">BUTTON</button>
    <button v-ripple type="button" class="button is-outline">BUTTON</button>
    <button v-ripple type="button" class="button button-primary">BUTTON</button>
    <button v-ripple type="button" class="button button-primary is-outline">BUTTON</button>
  </div>
</template>

<style>
  /* reboot */
  *, *::after, *::before {
    box-sizing: border-box;
  }
  
  #app {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 5px;
    height: 100vh;
    overflow: hidden;
    font-family: "Roboto", sans-serif;
  }
  
  button {
    margin: 0;
    padding: 0;
    outline: none;
    border: none;
    background: transparent;
    font-family: inherit;
  }
  
  /* layout */
  .container {
    min-width: 320px;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }
  
  /* button */
  .button {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.5rem 1rem;
    overflow: hidden;
    font-size: 1rem;
    color: #6B7280;
    cursor: pointer;
    background-color: #eee;
    border-radius: 0.25rem;
    border: 1px solid transparent;
    box-shadow: 1px 2px 1px -1px rgba(0, 0, 0, 0.2);
  }
  
  .button:active {
    box-shadow: inset 1px 1px 2px 0 rgba(0, 0, 0, 0.2);
  }
  
  .button-primary {
    color: #eee;
    background: #3B82F6;
  }
  
  .button.is-outline {
    background: transparent;
    border: 1px solid #6B7280;
  }
  
  .button-primary.is-outline {
    border-color: #3B82F6;
    color: #3B82F6;
  }
  
  /* ripple effect */
  .v-ripple {
    position: absolute;
    background-color: currentColor;
    border-radius: 50%;
    opacity: 0.2;
    transform: scale(0);
    animation: v-ripple-animation 0.6s linear;
  }

  @keyframes v-ripple-animation {
    to {
      opacity: 0;
      transform: scale(4);
    }
  }
</style>

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.