"use client"; import * as React from "react"; import { motion } from "framer-motion"; import { ArrowRight } from "lucide-react"; import Link from "next/link"; interface ButtonProps { href: string; children: React.ReactNode; variant?: "primary" | "outline" | "ghost"; size?: "normal" | "large"; className?: string; showArrow?: boolean; onClick?: (e: React.MouseEvent) => void; [key: string]: any; } /** * Premium Button: Pill-shaped, binary-accent hover effect, unified design. * * On hover: * - A stream of binary characters scrolls across the button background * - Primary: white binary on dark bg * - Outline: blue binary on transparent bg * - Subtle, fast, and satisfying */ export const Button: React.FC = ({ href, children, variant = "primary", size = "normal", className = "", showArrow = true, onClick, ...props }) => { const [hovered, setHovered] = React.useState(false); const [displayText, setDisplayText] = React.useState(null); const contentRef = React.useRef(null); // Binary scramble on hover React.useEffect(() => { if (!hovered) { setDisplayText(null); return; } const original = contentRef.current?.textContent || ""; if (!original) return; const chars = original.split(""); const total = 20; let frame = 0; const iv = setInterval(() => { frame++; const s = chars.map((c, i) => { if (c === " ") return " "; const settle = (frame / total) * chars.length; if (i < settle) return c; return Math.random() > 0.5 ? "1" : "0"; }); setDisplayText(s.join("")); if (frame >= total) { setDisplayText(null); clearInterval(iv); } }, 1000 / 60); return () => clearInterval(iv); }, [hovered]); const [binaryStr, setBinaryStr] = React.useState( "0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1", ); React.useEffect(() => { setBinaryStr( Array.from({ length: 60 }, () => (Math.random() > 0.5 ? "0" : "1")).join( " ", ), ); }, []); const base = "relative inline-flex items-center justify-center gap-3 overflow-hidden rounded-full font-bold uppercase tracking-[0.15em] transition-all duration-300 group cursor-pointer"; const sizes: Record = { normal: "px-8 py-4 text-[10px]", large: "px-10 py-5 text-[11px]", }; const variants: Record = { primary: "bg-slate-900 text-white hover:shadow-xl hover:shadow-slate-900/20 hover:-translate-y-0.5", outline: "border border-slate-200 bg-transparent text-slate-900 hover:border-slate-400 hover:-translate-y-0.5", ghost: "bg-transparent text-slate-500 hover:text-slate-900", }; // Binary stream overlay colors by variant const binaryColor = variant === "primary" ? "rgba(255,255,255,0.12)" : variant === "outline" ? "rgba(59,130,246,0.15)" : "rgba(148,163,184,0.15)"; const inner = ( setHovered(true)} onMouseLeave={() => setHovered(false)} className={`${base} ${sizes[size]} ${variants[variant]} ${className}`} > {/* Binary stream hover overlay */} {binaryStr} {binaryStr} {/* Shimmer line on hover (top edge) */} {/* Content */} {displayText ?? children} {showArrow && ( )} ); if (href.startsWith("#")) { return ( { if (onClick) onClick(e); e.preventDefault(); document.querySelector(href)?.scrollIntoView({ behavior: "smooth" }); }} {...props} > {inner} ); } return ( {inner} ); };