Skip to content

Ripple Press

Subtle press ripple expanding from the pointer inside any rounded child (button, card), in the current text color or primary tone, fading out quickly without blocking clicks.

minimal/effect/ripple-press
Open ↗

Source

"use client";

import { useRef, useState, type PointerEvent, type ReactNode } from "react";
import { cn } from "@/lib/utils";

export interface RipplePressProps {
  children: ReactNode;
  /** Ripple color token. */
  tone?: "current" | "primary";
  className?: string;
}

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

/** Subtle material-like ripple expanding from the press point inside its (rounded) child. Fades out, never blocks clicks. */
export function RipplePress({ children, tone = "current", className }: RipplePressProps) {
  const [ripples, setRipples] = useState<Ripple[]>([]);
  const next = useRef(0);

  const onPointerDown = (e: PointerEvent<HTMLSpanElement>) => {
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const r = e.currentTarget.getBoundingClientRect();
    const size = Math.max(r.width, r.height) * 2.2;
    const id = next.current++;
    setRipples((rs) => [...rs, { id, x: e.clientX - r.left - size / 2, y: e.clientY - r.top - size / 2, size }]);
    window.setTimeout(() => setRipples((rs) => rs.filter((it) => it.id !== id)), 650);
  };

  return (
    <span className={cn("relative isolate inline-flex overflow-hidden rounded-da-md", className)} onPointerDown={onPointerDown}>
      {children}
      <span aria-hidden className="pointer-events-none absolute inset-0 z-10">
        {ripples.map((r) => (
          <span
            key={r.id}
            className={cn("minimal-ripple absolute rounded-full", tone === "primary" ? "bg-da-primary/25" : "bg-current opacity-20")}
            style={{ left: r.x, top: r.y, width: r.size, height: r.size }}
          />
        ))}
      </span>
      <style href="minimal-ripple" precedence="default">{`
        @keyframes minimal-ripple { from { scale: 0; opacity: .35 } to { scale: 1; opacity: 0 } }
        .minimal-ripple { animation: minimal-ripple .6s cubic-bezier(.16,1,.3,1) forwards }
      `}</style>
    </span>
  );
}

export default RipplePress;

modules/minimal/effect/ripple-press/index.tsx

Props

PropTypeDefaultDescription
children*ReactNode—Pressable element.
tone"current" | "primary""current"Ripple color.
classNamestring—Classes (match the child radius).

Other micro interaction variants in Minimal

Micro interaction in other art directions

Animated

Click Burst

Micro-interaction wrapper that fires a burst of outlined square shards in DA colors from the click point (or from the center on Enter/Space), spinning outward in stepped motion.

BrutalistMicro interaction
Animated

Cursor Follower

Region where a tilted, outlined sticker label trails the mouse pointer with a springy lag and pops in/out on enter/leave. Mouse only; add cursor-none on the region to replace the cursor.

BrutalistMicro interaction
Animated

Shake Feedback

Error micro-interaction wrapper: shakes its content horizontally in hard steps and flashes a thick danger outline each time shakeKey changes (wrong password, invalid code). Children keep their state.

BrutalistMicro interaction
Animated

Stack Hover

Micro-interaction wrapper: colored slabs hidden behind a card or button fan out diagonally on hover and keyboard focus, in stepped motion, like a stack of printed sheets. Pure CSS.

BrutalistMicro interaction
Animated

Glare Card

Glass card where a soft specular glare and a luminous primary rim follow the mouse pointer (CSS variables, masked border), like light sliding over real glass.

GlassMicro interaction
Animated

Gradient Border

Highlight wrapper with a rotating conic-gradient border (primary → accent → pink) and optional soft glow around a frosted inner surface. Pure CSS via a registered @property angle.

GlassMicro interaction
Animated

Liquid Button

Frosted pill button that fills with a wavy primary→accent liquid rising from the bottom on hover or keyboard focus while the label turns white. Pure CSS.

GlassMicro interaction
Animated

Tilt Card

Frosted card that tilts in 3D toward the pointer on a spring while a specular glare slides across the glass; eases back flat on leave. Motion values only, no re-render per move.

GlassMicro interaction
Animated

Copy Email

Big email address that copies itself on click: the address swaps to “Copied to clipboard” with a signal dot for 1.8s via a vertical slide; announced to screen readers.

Mono CleanMicro interaction
Animated

Cursor Label

Hover area where a small ink pill with a mono label (“View ↗”) follows the pointer with slight lag; the native cursor stays visible. Mouse only; nothing on touch.

Mono CleanMicro interaction
Animated

Invert Hover

Row/button whose ink fill wipes in from the left on hover or focus while the text inverts (mix-blend-difference) — the signature interaction for index lists.

Mono CleanMicro interaction
Animated

Square Switch

Square toggle: a hairline rectangle with a square knob that snaps across with the emphasized ease, filling ink when on; mono ON/OFF state label; role=switch.

Mono CleanMicro interaction
Animated

Copy Feedback

Copyable mono field (API key, IBAN, invoice link): click copies, the icon cross-fades to a green check and a “Copied” tooltip pops for 1.6s; announced to screen readers.

Neo CorporateMicro interaction
Animated

Press Ripple

Button with a restrained material-style ripple: a soft circle expands from the press point and fades, plus a 1px press-down; disabled with reduced motion.

Neo CorporateMicro interaction
Animated

Spotlight Border

Card whose 1px border lights up in primary around the pointer (a radial highlight follows the cursor along the edge) with a faint inner glow; plain bordered card on touch.

Neo CorporateMicro interaction
Animated

Toggle Save

Settings row with an auto-saving switch: the knob slides, an inline status shows a spinner “Saving…” then a green “Saved” check that fades after 2s (aria-live).

Neo CorporateMicro interaction
Animated

Breathe Button

Calm primary CTA that slowly “breathes”: a soft sage halo expands and fades around the pill every few seconds; pauses on hover and with reduced motion. Pure CSS.

Organic SoftMicro interaction
Animated

Leaf Like

Appreciation button: pressing fills the leaf with sage, bumps it with a soft overshoot and releases three small leaves that float up and fade; counter updates.

Organic SoftMicro interaction
Animated

Sprout Progress

Progress bar with a plant riding the fill: a rounded sage track whose leading edge carries a sprout that grows leaves at 33% and 66% and blooms at 100%.

Organic SoftMicro interaction
Animated

Water Ripple

Surface that answers a tap like still water: three concentric sage rings spread from the pointer and fade. Wrap any card or panel.

Organic SoftMicro interaction
Animated

Check Burst

Satisfying task checkbox: the round box fills mint with a bouncy check and a ring of pastel dots bursts outward when ticked; the label strikes through.

Soft FlatMicro interaction
Animated

Heart Like

Like button: the heart pops with a springy scale, fills red and throws a ring of tiny hearts; the count ticks and the pill tints.

Soft FlatMicro interaction
Animated

Hover Lift

Card wrapper that lifts, tilts a couple of degrees and grows a soft wide shadow on hover or keyboard focus-within, with a springy ease.

Soft FlatMicro interaction
Animated

Squish Button

Chunky “gummy” pill button with a flat darker bottom lip that squishes down (scales wider/shorter, lip disappears) when pressed and springs back. CSS only.

Soft FlatMicro interaction