# Zoom Lightbox: Case Study

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

A shadcn/ui block from the [@keyframeui](https://keyframeui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/zoom-lightbox-case-study-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/zoom-lightbox-case-study-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/zoom-lightbox-case-study-demo-light-1afb2be5f5ed2124.png
- Registry item: https://keyframeui.com/r/zoom-lightbox-case-study-demo.json
- Categories: gallery, gesture, motion
- Listed: 2026-10-14, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/zoom-lightbox-case-study-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/zoom-lightbox.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/zoom-lightbox#zoom-lightbox-case-study-demo

## Files

### `registry/demos/zoom-lightbox-case-study-demo.tsx`

```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&apos;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&apos;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>
  );
}
```
