"use client"; import { useEffect, useRef } from "react"; import { cn } from "@/lib/utils"; /** * Vertical-bar "barcode" — a Scandinavian structural flourish. Heights are * deterministic (seeded by index, no Math.random) so SSR and client match. Bars * rise in sequence when the strip scrolls into view; reduced-motion shows them * settled. Decorative only (aria-hidden). */ export function Barcode({ count = 48, className, barClassName, seed = 1, }: { count?: number; className?: string; barClassName?: string; /** Varies the height pattern between instances. */ seed?: number; }) { const ref = useRef(null); // Deterministic, organic-looking height in [0.18, 1] from two offset sines. // Rounded to a fixed precision so the server and client render identical // strings (avoids a hydration mismatch on the inline height). const heightAt = (i: number) => { const a = Math.sin((i + seed * 7) * 1.3) * 0.5 + 0.5; const b = Math.sin((i + seed * 3) * 0.41) * 0.5 + 0.5; return (0.18 + (a * 0.6 + b * 0.4) * 0.82) * 100; }; useEffect(() => { const el = ref.current; if (!el) return; if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; const bars = Array.from(el.children) as HTMLElement[]; bars.forEach((b) => { b.style.transformOrigin = "bottom"; b.style.transform = "scaleY(0.04)"; b.style.opacity = "0"; }); const io = new IntersectionObserver( (entries) => { for (const e of entries) { if (!e.isIntersecting) continue; bars.forEach((b, i) => { b.style.transition = "transform 700ms cubic-bezier(0.16,1,0.3,1), opacity 700ms cubic-bezier(0.16,1,0.3,1)"; b.style.transitionDelay = `${Math.min(i, 60) * 14}ms`; b.style.transform = "scaleY(1)"; b.style.opacity = "1"; }); io.disconnect(); } }, { threshold: 0.15 }, ); io.observe(el); return () => io.disconnect(); }, [count, seed]); return (
{Array.from({ length: count }, (_, i) => ( ))}
); }