Blocks
Testimonial Line Sweep
A paged testimonial pair whose text sweeps out line by line while the portrait irises shut.
A ready-made testimonial section. Two quotes sit side by side, and paging sweeps each line of text up behind its own mask while the portrait irises shut on a circular clip. The incoming quote arrives a beat later the same way, so the two sets never collide. Page with the buttons or the arrow keys. GSAP + SplitText.
What our clients say about us
Hear from the people who use our platform every day, and see how it's helping them achieve their goals.
We replaced four tools with one workflow, and the team shipped its first release two weeks early. Nobody has asked to go back.
Maya Collins
Head of Operations
Reporting used to take a full day each month. Now it runs itself and lands in our inbox. That time went straight back into the roadmap.
Ethan Brooks
President of Sales
Onboarding took an afternoon, not a quarter. Every handoff is visible to the whole team, so nothing slips between design and build.
Liam Harper
Product Lead
Support tickets dropped by a third in one month. Customers find answers before they ask us, and the team finally has room to think.
Ava Sinclair
Marketing Head
Installation
pnpm dlx shadcn@latest add @tween-ui/testimonial-line-sweepUsage
import TestimonialLineSweep from '@/components/tweenui/testimonial-line-sweep';
export default function Page() {
return <TestimonialLineSweep />;
}Pass your own quotes. Each one is split in three so the middle clause can carry the emphasis:
import TestimonialLineSweep from '@/components/tweenui/testimonial-line-sweep';
export default function Page() {
return (
<TestimonialLineSweep
title="What our clients say about us"
items={[
{
quoteStart: 'We replaced four tools with one workflow, and',
quoteHighlight: 'the team shipped its first release two weeks early.',
quoteEnd: 'Nobody has asked to go back.',
name: 'Maya Collins',
jobTitle: 'Head of Operations',
image: '/people/maya.jpg',
},
{
quoteStart: 'Reporting used to take a full day each month.',
quoteHighlight: 'Now it runs itself and lands in our inbox.',
quoteEnd: 'That time went back into the roadmap.',
name: 'Ethan Brooks',
jobTitle: 'President of Sales',
image: '/people/ethan.jpg',
},
]}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | sample heading | Section heading. |
description | string | sample line | Supporting line under the heading. |
items | TestimonialLineSweepItem[] | four quotes | Quotes to page through, two at a time. |
Extends native <section> attributes (className, style, …).
TestimonialLineSweepItem
| Field | Type | Description |
|---|---|---|
quoteStart · quoteHighlight · quoteEnd | string | The quote, split so the middle clause reads darker. |
name · jobTitle | string | Reviewer identity. |
image · imageAlt | string | Portrait; imageAlt falls back to name. |
Two quotes are shown per page from md up and one below it, so pass an even
number of items for full pages on desktop. Four is the default.
Accessibility
Off-page quotes are aria-hidden and unfocusable, so a screen reader only meets
the pair on screen. Both paging buttons are labelled, and the arrow keys page
only while the section is in view — they never hijack the page from elsewhere.
Under prefers-reduced-motion: reduce the lines are not split at all and pages
simply cross-fade.