RippleButton
Material-style click ripple effect via DOM injection and CSS animations. No extra dependencies.
Use and inspectUse this pattern for the stated interaction and keep its keyboard path visible. Reset preview remounts the local example; it does not establish production persistence or a backend integration. Add a reduced-motion fallback where the source animates.Next: inspect the source and adapt its event or data boundary.
RippleButtonMaterial-style click ripple effect via DOM injection and CSS animations. No extra dependencies.
"use client";
import { useRef } from "react";
import type { MouseEvent } from "react";
type RippleButtonProps = {
children: React.ReactNode;
onClick?: () => void;
variant?: "solid" | "outline";
};
export function RippleButton({
children,
onClick,
variant = "solid",
}: RippleButtonProps) {
const ref = useRef<HTMLButtonElement>(null);
function handleClick(e: MouseEvent<HTMLButtonElement>) {
const btn = ref.current;
if (!btn) return;
const rect = btn.getBoundingClientRect();
const ripple = document.createElement("span");
const size = Math.max(rect.width, rect.height) * 2;
ripple.className = "sk-ripple-wave";
ripple.style.width = ripple.style.height = size + "px";
ripple.style.left = e.clientX - rect.left - size / 2 + "px";
ripple.style.top = e.clientY - rect.top - size / 2 + "px";
btn.appendChild(ripple);
ripple.addEventListener("animationend", () => ripple.remove());
onClick?.();
}
return (
<button
ref={ref}
className={`sk-ripple-btn sk-ripple-btn--${variant}`}
onClick={handleClick}
>
{children}
</button>
);
}
/* CSS required:
@keyframes sk-ripple {
to { transform: scale(4); opacity: 0; }
}
.sk-ripple-btn { position: relative; overflow: hidden; }
.sk-ripple-wave {
position: absolute; border-radius: 50%;
background: rgba(255,255,255,0.3); pointer-events: none;
transform: scale(0); animation: sk-ripple 0.5s ease-out;
}
*/Props
| Prop | Type | Default | Description |
|---|---|---|---|
children* | React.ReactNode | — | Button label content |
variant | "solid" | "outline" | "solid" | Visual style variant |
onClick | () => void | — | Optional click handler |