Menu

Change language

GitHub repo

Testimonial — Logo & Quote Row

blockmarketing

Three-up inline row pairing each customer wordmark with a one-sentence quote and a compact attribution line, for pages that need proof without a full testimonial block.

Installation

$npx uipkge-ng@latest add testimonial-logo-quote-row
Manifest:https://uipkge.dev/r/angular/testimonial-logo-quote-row.jsonInstalls to:src/app/components/blocks/testimonial-logo-quote-row/

Variants

Loading interactive previews…

Schema

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

LogoQuote
interface LogoQuote {
  wordmark: string
  quote: string
  author: string
  role: string
  initials: string
}

Files installed (2)

  • src/app/components/blocks/testimonial-logo-quote-row/testimonial-logo-quote-row.component.ts3.2 kB
    // Three-up inline row pairing each customer wordmark with a one-sentence quote
    // and a compact attribution line, for pages that need proof without a full
    // testimonial block. Port of the Vue/React block 1:1.
    import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
    import { cn } from '@/lib/utils'
    import { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'
    import { UiSeparatorComponent } from '@/ui/separator/separator.component'
    
    interface LogoQuote {
      wordmark: string
      quote: string
      author: string
      role: string
      initials: string
    }
    
    // One sentence each. The row is a proof band, not a testimonial section — if a
    // quote needs two sentences it belongs in a card layout instead.
    const quotes: LogoQuote[] = [
      {
        wordmark: 'Northwind',
        quote: 'Close starts from a reconciled position now, which it never did before.',
        author: 'Erin Walsh',
        role: 'VP Finance',
        initials: 'EW',
      },
      {
        wordmark: 'Halden',
        quote: 'Clinic leads answer their own questions without filing a ticket.',
        author: 'Daniel Brooks',
        role: 'Head of Analytics',
        initials: 'DB',
      },
      {
        wordmark: 'Verity',
        quote: 'Sales and finance forecast off one definition, so review is about the plan.',
        author: 'Priya Raman',
        role: 'RevOps Director',
        initials: 'PR',
      },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-testimonial-logo-quote-row, [ui-testimonial-logo-quote-row]',
      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: [UiAvatarComponent, UiAvatarFallbackComponent, UiSeparatorComponent],
      template: `
        <section data-slot="testimonial-logo-quote-row" [class]="rootClass">
          <div class="mx-auto max-w-6xl px-6 py-16">
            <div class="grid gap-8 md:grid-cols-3 md:gap-10">
              @for (entry of quotes; track entry.wordmark; let index = $index) {
                <figure class="relative">
                  <span class="text-sm font-semibold tracking-[0.18em] uppercase">{{ entry.wordmark }}</span>
                  <blockquote class="mt-4 text-sm leading-relaxed text-pretty">“{{ entry.quote }}”</blockquote>
                  <figcaption class="mt-5 flex items-center gap-3">
                    <ui-avatar class="size-8">
                      <ui-avatar-fallback class="text-xs">{{ entry.initials }}</ui-avatar-fallback>
                    </ui-avatar>
                    <div class="min-w-0">
                      <p class="truncate text-xs font-medium">{{ entry.author }}</p>
                      <p class="text-muted-foreground truncate text-xs">{{ entry.role }}</p>
                    </div>
                  </figcaption>
    
                  <!-- Vertical rules between columns only, so the row reads as one band. -->
                  @if (index < quotes.length - 1) {
                    <ui-separator orientation="vertical" class="absolute top-0 -right-5 hidden h-full md:block" />
                  }
                </figure>
              }
            </div>
          </div>
        </section>
      `,
    })
    export class UiTestimonialLogoQuoteRowComponent {
      protected readonly quotes = quotes
    
      @Input('class') className?: string
    
      get rootClass(): string {
        return cn('bg-background', this.className)
      }
    }
    
  • src/app/components/blocks/testimonial-logo-quote-row/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/testimonial-logo-quote-row.json