Menu

Change language

GitHub repo

Logo Ticker Infinite

blockmarketing

Infinite marquee brand partner ticker with sector category filters, speed and pause controls, and interactive telemetry hover cards.

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/logo-ticker-infinite.json
Named registry:npx shadcn-vue@latest add @uipkge/logo-ticker-infiniteInstalls to:app/components/blocks/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

BrandPartner
interface BrandPartner {
  id: string
  name: string
  category: 'devtools' | 'fintech' | 'enterprise'
  role: string
  metric: string
  region: string
}

npm dependencies

Includes

Files installed (1)

  • app/components/blocks/LogoTickerInfinite.vue7.6 kB
    <script setup lang="ts">
    import { computed, ref } from 'vue'
    import { Pause, Play } from 'lucide-vue-next'
    import { Badge } from '@/components/ui/badge'
    
    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'
    
    const selectedCategory = ref<CategoryFilter>('all')
    const isPaused = ref(false)
    const isFastSpeed = ref(false)
    const hoveredBrand = ref<BrandPartner | null>(null)
    
    const filteredPartners = computed(() => {
      if (selectedCategory.value === 'all') return partners
      return partners.filter((p) => p.category === selectedCategory.value)
    })
    </script>
    
    <template>
      <section
        data-slot="logo-ticker-infinite"
        class="border-border/80 bg-muted/20 relative overflow-hidden border-y px-4 py-14 sm:px-6 sm:py-20 lg:px-8"
      >
        <div class="mx-auto max-w-7xl space-y-8 text-center">
          <!-- Section Header & Category Filter Controls -->
          <div
            class="border-border/60 flex flex-col items-center justify-between gap-4 border-b pb-6 text-left md:flex-row"
          >
            <div class="space-y-1">
              <div class="flex items-center gap-2">
                <span class="bg-success size-2 rounded-full" />
                <p class="text-foreground font-mono text-xs font-semibold tracking-wider uppercase">
                  Production Verified Infrastructure
                </p>
              </div>
              <p class="text-muted-foreground text-xs">
                Trusted by modern engineering teams shipping high-throughput production workloads.
              </p>
            </div>
    
            <!-- Filter & Control Buttons -->
            <div class="flex flex-wrap items-center gap-2">
              <!-- Category Filter Pills -->
              <div class="bg-background border-border flex items-center gap-1 rounded-lg border p-0.5">
                <button
                  v-for="cat in ['all', 'devtools', 'fintech', 'enterprise'] as CategoryFilter[]"
                  :key="cat"
                  type="button"
                  class="rounded-md px-2.5 py-1 font-mono text-xs capitalize transition-colors"
                  :class="
                    selectedCategory === cat
                      ? 'bg-primary text-primary-foreground font-semibold shadow-xs'
                      : 'text-muted-foreground hover:text-foreground'
                  "
                  @click="selectedCategory = cat"
                >
                  {{ cat }}
                </button>
              </div>
    
              <!-- Marquee Speed / Pause Controls -->
              <div class="flex items-center gap-1">
                <button
                  type="button"
                  class="border-border bg-background text-muted-foreground hover:text-foreground rounded-lg border p-1.5 transition-colors"
                  :title="isPaused ? 'Resume Ticker' : 'Pause Ticker'"
                  @click="isPaused = !isPaused"
                >
                  <Play v-if="isPaused" class="size-3.5 fill-current" />
                  <Pause v-else class="size-3.5" />
                </button>
                <button
                  type="button"
                  class="border-border bg-background text-muted-foreground hover:text-foreground rounded-lg border px-2 py-1 font-mono text-xs transition-colors"
                  @click="isFastSpeed = !isFastSpeed"
                >
                  {{ isFastSpeed ? '2x' : '1x' }}
                </button>
              </div>
            </div>
          </div>
    
          <!-- Infinite Marquee Track Viewport with Gradient Fade Edges -->
          <div class="group relative overflow-hidden">
            <!-- Left & Right Gradient Shadows -->
            <div
              class="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
              class="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
              class="flex w-max items-center gap-4 py-3"
              :class="[
                isPaused ? '' : 'animate-marquee',
                isFastSpeed ? '[animation-duration:15s]' : '[animation-duration:32s]',
              ]"
            >
              <!-- Repeat partners array twice for infinite seamless loop -->
              <div
                v-for="(item, idx) in [...filteredPartners, ...filteredPartners]"
                :key="`${item.id}-${idx}`"
                class="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"
                @mouseenter="hoveredBrand = item"
                @mouseleave="hoveredBrand = null"
              >
                <div
                  class="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 class="text-left">
                  <p class="text-foreground font-mono text-xs font-semibold tracking-tight">{{ item.name }}</p>
                  <p class="text-muted-foreground text-xs">{{ item.role }}</p>
                </div>
    
                <Badge variant="outline" class="border-success/20 bg-success/10 text-success ml-1 font-mono text-xs">
                  {{ item.metric }}
                </Badge>
              </div>
            </div>
          </div>
    
          <!-- Live Hover Telemetry Card -->
          <div
            v-if="hoveredBrand"
            class="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 class="flex items-center justify-between font-mono text-xs">
              <span class="text-foreground font-bold">{{ hoveredBrand.name }} Architecture Integration</span>
              <span class="text-success">{{ hoveredBrand.region }}</span>
            </div>
            <p class="text-muted-foreground mt-1 text-xs">
              Connected through {{ hoveredBrand.role }} &bull; Monitored with {{ hoveredBrand.metric }} verified telemetry.
            </p>
          </div>
        </div>
      </section>
    </template>
    
    <style scoped>
    @keyframes marquee {
      0% {
        transform: translateX(0%);
      }
      100% {
        transform: translateX(-50%);
      }
    }
    .animate-marquee {
      animation: marquee 32s linear infinite;
    }
    .animate-marquee:hover {
      animation-play-state: paused;
    }
    </style>
    

Raw manifest:https://uipkge.dev/r/vue/logo-ticker-infinite.json