Search

Ctrl + K

Ripple

This document outlines the steps to create Ripple component styled withTailwind CSS and using some npm dependency libraries.

Prerequisites

  • Node.js and npm installed on your machine.
  • Tailwind CSS installed in your project.
  • CVA(class-variance-authority) is a utility for managing CSS class names based on various conditions.
  • clsx is a tiny utility for constructing className strings conditionally.
Step 1: Create css styles
1// style.css
2span.ripple {
3  position: absolute;
4  border-radius: 50%;
5  transform: scale(0);
6  animation: ripple 600ms linear;
7}
8
9@keyframes ripple {
10  to {
11    transform: scale(4);
12    opacity: 0;
13  }
14}
15
16
17
18
19
20
21
Step 2: Create Ripple component
1// ripple.component.tsx
2import clsx from "clsx";
3import { useCallback } from "react";
4import "./style.css";
5
6export interface RippleProps extends React.HTMLAttributes<HTMLDivElement> {
7  rippleColor: string;
8}
9
10const Ripple: React.FC<RippleProps> = ({
11  rippleColor,
12  children,
13  className,
14  ...props
15}) => {
16  const handleClick = useCallback(
17    (event: MouseEvent) => {
18      const btn = event.currentTarget as HTMLDivElement;
19
20      const circle = document.createElement("span");
21      const diameter = Math.max(btn.clientWidth, btn.clientHeight);
22      const radius = diameter / 2;
23
24      const rect = btn.getBoundingClientRect();
25
26      const x = event.clientX - rect.left;
27      const y = event.clientY - rect.top;
28      const l = x - radius;
29      const t = y - radius;
30
31      circle.style.width = circle.style.height = `${diameter}px`;
32      circle.style.left = `${l}px`;
33      circle.style.top = `${t}px`;
34      circle.style.backgroundColor = rippleColor;
35      circle.classList.add("ripple");
36
37      circle.addEventListener("animationend", circle.remove);
38
39      const ripples = btn.getElementsByClassName("ripple");
40
41      for (const ripple of ripples) {
42        btn.removeChild(ripple);
43      }
44
45      btn.appendChild(circle);
46    },
47    [rippleColor],
48  );
49
50  const refCallback = useCallback(
51    (el: HTMLDivElement | null) => {
52      if (!el) return;
53
54      el.addEventListener("click", handleClick);
55      return () => {
56        el.removeEventListener("click", handleClick);
57      };
58    },
59    [handleClick],
60  );
61
62  return (
63    <div
64      ref={refCallback}
65      className={cn("relative w-fit overflow-hidden", className)}
66      {...props}
67    >
68      {children}
69    </div>
70  );
71};
72
73export default Ripple;
74