---
title: Testimonial
description: A cited customer quote with an attribution — and a grid to line several of them up.
category: Marketing
related:
  - /components/avatar
  - /components/rating
  - /components/pricing-table
  - /components/card
visibility: guest
draft: false
---

# Testimonial

A testimonial is a **quotation with an attribution**, and that is exactly what
`<x-wirekit::testimonial>` renders: a `<figure>` wrapping a `<blockquote>` and a
`<figcaption>`. A screen reader announces it as a quote and reads the attribution
as belonging to it — which a stack of styled `<div>`s never does, however much it
looks the part.

:::preview{title="Three testimonials with ratings"}
<x-wirekit::testimonial-grid label="What developers say">
    <li>
        <x-wirekit::testimonial
            author="Ada Lovelace"
            role="Principal Engineer, Analytical"
            :rating="5"
        >
            We replaced four hand-built components with one and the accessibility
            review came back clean on the first pass.
        </x-wirekit::testimonial>
    </li>
    <li>
        <x-wirekit::testimonial
            author="Grace Hopper"
            role="CTO, Compiler Works"
            :rating="5"
        >
            The parts I usually have to fight — focus order, keyboard traps, dark
            mode — were simply already right.
        </x-wirekit::testimonial>
    </li>
    <li>
        <x-wirekit::testimonial
            author="Alan Turing"
            role="Head of Platform, Enigma"
            :rating="4"
        >
            Every component takes tokens, so our whole brand landed by editing one
            file. No forks, no overrides.
        </x-wirekit::testimonial>
    </li>
</x-wirekit::testimonial-grid>
:::

## Attribution

`author` is what makes a testimonial a testimonial. `role` adds the context that
makes it persuasive.

Without an `avatar`, the component **derives the author's initials** and renders
them in a deterministic color rather than showing an empty gray disc — the same
person always gets the same color. Pass `initials` yourself for a mononym, a
brand account, or any name where first-and-last-letter is the wrong reading.

```blade
{{-- Photo --}}
<x-wirekit::testimonial author="Ada Lovelace" role="Principal Engineer" avatar="/img/ada.jpg">
    …
</x-wirekit::testimonial>

{{-- No photo — initials are derived ("AL") --}}
<x-wirekit::testimonial author="Ada Lovelace" role="Principal Engineer">
    …
</x-wirekit::testimonial>

{{-- Override the derived initials --}}
<x-wirekit::testimonial author="Björk" initials="B">
    …
</x-wirekit::testimonial>
```

## Rating

`rating` renders a read-only star rating above the quote. It is a record of what
someone gave, never an input — the stars are not focusable and cannot be changed.

```blade
<x-wirekit::testimonial author="Grace Hopper" :rating="5">
    …
</x-wirekit::testimonial>
```

## Company logo

`logo` places a company mark at the end of the attribution.

Leave `logo-alt` unset when the author is already named in text: the logo is then
decoration, and giving it a name only makes a screen reader read the company
twice. Pass `logo-alt` when the logo is the **only** attribution.

```blade
{{-- Author named in text -> the logo is decorative and stays silent --}}
<x-wirekit::testimonial author="Alan Turing" role="Head of Platform, Enigma" logo="/img/enigma.svg">
    …
</x-wirekit::testimonial>

{{-- Logo carries the attribution on its own -> name it --}}
<x-wirekit::testimonial author="" logo="/img/enigma.svg" logo-alt="Enigma">
    …
</x-wirekit::testimonial>
```

## The grid

`<x-wirekit::testimonial-grid>` is a **labeled list** — a real `<ul>` whose
children are `<li>` elements. That is not decoration: it means a screen reader
announces "list, 3 items" before the quotes, so someone who cannot see the row
knows how many there are to get through.

It flows one column on a phone, two on a tablet, three on a desktop, and stretches
every quote to the height of the tallest so the row keeps a clean baseline however
uneven the copy is.

```blade
<x-wirekit::testimonial-grid label="What developers say">
    <li>
        <x-wirekit::testimonial author="Ada Lovelace">…</x-wirekit::testimonial>
    </li>
    <li>
        <x-wirekit::testimonial author="Grace Hopper">…</x-wirekit::testimonial>
    </li>
</x-wirekit::testimonial-grid>
```

## Props

### `<x-wirekit::testimonial>`

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `author` | `string` | `''` | Who said it. |
| `role` | `string\|null` | `null` | Their role / company. |
| `avatar` | `string\|null` | `null` | Author photo URL. Omit to render derived initials. |
| `initials` | `string\|null` | `null` | Override the derived initials. |
| `logo` | `string\|null` | `null` | Company logo URL. |
| `logoAlt` | `string\|null` | `null` | Accessible name for the logo. Omit when the author is named in text. |
| `rating` | `int\|float\|null` | `null` | Read-only star rating (0–5). |
| `scope` | `string\|null` | `null` | Class-scope override. |

### `<x-wirekit::testimonial-grid>`

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string` | `'Testimonials'` | Accessible name for the list of quotes. |
| `scope` | `string\|null` | `null` | Class-scope override. |

## Accessibility

- The quote is a real `<blockquote>` inside a `<figure>`, attributed by a
  `<figcaption>` — announced as a quotation with its source, not as loose text.
- The grid is a labeled `<ul role="list">` of `<li>` items, so the number of
  quotes is announced up front.
- The star rating is read-only and not focusable — it is a record, not a control.
- A decorative company logo renders `alt=""` and is skipped, so the company is not
  announced twice when the author already names it.
- Initials are derived with multibyte-safe string handling, so a non-ASCII name is
  never sliced mid-codepoint.

## Keyboard Interaction

None. A testimonial is content, not a control — there is nothing here to operate.
The read-only star rating is deliberately not focusable, so tabbing through a wall
of quotes never traps a keyboard user in decorative stars. Any link you place
inside the quote keeps its own normal tab behavior.
