Docs
GitHub Card - ShinyGitHub 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, useRef } from "react";
type Point = {
x: number;
y: number;
};
export function useMousePosition(
ref: React.RefObject<HTMLElement | null>,
callback?: (point: Point) => void,
) {
const callbackRef = useRef(callback);
const rectRef = useRef<DOMRect | null>(null);
const frameRef = useRef<number | null>(null);
const latestPointRef = useRef<Point | null>(null);
callbackRef.current = callback;
useEffect(() => {
const node = ref.current;
if (!node) return;
const updateRect = () => {
rectRef.current = node.getBoundingClientRect();
};
const flush = () => {
frameRef.current = null;
if (latestPointRef.current) {
callbackRef.current?.(latestPointRef.current);
}
};
const handlePointerMove = (event: PointerEvent) => {
if (!rectRef.current) {
updateRect();
}
const currentRect = rectRef.current;
if (!currentRect) return;
latestPointRef.current = {
x: event.clientX - currentRect.left,
y: event.clientY - currentRect.top,
};
if (frameRef.current === null) {
frameRef.current = requestAnimationFrame(flush);
}
};
updateRect();
node.addEventListener("pointerenter", updateRect);
node.addEventListener("pointermove", handlePointerMove, {
passive: true,
});
window.addEventListener("resize", updateRect);
window.addEventListener("scroll", updateRect, {
passive: true,
capture: true,
});
return () => {
node.removeEventListener("pointerenter", updateRect);
node.removeEventListener("pointermove", handlePointerMove);
window.removeEventListener("resize", updateRect);
window.removeEventListener("scroll", updateRect, true);
if (frameRef.current !== null) {
cancelAnimationFrame(frameRef.current);
}
};
}, [ref]);
}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.tsxPaste 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