Page Sections

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.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

400
350
6000
Active edited usageDefault props
<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.json

Or, 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

PropTypeDefaultDescription
testimonialsAvatarTabQuotesItem[]—Testimonials, in the same order as the avatar list
eyebrowstring—Small label above the title
titlestring—Section title; also the accessible name of the section
subtitlestring—Supporting text under the title
valuenumber—Controlled selected index; managed internally when omitted
defaultValuenumber0Initial selected index in uncontrolled mode
onValueChange(index: number) => void—Called when the selected index changes
autoplaybooleanfalseAuto-advance; shows a pause button and a progress line under the selected row
intervalnumber6000Auto-advance interval in ms, minimum 1000
orientation"vertical" | "horizontal""vertical"Avatar list direction; vertical automatically becomes a horizontally scrolling row below md
pauseLabelstring"暫停自動播放" (Pause autoplay)Pause button label (while playing)
playLabelstring"繼續自動播放" (Resume autoplay)Pause button label (while paused)
listLabelstring"選擇評價" (Choose a testimonial)Accessible name of the avatar list (tablist); also names the section when there is no title
classNamestring—Merged into the root section

AvatarTabQuotesItem

FieldTypeDefaultDescription
quotestring—Testimonial body
namestring—Reviewer name; also used for the avatar initials (last two characters for CJK, first letters for Latin)
rolestring—Job title
companystring—Company or organisation, shown under the name in the list and in the byline
ratingnumber—Star rating (1–5); hidden when omitted
avatarHuenumberGolden-angle hue by indexHue (0–360) for the avatar gradient and the backdrop glow
highlightstring—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
avatarSrcstring—Avatar image URL; falls back to initials on a gradient circle

CSS variables

VariableDefaultDescription
--wb-avatar-tab-quotes-accent#0d9488 (dark #2dd4bf)Accent for the indicator bar, large-avatar ring, eyebrow, metric and underline
--wb-avatar-tab-quotes-stiffness400Spring stiffness of the selection marker and avatar scale (damping fixed at 34)
--wb-avatar-tab-quotes-duration350msDuration of each half of the blurred fade-out / fade-in on the right
--wb-avatar-tab-quotes-glow-duration600msDuration of the backdrop glow's hue transition

How it works

  • The right-hand content uses AnimatePresence with mode="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 changes color, 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 via aria-labelledby when there is one, otherwise by listLabel
  • The avatar list is role="tablist" with aria-orientation matching the actual direction (vertical column or the narrow-screen row); each avatar is a role="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 via aria-labelledby; when not auto-advancing, content changes are announced with aria-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