All components

Glare Card

Sheet of light tracking the cursor, like a foil trading card.

0.8 KB gzippedZero dependenciesReduced-motion safeBase UI & Radix compatible

Glare Card

Move the cursor across this card.

Installation

npx shadcn@latest add https://innsite.com.mx/r/glare-card.json

Source

"use client";

import { useRef, useState } from "react";

import { cn } from "@/lib/utils";

interface GlareCardProps {
  children: React.ReactNode;
  className?: string;
}

/**
 * Card with a sheet of light that tracks the cursor across its surface, like
 * a foil trading card. CSS custom properties do the work — no dependency.
 */
export function GlareCard({ children, className }: GlareCardProps) {
  const ref = useRef<HTMLDivElement>(null);
  const [active, setActive] = useState(false);

  const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
    const element = ref.current;
    if (!element) return;
    const rect = element.getBoundingClientRect();
    element.style.setProperty(
      "--glare-x",
      `${((event.clientX - rect.left) / rect.width) * 100}%`
    );
    element.style.setProperty(
      "--glare-y",
      `${((event.clientY - rect.top) / rect.height) * 100}%`
    );
  };

  return (
    <div
      ref={ref}
      data-slot="glare-card"
      onMouseMove={handleMouseMove}
      onMouseEnter={() => setActive(true)}
      onMouseLeave={() => setActive(false)}
      className={cn(
        "group relative overflow-hidden rounded-2xl border bg-card p-6 [--glare-x:50%] [--glare-y:50%]",
        className
      )}
    >
      <span
        aria-hidden
        style={{ opacity: active ? 1 : 0 }}
        className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_var(--glare-x)_var(--glare-y),color-mix(in_oklch,var(--brand)_28%,transparent),transparent_55%)] transition-opacity duration-300 motion-reduce:transition-none"
      />
      <div className="relative">{children}</div>
    </div>
  );
}