A product site's customer stories. Move down the list: a row opens once the pointer settles, so a quick sweep doesn't make the list ripple. Each company's tab warms to its own color and opens into a photo, and the whole row links to the story.
@keyframeui
registry/demos/stretch-index-demo.tsx
import { StretchIndex, StretchIndexItem } from "@/components/ui/stretch-index";
const stories = [
{
title: "A bank that now ships every Friday",
company: "Halcyon Bank",
text: "Thirty engineers moved from quarterly release trains to weekly ones, without a rollback in the first year.",
result: "52 releases in 2025",
photo: "1460925895917-afdab827c52f",
accent: "#5b8def",
},
{
title: "One booking system for forty restaurants",
company: "Common Table",
text: "Hosts used to juggle three tools a night. Now every table, waitlist and deposit runs through one screen.",
result: "38% fewer no-shows",
photo: "1517248135467-4c7edcad34c4",
accent: "#e08a4f",
},
{
title: "Checkout, rebuilt for the phone first",
company: "Fern & Pilot",
text: "Two thirds of orders come from phones. A one-page checkout took paying from three minutes to forty seconds.",
result: "21% more completed orders",
photo: "1586953208448-b95a79798f07",
accent: "#3fae7a",
},
{
title: "Eleven designers, one library",
company: "Northstar",
text: "Six drifting UI kits became one shared library that engineers pull from directly, so mockups and code finally match.",
result: "6 kits down to 1",
photo: "1558655146-d09347e92766",
accent: "#a77df0",
},
{
title: "Code review in half the time",
company: "Relay",
text: "A team across nine time zones switched to smaller pull requests and recorded walkthroughs, and stopped waiting a day for review.",
result: "Reviews 2× faster",
photo: "1519389950473-47ba0277781c",
accent: "#e5b93f",
},
];
const photo = (id: string) =>
`https://images.unsplash.com/photo-${id}?auto=format&fit=crop&w=480&h=360&q=80`;
const slug = (company: string) => company.toLowerCase().replace(/[^a-z0-9]+/g, "-");
export function StretchIndexDemo() {
return (
<section className="w-full max-w-3xl text-zinc-900 dark:text-zinc-50">
<div className="mb-6 flex items-end justify-between gap-4">
<div>
<p className="font-mono text-xs tracking-widest text-zinc-500 uppercase">
Customer stories
</p>
<h3 className="mt-2 text-2xl font-semibold tracking-tight">
What changed after they switched
</h3>
</div>
<p className="hidden font-mono text-xs text-zinc-500 tabular-nums sm:block">
{stories.length} stories
</p>
</div>
<StretchIndex defaultIndex={0} className="border-t border-current/10">
{stories.map((story) => (
<StretchIndexItem
key={story.company}
title={story.title}
meta={story.company}
href={`#${slug(story.company)}`}
image={photo(story.photo)}
accent={story.accent}
>
<p className="max-w-md text-pretty text-zinc-600 dark:text-zinc-400">{story.text}</p>
<div className="mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-sm">
<span className="font-medium tabular-nums">{story.result}</span>
<a
href={`#${slug(story.company)}`}
className="rounded-sm font-medium text-zinc-500 underline-offset-4 outline-none hover:text-current hover:underline focus-visible:ring-2 focus-visible:ring-current"
>
Read the story <span aria-hidden>→</span>
</a>
</div>
</StretchIndexItem>
))}
</StretchIndex>
</section>
);
}
Live preview, examples and props: https://keyframeui.com/components/stretch-index#stretch-index-demo






















