A testimonials band is the evidence between the features and the price: a few short quotes, each
credited to the person who said it. It is the band where a page stops describing the product and
lets somebody else do it.

Reach for it when the quotes are specific and attributed, a named person, a role, a company. A
wall of anonymous praise is worse than no wall at all, which is why `name` is the one field the
type requires.

```tsx demo="marketing/testimonials-preview"
```

## Usage

```tsx
import { Testimonials } from "@asheeui/web";

<Testimonials
  items={[
    {
      quote: "We cut campaign setup from hours to minutes.",
      name: "Ama Boateng",
      role: "Head of growth, Kora Retail",
    },
  ]}
/>
```

Each item is a value: a `quote`, a `name`, an optional `role` and an optional picture. The heading
block, the spacing and the column options are the ones every marketing band takes, and `children`
renders below the quotes. An item may also state an `id`, used as its key when a builder can
reorder the wall.

## The quote and its attribution

`quote` is the only content in the card, and `name` is printed under it. `role` is optional, most
founders do not need one, and its line is drawn only when it is given, so an attribution without a
role is a complete one rather than a gap.

There is no anonymous variant: the band always draws the attribution, and a quote that arrives
without a name (a builder field left empty, a payload that skipped it) renders its words over an
empty line, beside an avatar with no initials and no label. A reader then hears a quotation with no
author, which is the one thing this band exists to prevent. If you have the words but not the
person, they do not belong here.

Length is what goes wrong next. A row of cards shares one height, so the longest quote sets it and
its neighbours keep the slack: a four-line quote makes a one-line quote beside it look like a
caption. Two sentences is a wall; the quote that is one sentence and one number is the one that gets
read.

```tsx demo="marketing/testimonials-attribution"
```

## Pictures and substitutions

Every quote can have a picture, and none of them has to. `avatarSrc` is the URL handed to the
framework's `Avatar`, which derives the initials of `name` and shows them when there is no picture
at all, or when the picture fails to load. A new `avatarSrc` is a new picture, so a wall that swaps
an item tries again rather than keeping the fallback the previous one earned.

`avatarComponent` and `avatarComponentProps` are the substitution seam. `avatarComponent` is the
framework's image component itself, the export from `next/image`, `expo-image` or whatever the
product already loads pictures with, and `avatarComponentProps` is what that component needs. The
band hands it a filled element: `fill` and `sizes` are supplied for it unless those props state
`width`, `height` or `fill`, and the props you pass win over the ones the framework supplies.

The substitution reaches the picture and nothing else. An item with no `avatarSrc` still draws its
initials whatever `avatarComponent` names, so one band mixes pictures and initials without a second
code path, which is what makes this seam worth using for a CDN with an allow-list, or for a
framework's optimised image, rather than a change at every call site.

```tsx demo="marketing/testimonials-avatars"
```

## Props

```props Testimonials
```

The nine options are registered under `components.testimonials` as `columns: 1`, `columnsMd: 2`,
`columnsLg: 3`, `gap: "lg"`, `spacing: "lg"`, `background: "none"`, `align: "center"`,
`containerSize: "lg"` and `contained: true`, deliberately the same values the feature grid
registers, so two adjacent bands line up without either of them stating anything. Each resolves
from the instance, then that section, then the registered value, and each of the three counts
resolves on its own: `columns={2}` alone still gives two at `md` and three at `lg`.

Two values resolve from outside this band. Each quote is a `bordered` `Card` at `md` density, so
its radius follows the application's `defaultRadius`, and the avatar's fallback circle takes its
colour from `defaultColor` and its radius from `defaultRadius`. A product that themes those two
values themes this band without touching it. `items`, `children`, `as` and the heading, its
content, its level (`titleAs`) and its size (`headingSize`), are instance-only, because the
quotes and the page's outline belong to the page rather than to the design.

## Accessibility

- `title` renders a level-two heading by default, and each quote is a `figure` with a `blockquote`
  inside it and a `figcaption` for the attribution, so a reader hears a quoted passage with its
  author attached rather than a paragraph that happens to sit in quotation marks. `titleAs` states
  another level, `h1` for the wall whose heading is the page's title, `h3` for one nested under a
  section heading, and `headingSize` changes the treatment without touching the level.
- The quotes are one list, so a reader hears how many there are before the first one and can move
  between them. The `<section>` the band draws is named by that heading through `aria-labelledby`,
  so it is a region a reader can jump to; a band with no `title` has nothing to be named by and
  stays unnamed, which is when `aria-label` is worth passing.
- The avatar is decorative: it is passed an empty `alt`, so assistive technology skips it, and the
  caption's name is what a reader hears once. The initials fallback is hidden for the same reason,
  the caption already carries the name.
- Nothing the band draws is focusable, quotes are words, so <kbd>Tab</kbd> passes over the wall
  and the page's only stops remain the ones its other bands supply.

The heading block, the data-not-markup decision and the one-`h1` rule are shared by every band,
see [Marketing sections](/concepts/marketing-sections).

## Related

```related card, avatar, feature-grid
```
