Skip to content

PDP: aspect ratio prop on gallery; suppress OOS card hover slideshow#248

Merged
laugharn merged 2 commits intomainfrom
pdp-aspect-ratio-and-oos-slideshow
May 5, 2026
Merged

PDP: aspect ratio prop on gallery; suppress OOS card hover slideshow#248
laugharn merged 2 commits intomainfrom
pdp-aspect-ratio-and-oos-slideshow

Conversation

@laugharn
Copy link
Copy Markdown
Contributor

@laugharn laugharn commented May 4, 2026

Summary

Two related polish items on top of the ProductCard aspectRatio work in #233:

  • PDP gallery now accepts aspectRatio. ProductDetailPage, ProductDetailSection, and ProductMedia take an optional aspectRatio: "landscape" | "portrait" | "square" (default "square"). The mobile carousel, the desktop 2×2 grid, and the color-image slot helpers pick the matching aspect-* class via the same data-[aspect-ratio=…] selector as the product card. Page-level and color-slot Suspense skeletons match the chosen aspect to avoid layout shift. The lightbox modal is intentionally left alone — it still fills the viewport with object-contain.
  • Out-of-stock cards no longer reveal the hover slideshow. ProductCardImage previously rendered ProductCardSlideshow regardless of stock. Because the OOS overlay is bg-black/60 (translucent), users hovering an OOS card on lg+ saw images cycling underneath the dim "Sold out" tint. Slideshow rendering is now skipped when outOfStock is true, which also avoids the wasted next/image requests.

aspectRatioClasses is exported from product-card/components.tsx so the PDP gallery and the page-level fallback skeleton reuse the same selector string instead of duplicating it.

Includes a template-rollout-log entry for downstream forks.

Test plan

  • pnpm --filter template lint and pnpm --filter template build clean
  • PDP renders unchanged at the default square
  • Pass aspectRatio="portrait" to <ProductDetailPage> in app/products/[handle]/page.tsx; the mobile carousel and desktop 2×2 grid switch to 3:4
  • Click a grid item with aspectRatio="portrait"; lightbox modal still fills the viewport (no aspect frame)
  • Hover an OOS product card with multiple images at lg+; no slideshow images cycle behind the dim overlay
  • Skeleton fallbacks (page-level + color-slot) reserve the right shape (no jump on data resolve)

🤖 Generated with Claude Code

Two related polish items on top of the ProductCard aspectRatio work
in #233:

1. Apply aspectRatio to the PDP gallery. ProductDetailPage,
   ProductDetailSection, and ProductMedia now accept an optional
   aspectRatio: "landscape" | "portrait" | "square" (default
   "square"). The mobile carousel items, the desktop 2x2 grid items,
   and the color-image slot helpers pick the matching aspect-* class
   via the same data-[aspect-ratio=...] selector pattern as the
   product card. Page-level and color-slot Suspense skeletons also
   match the chosen aspect to avoid layout shift. The lightbox modal
   is intentionally left alone; it still fills the viewport with
   object-contain so the full image is always visible.

2. Skip the hover slideshow on out-of-stock product cards. The OOS
   overlay is bg-black/60 (translucent), so users hovering an
   out-of-stock card on lg+ saw images cycling underneath the dim
   "Sold out" tint. Suppressing ProductCardSlideshow rendering when
   outOfStock is true is the simplest fix and avoids the wasted
   next/image requests.

aspectRatioClasses is exported from product-card/components.tsx so
the PDP gallery and the page-level fallback skeleton reuse the same
selector string instead of duplicating it.

Adds a template-rollout-log entry for downstream forks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
shop-docs Ready Ready Preview, Comment May 5, 2026 1:42am
shop-template Ready Ready Preview, Comment, Open in v0 May 5, 2026 1:42am

…249)

Two priority bugs in ProductMedia:

1. Mobile carousel under-prioritized in color-partitioned mode. The
   shared-items map used priority={!children && idx === 0}. Whenever
   a mobileSlot was passed (the resolved-color-images Suspense node),
   children was always truthy, so no shared item was marked priority
   even if the resolved color images turned out to be empty. The
   mobile LCP candidate was lazy-loaded.

2. Desktop grid double-prioritized. GridItem used priority={idx < 2}
   independently for color images and shared items. With color
   partitioning on, the first two color images were priority and the
   first two shared items at visual positions 3/4 also got priority,
   so four <link rel=preload> ended up competing for a single PDP.

Fix:

- Separate priority (the single LCP candidate; fetchPriority high +
  preload) from loading=eager (above-the-fold but not LCP; loads now
  without fighting the LCP for bandwidth).
- Compute hasColorSlot once in ProductMedia and pass it down.
- Color slot present: colorImage[0] is priority, colorImage[1] is
  eager, shared[0] is eager (covers the case where color resolves to
  0 or 1 images).
- No color slot: shared[0] is priority, shared[1] is eager.

MediaImage now takes an explicit eager prop so loading semantics
aren't conflated with priority. MediaVideo's preview image keeps the
combined priority||eager signal — small cost, and the preview is
what affects perceived load.

Adds a template-rollout-log entry.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@laugharn laugharn merged commit 35d93f2 into main May 5, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant