loading
Just a moment.

Button Ripple

November 2024


import React, { MouseEvent } from "react";

type ButtonRippleEffectProps = {
  variant: "neutral" | "blue" | "green" | "red";
};

const spanCn = {
  neutral: "bg-neutral-100 dark:bg-neutral-600",
  blue: "bg-blue-100",
  green: "bg-green-100",
  red: "bg-red-100",
};

const buttonCn = {
  neutral: "bg-[#101010] text-neutral-50 border-white/10",
  blue: "bg-cyan-600 text-cyan-200 border-cyan-400/50",
  green: "bg-green-600 text-green-200 border-green-400/50",
  red: "bg-red-600 text-red-200 border-red-400/50",
};

export const ButtonRippleEffect: React.FC<ButtonRippleEffectProps> = ({
  variant,
}) => {
  const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
    const button = e.currentTarget;
    const x = e.clientX - button.getBoundingClientRect().left;
    const y = e.clientY - button.getBoundingClientRect().top;
    const ripples = document.createElement("span");

    ripples.style.cssText = `
      left: ${x}px;
      top: ${y}px;
      position: absolute;
      transform: translate(-50%, -50%);
      pointer-events: none;
      border-radius: 50%;
      animation: ripple 1.4s linear infinite;
      transition: 0.5s;`;

    ripples.classList.add(...spanCn[variant].split(" "));

    button.appendChild(ripples);

    setTimeout(() => {
      ripples.remove();
    }, 1400);
  };

  return (
    <>
      <button
        className={`relative overflow-hidden rounded-full px-6 py-1 text-sm leading-8 border-solid border transition-colors duration-300 ${buttonCn[variant]}`}
        onClick={handleClick}
      >
        View on Git
      </button>
      <style jsx>{`
        @keyframes ripple {
          0% {
            width: 0;
            height: 0;
            opacity: 0.5;
          }

          100% {
            width: 520px;
            height: 520px;
            opacity: 0;
          }
        }
      `}</style>
    </>
  );
};

export default ButtonRippleEffect;