This document outlines the steps to create Ripple
component styled withTailwind CSS and using some npm dependency libraries.
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.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
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