Blocks

Testimonial Reel Focus

A reel of testimonial cards run sideways by vertical scroll, with a tick-strip scrubber and hover focus.

A testimonial section for landing pages. The cards sit in a row wider than the window, shown through a sticky stage, and the page's vertical scroll is spent running that row sideways — the runway is one viewport plus exactly the distance the row has to travel, so the reel starts the moment the stage lands and finishes on the last card. Underneath, a strip of 2px ticks reads the run back like a scrubber: a seven-bar window rides the head and tapers either side. Every card waits below its line until it crosses in from the right, and the whole row flexes while it travels — cards ahead of centre swing low, the ones behind ride high, by however fast you are scrolling — so eight cards read as one strip of film being drawn past. It falls flat the moment you stop. Hover and the rest of the reel drops back and blurs while the one you are reading stays sharp and its edge warms to the accent. GSAP.

New tab
  • Maya RahmanCEO, Northwind01/08
    “They did not just redesign our brand — they gave the whole company a spine. Our sales calls got shorter.”
    Brand identitySales cycle −30%
  • Daniel OseiVP Engineering, Halo Labs02/08
    “The design system paid for itself in a quarter. Engineers stopped asking what things should look like and started shipping.”
    Design systemShips 2× faster
  • Priya VenkataramanCMO, Kindred03/08
    “Three directions, all of them right. I have never had a harder time choosing, or an easier time defending it to the board.”
    Campaign+41% sign-ups
  • Tomás ReyesFounder, Lumen Health04/08
    “They made a 2am telehealth flow feel like a conversation, not a form. Completion has not stopped climbing since launch.”
    Product · App+27% completion
  • Hana KobayashiHead of Brand, Atlas & Co.05/08
    “A small senior team that actually listens. Every review ended with fewer open questions than it started with.”
    Brand identityOne round of edits
  • Marcus HaleCOO, Verge06/08
    “The strategy work changed how our leadership talks about the company. The identity simply made it visible.”
    StrategyRepositioned in 8 weeks
  • Elena PetrovaDesign Director, Monolith07/08
    “Rare to find a studio that cares about the system as much as the hero shot. We still build on their foundations.”
    Design system3 years on one base
  • Kenji WatanabeCTO, Parallel08/08
    “Clear process, honest timelines, and work we were proud to put in front of investors. We would hire them again tomorrow.”
    Web · MotionRaised on the first deck

01 / 08

Scroll

Installation

pnpm dlx shadcn@latest add @tween-ui/testimonial-reel-focus

Usage

app/page.tsx
app/page.tsx
import TestimonialReelFocus from '@/components/tweenui/testimonial-reel-focus';
 
export default function Page() {
  return <TestimonialReelFocus />;
}

Pass your own voices. Add or remove one and the reel renumbers itself, re-measures its runway and rebuilds the scrubber — there is nothing to keep in sync by hand:

app/page.tsx
app/page.tsx
import TestimonialReelFocus from '@/components/tweenui/testimonial-reel-focus';
 
export default function Page() {
  return (
    <TestimonialReelFocus
      aria-label="What residents say"
      items={[
        {
          name: 'Maya Rahman',
          role: 'Resident, Casa 07',
          quote: 'The gate closes and the noise stops. Six months in, I still notice it.',
          tag: 'Phase I · Villa',
          metric: 'Handover on time',
        },
        {
          name: 'Daniel Osei',
          role: 'Partner, Osei & Reyes',
          initials: 'DO',
          hue: 214,
          quote: 'Twenty-five homes that had to look unbothered in twenty years.',
          tag: 'Architecture',
          metric: '25 of 25 placed',
        },
      ]}
    />
  );
}

There are no image requests: each avatar is a monogram over a gradient built from its hue, with a grain overlay on top. Leave initials and hue off and they are derived from the name and the card's position. Quotes are plain text — the curly quotes belong to the component.

Card size and reel geometry are CSS variables in the className strings (--reel-card-w, --reel-card-h, --reel-gap, --reel-edge), and the accent is --reel-accent (#045f64, #c6f56f in dark mode), so you can restyle the whole reel in place. The row lines its inset up with the 1290px container the rest of the library uses, at every width.

Props

PropTypeDefaultDescription
itemsTestimonialReelItem[]8-voice sampleThe voices, in reel order.
hintstring'Hover to focus'Note at the end of the scrubber row.
aria-labelstring'Testimonials'Accessible name for the section.

Each item is { name, role, quote, tag, metric, initials?, hue? }. Extends native <section> attributes (className, style, …).

The block is the reel and nothing else — no heading, no copy, no figures — so it sits under whatever section heading the page already has.

Behavior

  • The runway is measured, not guessed — the section is one viewport tall plus the row's overflow, so the reel never stops short of the last card and never idles at the end.
  • The counter and the scrubber read the same fraction, so the number beside the strip always agrees with the lit bar.
  • The wave is scroll-linked, not timed — a card rises as it enters and lowers again if you scroll back, so it reads the same whichever way you go.
  • The flex follows speed, not position — it leans with the scroller, tips the other way when you reverse, caps itself on a flung scroll, and releases on a tween a beat after you stop.
  • It pins against whatever actually scrolls — the window on a normal page, or the nearest scrolling ancestor when the reel is dropped into a panel, a drawer or the preview above. That is the same element position: sticky picks, so the two can never disagree.
  • Resizing re-measures the runway and refits the strip to its new width.
  • A row that already fits — a very wide window, or two or three cards — skips the pinning entirely and stays a plain horizontal strip.
  • Hover focus is pointer-only: on touch every card stays at full strength.

Accessibility

The reel is a section labelled by its own heading, and the cards are a real list — each one keeps its place in the reading order whether or not it is on screen. The scrubber is a progressbar with aria-valuenow and an aria-valuetext of "3 of 8"; its bars, the monograms, the accent dots and the quote glyph are all aria-hidden. With no heading of its own the section carries an aria-label — override it to say what these voices are about. Under prefers-reduced-motion: reduce nothing is pinned and nothing is tweened — the row becomes a natively scrollable strip and the scrubber reads that scroller instead.