@pagix/plp-product-gallery
v0.1.0
Published
Anvilkit Puck-native PLP product gallery carousel — horizontal scroll of product cards with corner label, title, price, learn-more / buy buttons, plus a sticky bottom nav with 'View all' and prev/next arrows.
Downloads
98
Readme
@pagix/plp-product-gallery
Puck-native PLP (product listing page) product gallery carousel — a replica of the Huawei consumer-site "Which phone suits you best?" module. Horizontal CSS scroll-snap track of product cards (image, red corner label, title, price, learn-more / buy buttons) plus a sticky bottom nav with a "View all" pill and prev/next arrows.
RSC-safe by design: no "use client", no React hooks. The prev/next
buttons are wired by a per-instance inline <script> that calls
scrollBy and toggles disabled at the scroll ends (variant-banner
pattern).
Install
pnpm add @pagix/plp-product-gallery @anvilkit/ui @puckeditor/coreImport the CSS side-effect from @pagix/plp-product-gallery (it is
imported automatically by the package entry).
Usage
import {
PlpProductGallery,
type PlpProductGalleryProps,
} from "@pagix/plp-product-gallery";
const props: PlpProductGalleryProps = {
title: "哪一款手机最适合你?",
viewAllLink: "/cn/phones/",
products: [
{
productTitle: "HUAWEI Mate XT 2 非凡大师",
title: "HUAWEI Mate XT 2 非凡大师",
image: "/img/mate-xt-2.png",
link: "/cn/phones/mate-xt-2",
price: 19999,
cornerText: "最新",
},
],
};
<PlpProductGallery {...props} />;In Puck, register the config factory:
import { createPlpProductGalleryConfig } from "@pagix/plp-product-gallery";
const config = {
components: {
PlpProductGallery: createPlpProductGalleryConfig({ locale: "zh" }),
},
};The factory supports { locale?: "en" | "zh" | "ja" | "ko", messages? }
and translates the editor chrome (field labels, style-target labels) and
the component's default copy.
Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| title | string | "哪一款手机最适合你?" | Section heading |
| viewAllLink | string | "/" | Bottom "View all" link |
| products | PlpProductItem[] | 3 sample phones | Product cards |
| cornerColor | string | "" | Corner label color; empty → brand red #ce0e2d |
| slideBackground | string | "" | Per-card background; empty → none |
| slideRadius | number | 0 | Card corner radius (px); 0 → square |
| slidePadding | number | 0 | Card inner padding (px); 0 → none |
| slideWidth | number | 0 | Card width (px); 0 → responsive breakpoint widths |
| slideHeight | number | 0 | Card height (px); 0 → auto, fixed height clips content |
| slideGap | number | 0 | Gap between cards (px); 0 → responsive breakpoint gaps |
| viewAllText | string | "" | View-all button label; empty → locale default |
| viewAllAlign | "left" \| "center" \| "right" | "left" | View-all button alignment in the nav row |
| viewAllFontSize | number | 0 | View-all font size (px); 0 → default 14px |
| viewAllBackground | string | "" | View-all button background; empty → foreground token |
| arrowBackground | string | "" | Prev/next arrow background; empty → secondary token |
| arrowSize | number | 44 | Prev/next arrow button size (square, px) |
| arrowsAlign | "left" \| "right" | "right" | Which side of the nav row the arrows sit on |
| classNames | Record<string, string> | — | Tailwind passthrough per style target (root, track, item, viewAllBtn, arrow-prev, arrow-next) |
| animation | AnimationProps | — | Entrance animation (none / fade-in / slide-up / slide-down / zoom-in) |
PlpProductItem
| Field | Type | Description |
| --- | --- | --- |
| productTitle | string | Detail-page title attribute (SEO / a11y) |
| title | string | Display name |
| image | string | Image URL or data URL (the editor field accepts a link or an upload from device); empty → gradient placeholder |
| imageAlt | string | Alt text (defaults to title) |
| link | string | Detail page link |
| price | number | Rendered as currency + price + 起 |
| currency | string | Default ¥ |
| cornerText | string | Red corner label, e.g. 最新 |
| learnMoreLink | string | Learn-more button link (defaults to link) |
| buyLink | string | Buy button link (defaults to link) |
| learnMoreText | string | Learn-more button label; empty → locale default |
| learnMoreFontSize | number | Learn-more font size (px); 0 → default 14px |
| learnMoreBackground | string | Learn-more button background; empty → foreground token |
| learnMoreAlign | "left" \| "center" \| "right" | Learn-more button alignment inside the card (default center) |
Responsive breakpoints
| Breakpoint | Slide width | Gap | Image height | Title size | | --- | --- | --- | --- | --- | | < 768 (mobile) | 188px | 36px | 176px | 16px | | 768–1079 (tablet) | 224px | 66px | 176px | 16px | | 1080–1440 (pc) | 218px | 94px | 218px | 16px | | 1441–1919 (pc-big) | 280px | 90px | 240px | 20px | | 1920+ | 280px (1760px centered) | 90px | 240px | 20px |
Image field
The products[].image inspector field is a custom URL+upload row
(imageUrlField, same shape as video-banner): paste a link or click
"Upload from device" — uploads are stored as data URLs in the document
payload. The production upload pipeline remains the host's
@anvilkit/plugin-asset-manager.
Accessibility
- Product images carry
alt; image links carry the product title as content,productTitleas the a11y label. - Prev/next buttons have
aria-labeland a visibledisabledstate. - The track is a native scroll container: touch swipe, trackpad scroll and scrollbar-free keyboard arrows work without JavaScript.
- Entrance animations respect
prefers-reduced-motion.
editMode
All card links and the "View all" link render as inert <span>s in
editMode so the editor canvas stays click-safe.
i18n
Four locale catalogs (en / zh / ja / ko) with identical key
sets, enforced by the workspace test suite.
