Testimonial Masonry Verified Grid
blockmarketingMasonry grid of verified engineer endorsements with role filters, star ratings, avatar badges, and quantifiable impact metrics.
Installation
$pnpm dlx uipkge-ng@latest add testimonial-masonry-verified-grid$npx uipkge-ng@latest add testimonial-masonry-verified-grid$yarn dlx uipkge-ng@latest add testimonial-masonry-verified-grid$bunx uipkge-ng@latest add testimonial-masonry-verified-gridManifest:
https://uipkge.dev/r/angular/testimonial-masonry-verified-grid.jsonInstalls to:src/app/components/blocks/testimonial-masonry-verified-grid/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.
VerifiedTestimonialinterface VerifiedTestimonial {
id: string
name: string
role: string
roleCategory: 'frontend' | 'founder' | 'design-eng'
company: string
avatar: string
handle: string
verifiedSource: 'Twitter' | 'GitHub' | 'LinkedIn'
quote: string
metricBadge?: string
starCount: number
}npm dependencies
Files installed (2)
src/app/components/blocks/testimonial-masonry-verified-grid/testimonial-masonry-verified-grid.component.ts9.8 kB
// Masonry grid of verified engineer endorsements with role filters, star ratings, // avatar badges, and quantifiable impact metrics. Port of the Vue/React block 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { CheckCircle2, LucideAngularModule, Star } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiCardComponent } from '@/ui/card/card.component' export type MasonryRoleFilter = 'all' | 'frontend' | 'founder' | 'design-eng' export interface VerifiedTestimonial { id: string name: string role: string roleCategory: 'frontend' | 'founder' | 'design-eng' company: string avatar: string handle: string verifiedSource: 'Twitter' | 'GitHub' | 'LinkedIn' quote: string metricBadge?: string starCount: number } const testimonials: VerifiedTestimonial[] = [ { id: '1', name: 'Alexandre Rivière', role: 'Principal Design Engineer', roleCategory: 'design-eng', company: 'Linear Ecosystem', avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=150&auto=format&fit=crop&q=80', handle: '@alex_riviere', verifiedSource: 'Twitter', quote: 'The unbundled component model completely cured our team from npm dependency fatigue. We get raw SFCs and TSX with exact Tailwind v4 token alignments. Zero wrapper bloat, 100% code ownership.', metricBadge: '4.2x Faster Ship Velocity', starCount: 5, }, { id: '2', name: 'Sarah Chen', role: 'VP of Engineering', roleCategory: 'founder', company: 'ScaleDev AI', avatar: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?w=150&auto=format&fit=crop&q=80', handle: '@schen_ai', verifiedSource: 'GitHub', quote: 'We had severe bundle bloat with our previous UI package (over 500kB of unused JS). Switching to UIPKGE reduced our initial bundle to 38kB and solved our INP scores overnight.', metricBadge: '-88% Bundle Size', starCount: 5, }, { id: '3', name: 'Marcus Vance', role: 'Staff Frontend Architect', roleCategory: 'frontend', company: 'HyperQubit Cloud', avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&auto=format&fit=crop&q=80', handle: '@marcus_vance', verifiedSource: 'Twitter', quote: 'Dual-framework parity is not a gimmick here—it is mathematically verified. We maintain a Nuxt 3 admin console and Next.js customer portal with identical design tokens and micro-interactions.', metricBadge: '100% Token Parity', starCount: 5, }, { id: '4', name: 'Elena Rostova', role: 'Head of Product Design', roleCategory: 'design-eng', company: 'Fintech Velocity', avatar: 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=150&auto=format&fit=crop&q=80', handle: '@elena_craft', verifiedSource: 'GitHub', quote: 'The spring physics and micro-interactions match Linear and Raycast levels of craft. No arbitrary sub-12px micro-text or sloppy contrast issues.', metricBadge: 'WCAG AA AA Certified', starCount: 5, }, { id: '5', name: 'David Kim', role: 'Founder & CTO', roleCategory: 'founder', company: 'PulseOps', avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=150&auto=format&fit=crop&q=80', handle: '@dkim_ops', verifiedSource: 'Twitter', quote: 'Being able to run `npx shadcn-vue add` and have clean, pristine components in our git repository is the single biggest DX breakthrough since Vite.', metricBadge: 'Zero Upstream Lock-in', starCount: 5, }, { id: '6', name: 'Liam O’Connor', role: 'Lead UI Engineer', roleCategory: 'frontend', company: 'Starlight Media', avatar: 'https://images.unsplash.com/photo-1522075469751-3a6694fb2f61?w=150&auto=format&fit=crop&q=80', handle: '@liam_dev', verifiedSource: 'GitHub', quote: 'The marketing blocks are actually functional workbenches with live sliders, real SVG telemetry sparklines, and zero dummy shapes. Huge time saver.', metricBadge: '450+ Verified Blocks', starCount: 5, }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-testimonial-masonry-verified-grid, [ui-testimonial-masonry-verified-grid]', standalone: true, // React renders the root <section> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiBadgeComponent, UiCardComponent], template: ` <section data-slot="testimonial-masonry-verified-grid" [class]="rootClass"> <div class="mx-auto max-w-7xl space-y-12"> <!-- Section Header --> <div class="mx-auto max-w-3xl space-y-4 text-center"> <span ui-badge variant="secondary" class="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs"> <lucide-icon [img]="Star" class="fill-warning text-warning size-3.5" /> Verified Engineer Endorsements </span> <h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl"> Loved by design engineers and CTOs worldwide. </h2> <p class="text-muted-foreground text-base"> Real feedback from engineers who dumped monolithic npm packages for unbundled code ownership. </p> <!-- Category Filters --> <div class="flex flex-wrap items-center justify-center gap-2 pt-2"> <button type="button" [class]="filterClass('all')" (click)="setFilter('all')"> All Perspectives ({{ totalCount }}) </button> <button type="button" [class]="filterClass('design-eng')" (click)="setFilter('design-eng')"> Design Engineers </button> <button type="button" [class]="filterClass('frontend')" (click)="setFilter('frontend')"> Frontend Architects </button> <button type="button" [class]="filterClass('founder')" (click)="setFilter('founder')"> CTOs & Founders </button> </div> </div> <!-- Masonry Grid (3 Columns) --> <div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2 lg:grid-cols-3"> @for (t of filteredTestimonials(); track t.id) { <div ui-card class="border-border bg-card/95 hover:border-primary/40 group relative flex flex-col justify-between space-y-4 overflow-hidden rounded-2xl p-6 text-left shadow-lg transition-colors hover:shadow-xl" > <!-- Top Row: Avatar, Name, Handle, Verified Badge --> <div class="flex items-start justify-between gap-3"> <div class="flex items-center gap-3"> <img [src]="t.avatar" [alt]="t.name" class="border-border size-10 shrink-0 rounded-full border object-cover" /> <div class="min-w-0"> <div class="flex items-center gap-1.5"> <h4 class="text-foreground truncate font-mono text-xs font-bold">{{ t.name }}</h4> <lucide-icon [img]="CheckCircle2" class="text-info size-3.5 shrink-0" /> </div> <p class="text-muted-foreground truncate text-xs">{{ t.role }} • {{ t.company }}</p> </div> </div> <span ui-badge variant="outline" class="text-muted-foreground shrink-0 font-mono text-xs"> {{ t.verifiedSource }} </span> </div> <!-- Quote text --> <p class="text-foreground/90 text-xs leading-relaxed italic sm:text-sm">“{{ t.quote }}”</p> <!-- Footer Row: Star Rating & Impact Metric Badge --> <div class="border-border/60 flex flex-wrap items-center justify-between gap-2 border-t pt-3"> <div class="flex items-center gap-0.5"> @for (s of stars(t.starCount); track s) { <lucide-icon [img]="Star" class="fill-warning text-warning size-3.5" /> } </div> @if (t.metricBadge) { <span ui-badge variant="outline" class="border-success/20 bg-success/10 text-success font-mono text-xs" > {{ t.metricBadge }} </span> } </div> </div> } </div> </div> </section> `, }) export class UiTestimonialMasonryVerifiedGridComponent { protected readonly CheckCircle2 = CheckCircle2 protected readonly Star = Star @Input() set initialFilter(v: MasonryRoleFilter) { this.activeFilter.set(v ?? 'all') } @Input('class') className?: string readonly activeFilter = signal<MasonryRoleFilter>('all') readonly filteredTestimonials = computed(() => { const f = this.activeFilter() if (f === 'all') return testimonials return testimonials.filter((t) => t.roleCategory === f) }) get totalCount(): number { return testimonials.length } get rootClass(): string { return cn('bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8', this.className) } setFilter(f: MasonryRoleFilter): void { this.activeFilter.set(f) } filterClass(f: MasonryRoleFilter): string { return cn( 'rounded-lg border px-3.5 py-1 font-mono text-xs transition-colors', this.activeFilter() === f ? 'border-primary bg-primary text-primary-foreground font-semibold shadow-xs' : 'border-border bg-card text-muted-foreground hover:text-foreground', ) } stars(count: number): number[] { return Array.from({ length: count }, (_, i) => i) } }src/app/components/blocks/testimonial-masonry-verified-grid/index.ts0.2 kB
export { UiTestimonialMasonryVerifiedGridComponent, type MasonryRoleFilter, type VerifiedTestimonial, } from './testimonial-masonry-verified-grid.component'
Raw manifest:https://uipkge.dev/r/angular/testimonial-masonry-verified-grid.json