Social Proof Video Wall
blockmarketingEngineering case study and video testimonial wall with playback simulation, category filtering, and metric highlights.
Installation
$pnpm dlx uipkge-ng@latest add social-proof-video-wall$npx uipkge-ng@latest add social-proof-video-wall$yarn dlx uipkge-ng@latest add social-proof-video-wall$bunx uipkge-ng@latest add social-proof-video-wallManifest:
https://uipkge.dev/r/angular/social-proof-video-wall.jsonInstalls to:src/app/components/blocks/social-proof-video-wall/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
VideoTestimonialinterface VideoTestimonial {
id: string
name: string
role: string
company: string
category: 'fintech' | 'saas' | 'ai'
quote: string
duration: string
metricHighlight: string
metricLabel: string
}npm dependencies
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">“{{ item.quote }}”</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 }} • {{ 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 }} • 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 }} • {{ av.role }}</div> </div> <div class="text-muted-foreground text-xs">“{{ av.quote }}”</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
export { UiSocialProofVideoWallComponent, type VideoTestimonial, type VideoWallCategory, } from './social-proof-video-wall.component'
Raw manifest:https://uipkge.dev/r/angular/social-proof-video-wall.json