Component

PhotoGrid

The full homepage gallery — eight rows of ParallaxCards pulled from Sanity or falling back to Unsplash. Rows reveal independently on scroll.

Architecture

The gallery is rendered directly in apps/web/app/page.tsx as either SanityGallery (when CMS is configured) or UnsplashFallbackGallery.

tsx
// Sanity CMS (when configured)
<SanityGallery photos={photos} />

// Unsplash fallback (no CMS)
<UnsplashFallbackGallery />

Image sizes

Source widths are requested from Sanity CDN based on column count, and the sizes hint tells Next.js Image the rendered viewport width so it selects the correct srcset entry. All images use quality=75 and auto='format' (AVIF → WebP → JPEG).

ColumnsCDN widthnext/image sizes hint
1 — Full-width1920px100vw
2 — Two-col1200px(max-width: 768px) 100vw, 50vw
3 — Three-col900px(max-width: 768px) 100vw, 33vw
4 — Four-col700px(max-width: 768px) 100vw, 25vw
tsx
// CDN request width is chosen by column count — 2× retina headroom
const colWidths = { 1: 1920, 2: 1200, 3: 900, 4: 700 }
const src = urlFor(photo.image)
  .width(colWidths[row.length])
  .auto('format')
  .quality(75)
  .url()

// sizes hint — prevents Next.js picking an oversized srcset on mobile
const colSizes = {
  1: '100vw',
  2: '(max-width: 768px) 100vw, 50vw',
  3: '(max-width: 768px) 100vw, 33vw',
  4: '(max-width: 768px) 100vw, 25vw',
}

<Image src={src} sizes={colSizes[row.length]} fill quality={75} />

Row stagger animation

Each GalleryRow triggers independently when scrolled into view (viewport margin: -80px). Cards within each row stagger at 0.3s intervals using Framer Motion variant propagation — no prop threading required.