Shots spread through an article, in different grids. The viewer pages through them in reading order and scrolls each one into view so it has a tile to land in.
@keyframeui
registry/demos/zoom-lightbox-case-study-demo.tsx
import { ZoomLightbox, ZoomLightboxItem } from "@/components/ui/zoom-lightbox";
const shots = {
exterior: { id: "1600585154340-be6161a56a0c", width: 4500, height: 3000, alt: "A low timber and glass house lit up at dusk", caption: "The street side at dusk. The old garage is now the entrance." },
chair: { id: "1586023492125-27b2c045efd7", width: 3400, height: 3000, alt: "A yellow armchair beside a floor lamp against a white wall", caption: "Reading corner in the old hallway." },
sofa: { id: "1555041469-a586c61ea9bc", width: 5760, height: 3840, alt: "A green velvet sofa on a pale wooden floor", caption: "The snug, with the original floorboards kept." },
living: { id: "1618221195710-dd6b41faaea6", width: 6144, height: 3240, alt: "A long open living room with a sofa, coffee table and wide windows", caption: "One room from the front door to the garden." },
};
const image = (id: string, width: number) =>
`https://images.unsplash.com/photo-${id}?auto=format&fit=max&q=80&w=${width}`;
function Shot({
shot,
aspect,
className,
}: {
shot: (typeof shots)[keyof typeof shots];
aspect: string;
className?: string;
}) {
return (
<figure className={className}>
<ZoomLightboxItem
src={image(shot.id, 2400)}
thumbnail={image(shot.id, 1200)}
width={shot.width}
height={shot.height}
alt={shot.alt}
caption={shot.caption}
className={`w-full rounded-xl ${aspect}`}
/>
<figcaption className="mt-2 text-xs text-zinc-500">{shot.caption}</figcaption>
</figure>
);
}
// Items can sit anywhere inside <ZoomLightbox>, between paragraphs and in
// different grids. The viewer pages through all of them in reading order.
export function ZoomLightboxCaseStudyDemo() {
return (
<div className="w-full overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950">
<div className="h-[var(--demo-height,36rem)] overflow-y-auto overscroll-contain">
<ZoomLightbox className="mx-auto max-w-2xl px-6 py-10 sm:px-10">
<p className="font-mono text-xs uppercase tracking-widest text-zinc-500">
Case study · Residential
</p>
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance text-zinc-900 dark:text-zinc-50">
The Hollow House
</h2>
<p className="mt-3 text-sm leading-relaxed text-pretty text-zinc-600 dark:text-zinc-400">
A 1960s bungalow opened up into one long room that follows the light from the street to
the garden.
</p>
<dl className="mt-6 grid grid-cols-3 gap-4 border-y border-zinc-200 py-4 text-xs dark:border-zinc-800">
{[
["Client", "Private"],
["Year", "2026"],
["Scope", "Interiors"],
].map(([term, value]) => (
<div key={term}>
<dt className="font-mono uppercase tracking-widest text-zinc-500">{term}</dt>
<dd className="mt-1 text-zinc-900 dark:text-zinc-100">{value}</dd>
</div>
))}
</dl>
<Shot shot={shots.exterior} aspect="aspect-[3/2]" className="mt-8" />
<h3 className="mt-14 text-base font-medium text-zinc-900 dark:text-zinc-50">The brief</h3>
<p className="mt-2 text-sm leading-relaxed text-pretty text-zinc-600 dark:text-zinc-400">
Four small rooms, a corridor and a garage that hadn't held a car in decades. The
owners wanted somewhere to read, somewhere to sit together, and to see the garden from
the front door.
</p>
<div className="mt-8 grid grid-cols-2 gap-3">
<Shot shot={shots.chair} aspect="aspect-[4/5]" />
<Shot shot={shots.sofa} aspect="aspect-[4/5]" />
</div>
<h3 className="mt-14 text-base font-medium text-zinc-900 dark:text-zinc-50">One room</h3>
<p className="mt-2 text-sm leading-relaxed text-pretty text-zinc-600 dark:text-zinc-400">
Taking out two walls turned the corridor into the house's spine. Everything else
hangs off it: the snug, the reading corner, and the long room that opens onto the
garden.
</p>
<Shot shot={shots.living} aspect="aspect-[2/1]" className="mt-8" />
</ZoomLightbox>
</div>
</div>
);
}
Live preview, examples and props: https://keyframeui.com/components/zoom-lightbox#zoom-lightbox-case-study-demo






















