NEW
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
Learn more
Skip to content
Docs

GitHub Card - Shiny

A card component where a shiny background follows the mouse when hovered, as seen in GitHub's homepage

requires interactionhover
Loading...

Installation

CLI

pnpm dlx shadcn@latest add https://animata.design/r/card/github-card-shiny.json

Manual

Copy useMousePosition hook

import { useEffect } from "react";
 
export function useMousePosition(
  ref: React.RefObject<HTMLElement | null>,
  callback?: ({ x, y }: { x: number; y: number }) => void,
) {
  useEffect(() => {
    const handleMouseMove = (event: MouseEvent) => {
      const { clientX, clientY } = event;
      const { top, left } = ref.current?.getBoundingClientRect() || {
        top: 0,
        left: 0,
      };
 
      callback?.({ x: clientX - left, y: clientY - top });
    };
 
    const handleTouchMove = (event: TouchEvent) => {
      const { clientX, clientY } = event.touches[0];
      const { top, left } = ref.current?.getBoundingClientRect() || {
        top: 0,
        left: 0,
      };
 
      callback?.({ x: clientX - left, y: clientY - top });
    };
 
    ref.current?.addEventListener("mousemove", handleMouseMove);
    ref.current?.addEventListener("touchmove", handleTouchMove);
 
    const nodeRef = ref.current;
    return () => {
      nodeRef?.removeEventListener("mousemove", handleMouseMove);
      nodeRef?.removeEventListener("touchmove", handleTouchMove);
    };
  }, [ref, callback]);
}

Run the following command

It will create a new file github-card-shiny.tsx inside the components/animata/card directory.

mkdir -p components/animata/card && touch components/animata/card/github-card-shiny.tsx

Paste the code

Open the newly created file and paste the following code:

import { CheckCircle2 } from "lucide-react";
import { useCallback, useRef } from "react";
 
import { useMousePosition } from "@/hooks/use-mouse-position";
import { cn } from "@/lib/utils";
 
export default function GithubCardShiny({ className }: { className?: string }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const overlayRef = useRef<HTMLDivElement>(null);
 
  const update = useCallback(({ x, y }: { x: number; y: number }) => {
    if (!overlayRef.current) {
      return;
    }
 
    const { width, height } = overlayRef.current?.getBoundingClientRect() ?? {};
    const xOffset = x - width / 2;
    const yOffset = y - height / 2;
 
    overlayRef.current?.style.setProperty("--x", `${xOffset}px`);
    overlayRef.current?.style.setProperty("--y", `${yOffset}px`);
  }, []);
 
  useMousePosition(containerRef, update);
 
  return (
    <div
      ref={containerRef}
      className={cn(
        "group/shiny relative w-96 min-w-fit max-w-full overflow-hidden rounded-md border border-border bg-zinc-700 p-6 text-zinc-200 shadow-lg",
        className,
      )}
    >
      <div
        ref={overlayRef}
        // Adjust height & width as required
        className="z-0 absolute h-64 w-64 rounded-full bg-white opacity-0 bg-blend-soft-light blur-3xl transition-opacity group-hover/shiny:opacity-20"
        style={{
          transform: "translate(var(--x), var(--y))",
        }}
      />
 
      <div className="font-mono text-sm">
        cmake.yml
        <div className="text-xs text-zinc-400">on: push</div>
      </div>
 
      <div className="z-10 mt-10 flex w-full min-w-fit flex-col gap-2 rounded-md bg-zinc-600 p-4 shadow-2xl">
        {[
          {
            title: "Install dependencies",
            time: "1m 20s",
          },
          {
            title: "Build",
            time: "2m 10s",
          },
          {
            title: "Test",
            time: "1m 30s",
          },
        ].map((step) => {
          return (
            <div className="flex w-full items-center gap-2" key={step.title}>
              <CheckCircle2 className="shrink-0 fill-green-400 text-zinc-600" />
              <strong className="text-xs md:shrink-0 md:text-base">{step.title}</strong>
 
              <span className="ml-auto inline-block shrink-0 text-xs opacity-75">{step.time}</span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

Credits

Built by hari

Inspired by GitHub's homepage