Avatar Tab Quotes
A testimonial section React component: a vertical column of avatars and names acts as tabs, the selection marker slides along the column, and the quote on the right switches with a blurred crossfade while the avatar scales up.
Gather several customer quotes into one section. On the left is a column of avatars and names — click one or use the arrow keys to switch. The selection marker (a 3px accent bar plus a soft tint) springs along the column, and the selected avatar in the list scales up to 1.1×. On the right you get the selected person's large avatar, the quote, the author and an optional result figure; on switch, the old content blurs out and the new one resolves out of a blur, while a soft 8% opacity glow behind it transitions to the selected person's hue. On narrow screens the list becomes a horizontally scrolling avatar row. No photos are needed: avatars default to initials on a golden-angle gradient circle, and you can pass avatarSrc to use a real image.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add avatar-tab-quotes.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<AvatarTabQuotes/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/avatar-tab-quotes.en.jsonOr, once registries are configured in components.json, install it as @webberui/avatar-tab-quotes.
Usage
import { AvatarTabQuotes } from "@/components/ui/avatar-tab-quotes";
<AvatarTabQuotes
eyebrow="What customers say"
title="Four teams, four launch speeds"
testimonials={[
{
quote: "The new member pages shipped in a week, and store staff now swap seasonal menus themselves.",
name: "Maya Lindqvist",
role: "Head of Operations",
company: "Islet Coffee",
highlight: "shipped in a week",
metric: { value: "7 days", label: "Design to launch" },
},
{
quote: "Our tracking page loads in half the time, and support calls dropped noticeably.",
name: "Daniel Okafor",
role: "Product Manager",
company: "Polaris Freight",
},
]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
testimonials | AvatarTabQuotesItem[] | — | Testimonials, in the same order as the avatar list |
eyebrow | string | — | Small label above the title |
title | string | — | Section title; also the accessible name of the section |
subtitle | string | — | Supporting text under the title |
value | number | — | Controlled selected index; managed internally when omitted |
defaultValue | number | 0 | Initial selected index in uncontrolled mode |
onValueChange | (index: number) => void | — | Called when the selected index changes |
autoplay | boolean | false | Auto-advance; shows a pause button and a progress line under the selected row |
interval | number | 6000 | Auto-advance interval in ms, minimum 1000 |
orientation | "vertical" | "horizontal" | "vertical" | Avatar list direction; vertical automatically becomes a horizontally scrolling row below md |
pauseLabel | string | "暫停自動播放" (Pause autoplay) | Pause button label (while playing) |
playLabel | string | "繼續自動播放" (Resume autoplay) | Pause button label (while paused) |
listLabel | string | "選擇評價" (Choose a testimonial) | Accessible name of the avatar list (tablist); also names the section when there is no title |
className | string | — | Merged into the root section |
AvatarTabQuotesItem
| Field | Type | Default | Description |
|---|---|---|---|
quote | string | — | Testimonial body |
name | string | — | Reviewer name; also used for the avatar initials (last two characters for CJK, first letters for Latin) |
role | string | — | Job title |
company | string | — | Company or organisation, shown under the name in the list and in the byline |
rating | number | — | Star rating (1–5); hidden when omitted |
avatarHue | number | Golden-angle hue by index | Hue (0–360) for the avatar gradient and the backdrop glow |
highlight | string | — | Fragment of quote to underline in the accent colour; must match the text exactly |
metric | { value: string; label: string } | — | Result figure shown under the quote |
avatarSrc | string | — | Avatar image URL; falls back to initials on a gradient circle |
CSS variables
| Variable | Default | Description |
|---|---|---|
--wb-avatar-tab-quotes-accent | #0d9488 (dark #2dd4bf) | Accent for the indicator bar, large-avatar ring, eyebrow, metric and underline |
--wb-avatar-tab-quotes-stiffness | 400 | Spring stiffness of the selection marker and avatar scale (damping fixed at 34) |
--wb-avatar-tab-quotes-duration | 350ms | Duration of each half of the blurred fade-out / fade-in on the right |
--wb-avatar-tab-quotes-glow-duration | 600ms | Duration of the backdrop glow's hue transition |
How it works
- The right-hand content uses
AnimatePresencewithmode="wait": the old content blurs (8px), moves up 8px and fades out, then the new content rises 8px out of a blur; both halves use--wb-avatar-tab-quotes-duration. - The backdrop glow is a radial gradient filled with
currentColor. Switching only changescolor, so the browser's native colour transition drives the hue change without per-frame repaints from JavaScript. - On narrow screens the avatar row scrolls horizontally; switching (including autoplay) only scrolls the list itself, never the page, and brings the selected avatar into view.
- Autoplay pauses on hover, while keyboard focus is inside the section, or when the tab is hidden; the progress line stops while paused and restarts on resume.
Accessibility
- The root is a
<section>, named by the title viaaria-labelledbywhen there is one, otherwise bylistLabel - The avatar list is
role="tablist"witharia-orientationmatching the actual direction (vertical column or the narrow-screen row); each avatar is arole="tab"button with a roving tabindex - ↑/↓ in the vertical column, ←/→ in the horizontal row, and Home/End jump to the first and last testimonial; focus follows the selection
- The right side is
role="tabpanel", labelled by the current tab viaaria-labelledby; when not auto-advancing, content changes are announced witharia-live="polite", and announcements are turned off during autoplay - Quotes render as
figure/blockquote/figcaption; avatars are decorative (aria-hidden) and tab names combine the name and company - When autoplay is on, a visible pause/play button is always shown (WCAG 2.2.2)
- When the user has reduce motion enabled, autoplay starts off (the play button can still turn it on), and the spring slide, avatar scale, blur and offset become a 150ms crossfade; testimonials can still be switched from the avatars
Orbiting Customer Avatars
Customer avatars circle slowly on one or two orbits; selecting or focusing an avatar pauses the orbit and crossfades that customer's quote into the centre.
Review Ticker
A React single-line, endlessly scrolling ticker of short testimonials separated by stars and fading at both edges, with an optional reverse second line, pausing on hover or focus and a visible pause button.