Skip to content
All Taste components

Animated buttons

Ripple button

Buttons that answer a press with a ring of colour spreading from where you touched.

solid · outline · icon

Press and hold any of them
"use client";

/* globals.css
@keyframes ripple { from { transform: scale(0); opacity: .35; } to { transform: scale(1); opacity: 0; } }
@keyframes ripple-calm { from { opacity: .25; } to { opacity: 0; } }
*/
import { useState } from "react";
import { cn } from "@/lib/utils";

type Ripple = { id: number; x: number; y: number; size: number };

export function RippleButton({ className, children, onPointerDown, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>) {
  const [ripples, setRipples] = useState<Ripple[]>([]);

  function press(e: React.PointerEvent<HTMLButtonElement>) {
    const r = e.currentTarget.getBoundingClientRect();
    const size = Math.max(r.width, r.height) * 2.2;
    setRipples((rs) => [...rs, { id: e.timeStamp, x: e.clientX - r.left - size / 2, y: e.clientY - r.top - size / 2, size }]);
    onPointerDown?.(e);
  }

  return (
    <button
      type="button"
      onPointerDown={press}
      className={cn(
        "relative isolate inline-flex h-11 items-center justify-center gap-1.5 overflow-hidden rounded bg-primary px-5 text-[15px] font-medium text-primary-fg hover:bg-primary-hover",
        "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
        className,
      )}
      {...props}
    >
      {children}
      {ripples.map((r) => (
        <span
          key={r.id}
          aria-hidden
          onAnimationEnd={() => setRipples((rs) => rs.filter((x) => x.id !== r.id))}
          className="pointer-events-none absolute rounded-full bg-current animate-[ripple_.65s_cubic-bezier(.2,.7,.1,1)_forwards] motion-reduce:animate-[ripple-calm_.2s_forwards]"
          style={{ left: r.x, top: r.y, width: r.size, height: r.size }}
        />
      ))}
    </button>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.