Logo Ticker Infinite
blockmarketingInfinite marquee brand partner ticker with sector category filters, speed and pause controls, and interactive telemetry hover cards.
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/logo-ticker-infinite.json$npx shadcn@latest add https://uipkge.dev/r/react/logo-ticker-infinite.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/logo-ticker-infinite.json$bunx shadcn@latest add https://uipkge.dev/r/react/logo-ticker-infinite.jsonNamed registry:
npx shadcn@latest add @uipkge-react/logo-ticker-infiniteInstalls to:components/blocks/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
className | string | — | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
BrandPartnerinterface BrandPartner {
id: string
name: string
category: 'devtools' | 'fintech' | 'enterprise'
role: string
metric: string
region: string
}npm dependencies
Includes
Files installed (1)
components/blocks/LogoTickerInfinite.tsx8.5 kB
'use client' import * as React from 'react' import { Pause, Play } from 'lucide-react' import { Badge } from '@/components/ui/badge' import { cn } from '@/lib/utils' interface BrandPartner { id: string name: string category: 'devtools' | 'fintech' | 'enterprise' role: string metric: string region: string } const partners: BrandPartner[] = [ { id: 'vercel', name: 'Vercel Edge', category: 'devtools', role: 'Global Edge Ingestion', metric: '99.95% SLA', region: 'Global', }, { id: 'supabase', name: 'Supabase Postgres', category: 'devtools', role: 'Real-Time Replication', metric: '1.2M QPS', region: 'US-East', }, { id: 'linear', name: 'Linear Method', category: 'enterprise', role: 'Issue Sync Engine', metric: '< 20ms Sync', region: 'Global', }, { id: 'stripe', name: 'Stripe Billing', category: 'fintech', role: 'Usage Metering Gateway', metric: '$120M+ Processed', region: 'Global', }, { id: 'resend', name: 'Resend SMTP', category: 'devtools', role: 'Transactional Dispatch', metric: '200M msgs/mo', region: 'US-West', }, { id: 'cloudflare', name: 'Cloudflare Workers', category: 'devtools', role: 'Zero-Trust Isolation', metric: '300+ Cities', region: 'Global', }, { id: 'planetscale', name: 'PlanetScale DB', category: 'enterprise', role: 'Branching Schema', metric: 'Zero Downtime', region: 'EU-Central', }, { id: 'brex', name: 'Brex Treasury', category: 'fintech', role: 'Ledger Reconciliation', metric: 'Real-time API', region: 'US-East', }, ] type CategoryFilter = 'all' | 'devtools' | 'fintech' | 'enterprise' export interface LogoTickerInfiniteProps { className?: string } export function LogoTickerInfinite({ className }: LogoTickerInfiniteProps) { const [selectedCategory, setSelectedCategory] = React.useState<CategoryFilter>('all') const [isPaused, setIsPaused] = React.useState(false) const [isFastSpeed, setIsFastSpeed] = React.useState(false) const [hoveredBrand, setHoveredBrand] = React.useState<BrandPartner | null>(null) const filteredPartners = React.useMemo(() => { if (selectedCategory === 'all') return partners return partners.filter((p) => p.category === selectedCategory) }, [selectedCategory]) return ( <section data-slot="logo-ticker-infinite" className={cn( 'border-border/80 bg-muted/20 relative overflow-hidden border-y px-4 py-14 sm:px-6 sm:py-20 lg:px-8', className, )} > <div className="mx-auto max-w-7xl space-y-8 text-center"> {/* Section Header & Category Filter Controls */} <div className="border-border/60 flex flex-col items-center justify-between gap-4 border-b pb-6 text-left md:flex-row"> <div className="space-y-1"> <div className="flex items-center gap-2"> <span className="bg-success size-2 rounded-full" /> <p className="text-foreground font-mono text-xs font-semibold tracking-wider uppercase"> Production Verified Infrastructure </p> </div> <p className="text-muted-foreground text-xs"> Trusted by modern engineering teams shipping high-throughput production workloads. </p> </div> {/* Filter & Control Buttons */} <div className="flex flex-wrap items-center gap-2"> {/* Category Filter Pills */} <div className="bg-background border-border flex items-center gap-1 rounded-lg border p-0.5"> {(['all', 'devtools', 'fintech', 'enterprise'] as CategoryFilter[]).map((cat) => ( <button key={cat} type="button" className={cn( 'rounded-md px-2.5 py-1 font-mono text-xs capitalize transition-colors', selectedCategory === cat ? 'bg-primary text-primary-foreground font-semibold shadow-xs' : 'text-muted-foreground hover:text-foreground', )} onClick={() => setSelectedCategory(cat)} > {cat} </button> ))} </div> {/* Marquee Speed / Pause Controls */} <div className="flex items-center gap-1"> <button type="button" className="border-border bg-background text-muted-foreground hover:text-foreground rounded-lg border p-1.5 transition-colors" title={isPaused ? 'Resume Ticker' : 'Pause Ticker'} onClick={() => setIsPaused(!isPaused)} > {isPaused ? <Play className="size-3.5 fill-current" /> : <Pause className="size-3.5" />} </button> <button type="button" className="border-border bg-background text-muted-foreground hover:text-foreground rounded-lg border px-2 py-1 font-mono text-xs transition-colors" onClick={() => setIsFastSpeed(!isFastSpeed)} > {isFastSpeed ? '2x' : '1x'} </button> </div> </div> </div> {/* Infinite Marquee Track Viewport with Gradient Fade Edges */} <div className="group relative overflow-hidden"> {/* Left & Right Gradient Shadows */} <div className="from-background via-background/80 pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-gradient-to-r to-transparent sm:w-28" /> <div className="from-background via-background/80 pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-gradient-to-l to-transparent sm:w-28" /> {/* Double Track Container for Seamless Infinite Scrolling */} <div className={cn( 'flex w-max items-center gap-4 py-3', !isPaused && 'animate-marquee', isFastSpeed ? '[animation-duration:15s]' : '[animation-duration:32s]', )} style={{ animationPlayState: isPaused ? 'paused' : 'running', }} > {/* Repeat partners array twice for infinite seamless loop */} {[...filteredPartners, ...filteredPartners].map((item, idx) => ( <div key={`${item.id}-${idx}`} className="border-border bg-card/80 hover:bg-card hover:border-primary/50 relative flex shrink-0 cursor-pointer items-center gap-3 rounded-xl border px-4 py-2.5 shadow-xs backdrop-blur-md transition-colors duration-200" onMouseEnter={() => setHoveredBrand(item)} onMouseLeave={() => setHoveredBrand(null)} > <div className="bg-primary/10 text-primary flex size-7 items-center justify-center rounded-lg font-mono text-xs font-bold"> {item.name.charAt(0)} </div> <div className="text-left"> <p className="text-foreground font-mono text-xs font-semibold tracking-tight">{item.name}</p> <p className="text-muted-foreground text-xs">{item.role}</p> </div> <Badge variant="outline" className="border-success/20 bg-success/10 text-success ml-1 font-mono text-xs" > {item.metric} </Badge> </div> ))} </div> </div> {/* Live Hover Telemetry Card */} {hoveredBrand && ( <div className="border-border bg-card animate-in fade-in-0 mx-auto max-w-md rounded-xl border p-3 text-left shadow-lg duration-150"> <div className="flex items-center justify-between font-mono text-xs"> <span className="text-foreground font-bold">{hoveredBrand.name} Architecture Integration</span> <span className="text-success">{hoveredBrand.region}</span> </div> <p className="text-muted-foreground mt-1 text-xs"> Connected through {hoveredBrand.role} • Monitored with {hoveredBrand.metric} verified telemetry. </p> </div> )} </div> <style>{` @keyframes marquee { 0% { transform: translateX(0%); } 100% { transform: translateX(-50%); } } .animate-marquee { animation: marquee 32s linear infinite; } `}</style> </section> ) } export default LogoTickerInfinite
Raw manifest:https://uipkge.dev/r/react/logo-ticker-infinite.json