Testimonial — Social Cards
blockmarketingMasonry of social-post style endorsements with handle, avatar, timestamp, body copy, and engagement counts, formatted as posts rather than polished marketing quotes.
Installation
$pnpm dlx uipkge-ng@latest add testimonial-social-cards$npx uipkge-ng@latest add testimonial-social-cards$yarn dlx uipkge-ng@latest add testimonial-social-cards$bunx uipkge-ng@latest add testimonial-social-cardsManifest:
https://uipkge.dev/r/angular/testimonial-social-cards.jsonInstalls to:src/app/components/blocks/testimonial-social-cards/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.
SocialPostinterface SocialPost {
name: string
handle: string
initials: string
when: string
body: string
replies: number
reposts: number
likes: number
}npm dependencies
Files installed (2)
src/app/components/blocks/testimonial-social-cards/testimonial-social-cards.component.ts5.9 kB
// Masonry of social-post style endorsements with handle, avatar, timestamp, body copy, // and engagement counts — written as posts, not polished quotes. Port of the Vue/React block 1:1. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { Heart, LucideAngularModule, MessageCircle, Repeat2 } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component' export interface SocialPost { name: string handle: string initials: string when: string body: string replies: number reposts: number likes: number } export const socialPosts: SocialPost[] = [ { name: 'Erin Walsh', handle: '@erinwalsh', initials: 'EW', when: '4h', body: 'closed the month in four days. four. i have been doing this for eleven years and that number has never started with a four.', replies: 18, reposts: 42, likes: 310, }, { name: 'Marcus Ellery', handle: '@mellery', initials: 'ME', when: '1d', body: 'the thing that sold me was the diff. every metric change opens a PR. i can finally review what finance is about to publish instead of finding out in the board deck.', replies: 7, reposts: 91, likes: 604, }, { name: 'Anna Reyes', handle: '@annareyes', initials: 'AR', when: '2d', body: 'migration took longer than the marketing site says (three weeks, not one) but nothing broke and we did not copy a single row out of the warehouse.', replies: 24, reposts: 33, likes: 188, }, { name: 'Tom Fairbanks', handle: '@tfairbanks', initials: 'TF', when: '3d', body: 'row level scoping wired to okta groups. someone changes teams, their dashboards change with them. this used to be a quarterly cleanup ticket.', replies: 11, reposts: 57, likes: 402, }, { name: 'Priya Raman', handle: '@praman', initials: 'PR', when: '5d', body: 'forecast review used to open with twenty minutes of reconciling two decks. now it opens with the forecast. small change, enormous difference.', replies: 5, reposts: 28, likes: 233, }, { name: 'Sophie Lindqvist', handle: '@slindqvist', initials: 'SL', when: '1w', body: 'handed the auditors an exported change log instead of a shared folder and they were visibly confused about what to do with their afternoon.', replies: 31, reposts: 120, likes: 870, }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-testimonial-social-cards, [ui-testimonial-social-cards]', 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, UiAvatarComponent, UiAvatarFallbackComponent, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, ], template: ` <section data-slot="testimonial-social-cards" [class]="rootClass"> <div class="mx-auto max-w-6xl px-6 py-20 lg:py-28"> <div class="max-w-2xl"> <span ui-badge variant="secondary">In the wild</span> <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">Posted without being asked</h2> <p class="text-muted-foreground mt-3 text-lg"> Unedited, including the one about the migration taking three weeks. </p> </div> <!-- CSS columns give a masonry flow with no JS layout pass; cards use break-inside to avoid splitting across a column boundary. --> <div class="mt-10 gap-4 sm:columns-2 lg:columns-3"> @for (post of posts; track post.handle) { <div ui-card class="mb-4 break-inside-avoid"> <div ui-card-content class="p-5"> <div class="flex items-center gap-3"> <ui-avatar class="size-9"> <ui-avatar-fallback class="text-xs">{{ post.initials }}</ui-avatar-fallback> </ui-avatar> <div class="min-w-0"> <p class="truncate text-sm font-medium">{{ post.name }}</p> <p class="text-muted-foreground truncate text-xs">{{ post.handle }} · {{ post.when }}</p> </div> </div> <p class="mt-3 text-sm leading-relaxed">{{ post.body }}</p> <div class="text-muted-foreground mt-4 flex items-center gap-5 text-xs"> <button ui-button variant="ghost" size="sm" class="h-auto gap-1.5 px-1.5 py-1 text-xs font-normal"> <lucide-icon [img]="MessageCircle" class="size-3.5" aria-hidden="true" /> {{ post.replies }} </button> <button ui-button variant="ghost" size="sm" class="h-auto gap-1.5 px-1.5 py-1 text-xs font-normal"> <lucide-icon [img]="Repeat2" class="size-3.5" aria-hidden="true" /> {{ post.reposts }} </button> <button ui-button variant="ghost" size="sm" class="h-auto gap-1.5 px-1.5 py-1 text-xs font-normal"> <lucide-icon [img]="Heart" class="size-3.5" aria-hidden="true" /> {{ post.likes }} </button> </div> </div> </div> } </div> </div> </section> `, }) export class UiTestimonialSocialCardsComponent { protected readonly Heart = Heart protected readonly MessageCircle = MessageCircle protected readonly Repeat2 = Repeat2 protected readonly posts = socialPosts @Input('class') className?: string get rootClass(): string { return cn('bg-background', this.className) } }src/app/components/blocks/testimonial-social-cards/index.ts0.1 kB
export { UiTestimonialSocialCardsComponent, type SocialPost, socialPosts } from './testimonial-social-cards.component'
Raw manifest:https://uipkge.dev/r/angular/testimonial-social-cards.json