Playground

Spotlight

April 2025

Vietnam’s Reunification Day

Apr 30, 1975 - Apr 30, 2025

Reunification Day is more than a historical event—it’s a celebration of resilience, unity, and the Vietnamese spirit.

On Apr 30, 1975

Saigon fell, ending the Vietnam War and reuniting Vietnam, symbolizing peace after decades of conflict.

"use client";
import {cn} from "@/app/_lib/cn";
import React, {MouseEvent, ReactNode, useEffect, useRef, useState} from "react";

interface MousePosition {
    x: number;
    y: number;
}

export default function Spotlight() {
    const containerRef = useRef<HTMLDivElement>(null);
    const [position, setPosition] = useState<MousePosition | undefined>({
        x: 0,
        y: 0,
    });
    const [isMouseLeave, setIsMouseLeave] = useState(true);
    const handleMouseMove = (e: MouseEvent<HTMLDivElement>) => {
        const {clientX, clientY} = e;
        setIsMouseLeave(false);
        setPosition({
            x: clientX,
            y: clientY,
        });
    };

    const handleMouseLeave = () => setIsMouseLeave(true);
    return (
        <article
            onMouseMove={handleMouseMove}
            onMouseLeave={handleMouseLeave}
            ref={containerRef}
            className="font-poppins group/article relative grid min-h-96 place-items-center rounded-xl border border-white/10 bg-white/5 p-4 md:aspect-square md:min-h-0">
            <div className="size-full overflow-hidden">
                <div className="grid h-full w-full grid-cols-4 grid-rows-16 gap-4 lg:grid-cols-3 lg:grid-rows-15">
                    <Card
                        className="col-span-full row-span-6 lg:col-span-2 lg:row-span-11"
                        containerMousePosition={position}
                        isMouseLeave={isMouseLeave}>
                        <div className="relative grid size-full place-items-center">
                            <div className="absolute top-13 right-4 w-6/10 text-right">
                                <h1 className="mb-2 text-lg lg:text-xl">
                                    Vietnam’s Reunification Day
                                </h1>
                                <p className="text-xs opacity-60">
                                    Apr 30, 1975 - Apr 30, 2025
                                </p>
                            </div>

                            <img
                                src="https://ik.imagekit.io/khoaphan/playground/Spotlight/VN-map.svg?updatedAt=1745730197368"
                                className="w-9/10 grayscale-100 transition-[filter] duration-300 group-hover/article:grayscale-50 hover:grayscale-0"
                            />
                        </div>
                    </Card>
                    <Card
                        className="col-span-full row-span-4 lg:col-span-1 lg:row-span-8"
                        containerMousePosition={position}
                        isMouseLeave={isMouseLeave}>
                        <div className="flex size-full flex-col justify-between gap-y-5 px-4 py-6 lg:p-3 lg:py-4">
                            <p className="text-sm lg:pt-2">
                                Reunification Day is more than a historical
                                event—it’s a celebration of resilience, unity,
                                and the Vietnamese spirit.
                            </p>
                            <img
                                src="https://ik.imagekit.io/khoaphan/playground/Spotlight/end-of-war.jpg?updatedAt=1745732134314"
                                className="rounded-lg brightness-75"
                            />
                        </div>
                    </Card>

                    <Card
                        className="col-span-full row-span-4 overflow-hidden lg:col-span-1 lg:row-span-7"
                        containerMousePosition={position}
                        isMouseLeave={isMouseLeave}>
                        <img
                            src="https://ik.imagekit.io/khoaphan/playground/Spotlight/VN-flag.svg?updatedAt=1745730193410"
                            className="size-full object-cover grayscale-35 transition-[filter] duration-300 will-change-[filter] group-hover/article:grayscale-25 hover:grayscale-0"
                        />
                    </Card>
                    <Card
                        className="col-span-full row-span-2 lg:col-span-2 lg:row-span-4"
                        containerMousePosition={position}
                        isMouseLeave={isMouseLeave}>
                        <div className="grid size-full place-items-center">
                            <div className="p-4">
                                <h2 className="mb-2 font-medium">
                                    On Apr 30, 1975
                                </h2>
                                <p className="text-sm">
                                    Saigon fell, ending the Vietnam War and
                                    reuniting Vietnam, symbolizing peace after
                                    decades of conflict.
                                </p>
                            </div>
                        </div>
                    </Card>
                </div>
            </div>
        </article>
    );
}
const Card = ({
    containerMousePosition,
    className,
    isMouseLeave,
    children,
}: {
    containerMousePosition?: MousePosition;
    className?: string;
    isMouseLeave?: boolean;
    children: ReactNode;
}) => {
    const spotlightRef = useRef<HTMLDivElement>(null);
    const [spotlightPosition, setSpotlightPosition] = useState({x: 0, y: 0});
    useEffect(() => {
        const rect = spotlightRef.current?.getBoundingClientRect();
        setSpotlightPosition({
            x: (containerMousePosition?.x || 0) - (rect?.left || 0),
            y: (containerMousePosition?.y || 0) - (rect?.top || 0),
        });
    }, [containerMousePosition?.x, containerMousePosition?.y]);
    return (
        <div
            className={cn(
                "relative overflow-hidden rounded-xl border-gray-500 backdrop-blur-2xl",
                className
            )}>
            <div
                className={cn(
                    "absolute inset-0 bg-white/10 transition-opacity duration-1000",
                    isMouseLeave && "opacity-0"
                )}
                style={{
                    backgroundImage: `radial-gradient(400px at ${spotlightPosition.x}px ${spotlightPosition.y}px ,rgba(255,255,255,0.3), transparent 70%)`,
                }}
            />
            <div className="relative h-full w-full p-px">
                <div className="h-full w-full overflow-hidden rounded-xl bg-black">
                    {children}
                </div>
            </div>
            <div
                ref={spotlightRef}
                style={{
                    backgroundImage: `radial-gradient(800px at ${spotlightPosition.x}px ${spotlightPosition.y}px ,rgba(255,255,255,0.06), transparent 70%)`,
                }}
                className={cn(
                    "pointer-events-none absolute inset-0 transition-opacity duration-1000",
                    isMouseLeave && "opacity-0"
                )}
            />
        </div>
    );
};
Next

Jelly Cursor