Buttons

Effect Button

One button, seven visual effects — shimmer, shine, spotlight, glow, gradient, glass and ripple as cva variants.

Preview in your theme

Loading preview…

"use client"

import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

/**
 * Keyframes ship inside the component via React 19 hoisted <style> —
 * no tailwind config edits, and duplicates dedupe by href.
 */
const KEYFRAMES = `@keyframes eb-shimmer{from{transform:translateX(-100%) skewX(-12deg)}to{transform:translateX(400%) skewX(-12deg)}}
@keyframes eb-glow{0%,100%{box-shadow:0 0 14px 0 color-mix(in oklab,var(--primary) 50%,transparent)}50%{box-shadow:0 0 30px 8px color-mix(in oklab,var(--primary) 68%,transparent)}}
@keyframes eb-ripple{to{transform:translate(-50%,-50%) scale(3);opacity:0}}`

Installation

npx shadcn@latest add https://ui.zyeon.ai/r/effect-button.json

Prompt

The prompt behind this component — paste it into your AI assistant to recreate or adapt it.

Build a React + TypeScript + Tailwind "EffectButton" component using
class-variance-authority (cva) for variants.

Contract
- Export a forwardRef button extending React.ButtonHTMLAttributes plus
  VariantProps<typeof effectButtonVariants>.
- One prop drives everything: effect = "shimmer" | "shine" | "spotlight" |
  "glow" | "gradient" | "glass" | "ripple" (default "shimmer").
- Preserve all consumer handlers (onClick / onMouseMove / onMouseEnter /
  onMouseLeave): internal effect logic runs first, then the consumer's handler.

Behavior
- shimmer: a 1/3-width skewed light bar sweeps across the surface on an
  infinite 2.75s keyframe loop.
- shine: the same light bar, but idle off-canvas; on hover it sweeps once
  (transition duration 700ms on hover-in, 0ms snap-back on hover-out).
- spotlight: track the cursor via onMouseMove into local {x, y} state; render a
  radial-gradient that follows the cursor, fading in/out on enter/leave (300ms).
- glow: a pulsing box-shadow keyframe (2.4s loop) around the button.
- gradient: background-size 200% with background-position left→right on hover.
- glass: translucent background + backdrop-blur + subtle border; no animation.
- ripple: on click, append {id, x, y, size} to state and render an expanding
  circle from the click point (600ms, scale 0→3, fade out); remove each ripple
  in onAnimationEnd — no timers to leak. Skip spawning entirely when
  prefers-reduced-motion matches.
- Ship the three @keyframes inside the component with a React 19 hoisted
  <style href="..." precedence="medium"> tag so no Tailwind config edits are
  needed and duplicate buttons dedupe to one style tag.

Rendering & styling
- Semantic tokens only: bg-primary / text-primary-foreground for solid skins,
  primary-foreground at low opacity for light-bar and ripple overlays,
  color-mix(in oklab, var(--primary) N%, transparent) for glow shadows,
  bg-background + border-border for glass. No hardcoded colors — every effect
  adapts to the host theme and dark mode.
- All overlays: absolute, pointer-events-none, aria-hidden, rendered under a
  z-10 label span; decorative animation layers are motion-reduce:hidden.
- Base classes via cva: rounded-lg px-6 py-2.5, focus-visible ring,
  disabled:opacity-50, active:scale-[0.98]; merge consumer className via cn().

Customization levers
- Add a skin: one cva variant entry + (optionally) one overlay span — the
  contract and every other effect stay untouched.
- Sweep speed / glow intensity: the keyframe durations and the color-mix
  percentages are the only knobs; keep them paired (slower sweep reads better
  with lower opacity).
- Size: override px/py via className, or add a cva `size` axis if you need a
  documented scale (sm / md / lg).
- Ripple color: swap bg-primary-foreground/30 for bg-primary/30 when placing
  the ripple variant on secondary surfaces.
- Spotlight radius: the 120px in the radial-gradient; 80–150px is the useful
  range (match your button size).

Concepts

  • One contract, many skins — the effect prop selects a cva variant plus (at most) one overlay span; swapping visual styles is a prop change, so design iteration never touches call sites.
  • Overlay layering — every effect renders into an absolutely-positioned, aria-hidden, pointer-events-none layer beneath a z-10 label, so effects can never intercept clicks or confuse screen readers.
  • Hoisted keyframes — React 19 dedupes <style href precedence> tags into the document head, letting a registry component ship its own @keyframes with zero Tailwind config edits.
  • Mouse-tracked spotlight — cursor position feeds a radial-gradient through local state; color-mix on --primary-foreground keeps the light readable on the primary surface in both color schemes.
  • Self-cleaning ripples — each ripple removes itself in onAnimationEnd instead of setTimeout, so unmounting mid-animation leaks nothing.
  • Reduced-motion honesty — looping and pointer overlays are motion-reduce:hidden, and ripples are never spawned under prefers-reduced-motion; the button itself keeps full function.

On This Page