Menu

Change language

GitHub repo

Social Proof Video Wall

blockmarketing

Engineering case study and video testimonial wall with playback simulation, category filtering, and metric highlights.

Installation

$npx uipkge-ng@latest add social-proof-video-wall
Manifest:https://uipkge.dev/r/angular/social-proof-video-wall.jsonInstalls to:src/app/components/blocks/social-proof-video-wall/

Variants

Loading interactive previews…

Schema

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

VideoTestimonial
interface VideoTestimonial {
  id: string
  name: string
  role: string
  company: string
  category: 'fintech' | 'saas' | 'ai'
  quote: string
  duration: string
  metricHighlight: string
  metricLabel: string
}

npm dependencies

Includes

Files installed (2)

  • src/app/components/blocks/social-proof-video-wall/social-proof-video-wall.component.ts10.6 kB
    // Engineering case study and video testimonial wall with playback simulation,
    // category filtering, and metric highlights. Port of the Vue/React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'
    import { ArrowRight, LucideAngularModule, Play, Video, X } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'
    
    export interface VideoTestimonial {
      id: string
      name: string
      role: string
      company: string
      category: 'fintech' | 'saas' | 'ai'
      quote: string
      duration: string
      metricHighlight: string
      metricLabel: string
    }
    
    export type VideoWallCategory = 'all' | 'fintech' | 'saas' | 'ai'
    
    const defaultTestimonials: VideoTestimonial[] = [
      {
        id: 'story-1',
        name: 'Elena Rostova',
        role: 'Staff Frontend Architect',
        company: 'FinFlow Global',
        category: 'fintech',
        quote:
          'Unbundled components gave our core engineering team complete ownership over PCI-compliant ledger tables without maintaining a bloated bespoke fork.',
        duration: '2:14',
        metricHighlight: '4.2x Faster',
        metricLabel: 'Sprint delivery velocity',
      },
      {
        id: 'story-2',
        name: 'Marcus Vance',
        role: 'VP of Engineering',
        company: 'Synthetix AI',
        category: 'ai',
        quote:
          'Our AI canvas needed sub-millisecond AST updates. Copying pure Vue and React primitives eliminated hours of npm dependency version conflicts.',
        duration: '1:48',
        metricHighlight: '-380 KB',
        metricLabel: 'Zero runtime bundle overhead',
      },
      {
        id: 'story-3',
        name: 'Sarah Chen',
        role: 'Head of Product Design',
        company: 'Linearis Cloud',
        category: 'saas',
        quote:
          'The Tailwind v4 token system and OKLCH color palettes match our design system tokens 1:1. Zero translation friction between Figma and code.',
        duration: '3:05',
        metricHighlight: '100% Token Sync',
        metricLabel: 'Figma to code parity',
      },
      {
        id: 'story-4',
        name: 'Devon Wright',
        role: 'Principal Engineer',
        company: 'OmniStream Data',
        category: 'saas',
        quote:
          'We replaced 14 disparate npm UI libraries with a single clean registry pull. Full keyboard ergonomics and ARIA compliance right out of the box.',
        duration: '2:30',
        metricHighlight: '14 → 1',
        metricLabel: 'Vendor dependency consolidation',
      },
    ]
    
    const categories: { id: VideoWallCategory; label: string }[] = [
      { id: 'all', label: 'All Customer Stories' },
      { id: 'saas', label: 'Enterprise SaaS' },
      { id: 'fintech', label: 'Fintech & Security' },
      { id: 'ai', label: 'AI & Data Platforms' },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-social-proof-video-wall, [ui-social-proof-video-wall]',
      standalone: true,
      host: { '[attr.class]': '"contents"' },
      imports: [LucideAngularModule, UiBadgeComponent, UiCardComponent, UiCardContentComponent],
      template: `
        <section data-slot="social-proof-video-wall" [class]="rootClass">
          <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
            <div class="mx-auto max-w-3xl space-y-4 text-center">
              <a
                href="#customer-stories"
                class="group border-border/80 bg-secondary/60 hover:bg-secondary text-foreground inline-flex items-center gap-2 rounded-full border px-3.5 py-1 text-xs font-medium shadow-2xs transition-colors"
              >
                <lucide-icon [img]="Video" class="text-primary size-3.5" />
                <span>Engineering Case Studies</span>
                <lucide-icon
                  [img]="ArrowRight"
                  class="text-muted-foreground size-3 transition-transform group-hover:translate-x-0.5"
                />
              </a>
    
              <h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl">{{ title }}</h2>
              <p class="text-muted-foreground text-base sm:text-lg">{{ description }}</p>
    
              <div class="flex flex-wrap items-center justify-center gap-2 pt-2">
                @for (cat of filterCategories; track cat.id) {
                  <button type="button" [class]="filterButtonClass(cat.id)" (click)="selectedCategory.set(cat.id)">
                    {{ cat.label }}
                  </button>
                }
              </div>
            </div>
    
            <div class="mt-12 grid grid-cols-1 gap-6 md:grid-cols-2">
              @for (item of filteredTestimonials(); track item.id) {
                <div
                  ui-card
                  class="group border-border bg-card hover:border-primary/40 relative flex flex-col justify-between overflow-hidden shadow-xs transition-colors hover:shadow-sm"
                >
                  <div ui-card-content class="space-y-5 p-6">
                    <div
                      class="border-border bg-muted/60 focus-visible:ring-ring group/video relative flex aspect-16/9 w-full cursor-pointer items-center justify-center overflow-hidden rounded-lg border focus-visible:ring-2 focus-visible:outline-none"
                      (click)="openVideoModal(item)"
                      (keydown.enter)="openVideoModal(item)"
                      tabindex="0"
                      role="button"
                      [attr.aria-label]="'Play testimonial from ' + item.name"
                    >
                      <div
                        class="bg-background/90 text-primary flex size-12 items-center justify-center rounded-full shadow-md transition-transform group-hover/video:scale-110"
                      >
                        <lucide-icon [img]="Play" class="fill-primary size-5 pl-0.5" />
                      </div>
                      <div
                        class="bg-background/80 text-foreground absolute right-2.5 bottom-2.5 rounded px-2 py-0.5 font-mono text-xs font-medium backdrop-blur-xs"
                      >
                        {{ item.duration }}
                      </div>
                      <div class="absolute top-2.5 left-2.5">
                        <span
                          ui-badge
                          variant="outline"
                          class="border-border/80 bg-background/80 font-mono text-xs tracking-wider uppercase backdrop-blur-xs"
                        >
                          {{ item.company }}
                        </span>
                      </div>
                    </div>
    
                    <div class="space-y-3">
                      <p class="text-muted-foreground text-xs leading-relaxed italic">&ldquo;{{ item.quote }}&rdquo;</p>
                      <div class="border-border/80 flex items-center justify-between border-t pt-3">
                        <div>
                          <div class="text-foreground text-xs font-bold">{{ item.name }}</div>
                          <div class="text-muted-foreground text-xs">{{ item.role }} &bull; {{ item.company }}</div>
                        </div>
                        <div class="text-right">
                          <div class="text-success font-mono text-sm font-bold">{{ item.metricHighlight }}</div>
                          <div class="text-muted-foreground text-xs">{{ item.metricLabel }}</div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              }
            </div>
    
            @if (activeVideo(); as av) {
              <div
                class="bg-background/80 fixed inset-0 z-50 flex items-center justify-center p-4 backdrop-blur-sm"
                (click)="closeVideoModal()"
              >
                <div
                  class="border-border bg-card relative w-full max-w-2xl space-y-4 rounded-xl border p-6 shadow-sm"
                  (click)="$event.stopPropagation()"
                >
                  <div class="border-border flex items-center justify-between border-b pb-3">
                    <div class="flex items-center gap-2">
                      <lucide-icon [img]="Video" class="text-primary size-4" />
                      <span class="text-foreground text-sm font-bold">{{ av.company }} &bull; Architecture Deep-Dive</span>
                    </div>
                    <button
                      type="button"
                      class="text-muted-foreground hover:text-foreground rounded-md p-1"
                      aria-label="Close video"
                      (click)="closeVideoModal()"
                    >
                      <lucide-icon [img]="X" class="size-4" />
                    </button>
                  </div>
                  <div
                    class="border-border bg-muted/40 flex aspect-16/9 w-full flex-col items-center justify-center space-y-2 rounded-lg border p-6 text-center"
                  >
                    <div
                      class="bg-primary text-primary-foreground flex size-14 animate-pulse items-center justify-center rounded-full shadow-lg"
                    >
                      <lucide-icon [img]="Play" class="fill-primary-foreground size-6 pl-0.5" />
                    </div>
                    <div class="text-foreground text-xs font-bold">Streaming Case Study (HD 1080p)</div>
                    <div class="text-muted-foreground text-xs">{{ av.name }} &bull; {{ av.role }}</div>
                  </div>
                  <div class="text-muted-foreground text-xs">&ldquo;{{ av.quote }}&rdquo;</div>
                </div>
              </div>
            }
          </div>
        </section>
      `,
    })
    export class UiSocialProofVideoWallComponent {
      protected readonly ArrowRight = ArrowRight
      protected readonly Play = Play
      protected readonly Video = Video
      protected readonly X = X
    
      @Input() title = 'Trusted by world-class design engineers and platform architects.'
      @Input()
      description =
        'Discover how high-velocity engineering organizations leverage unbundled UI primitives to build ultra-fast, accessible products.'
      @Input() testimonials?: VideoTestimonial[]
      @Input('class') className?: string
    
      readonly selectedCategory = signal<VideoWallCategory>('all')
      readonly activeVideo = signal<VideoTestimonial | null>(null)
      readonly filterCategories = categories
    
      readonly activeTestimonials = computed(() => this.testimonials ?? defaultTestimonials)
      readonly filteredTestimonials = computed(() => {
        const cat = this.selectedCategory()
        const all = this.activeTestimonials()
        if (cat === 'all') return all
        return all.filter((t) => t.category === cat)
      })
    
      get rootClass(): string {
        return cn('bg-background relative overflow-hidden py-16 sm:py-24', this.className)
      }
    
      filterButtonClass(id: VideoWallCategory): string {
        return cn(
          'rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors',
          this.selectedCategory() === id
            ? 'border-primary bg-primary/10 text-primary font-semibold'
            : 'border-border bg-card text-muted-foreground hover:text-foreground',
        )
      }
    
      openVideoModal(t: VideoTestimonial): void {
        this.activeVideo.set(t)
      }
    
      closeVideoModal(): void {
        this.activeVideo.set(null)
      }
    }
    
  • src/app/components/blocks/social-proof-video-wall/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/social-proof-video-wall.json