Purpose#
ProductGridItemClassic is the Classic-theme counterpart of ProductGridItem: it renders the grid-view product card used in listing, category, and search result pages.It mirrors ProductGridItem's behavior (media, pricing/labels, quick actions, add-to-cart) with Classic-specific markup, an image swiper, and cart-quantity syncing for items already in the cart.Where it's rendered#
It's rendered from the same kind of listing/category/search contexts as ProductGridItem, using the Classic theme variant, e.g.:{% render 'Reusables\ProductGridItemClassic\Default',
product: product,
modelId: id,
listName: listName,
position: forloop.index0
%}
Named parameters (same contract as ProductGridItem):The product object shown in the card.
Used to build a unique key (modelId-product.id) for DOM scoping.
Used for GA list tracking (select-item events).
Used for GA list tracking (select-item events).
Template behavior (Liquid + Alpine)#
High-level rendering follows ProductGridItem: brand badge, category/title, SKU/MPN blocks, dimension/variant selectors, and quick actions (comparison, wishlist, shopping lists) gated by the same GlobalData.Settings.* feature flags.Differences from ProductGridItem:Adds an image swiper (initImageSwiper) for cards with multiple product images.
Syncs displayed quantity with the item already present in the cart (cartItem, isInCart, handleCartIncrease/handleCartDecrease/handleCartQuantityInput).
Supports viewport-based, batched hydration of prices/constraints (hydrateOnViewport, scheduleConstraintsHydration, schedulePriceHydration).
x-data='productgriditemclassicreusabledefault.initComponent({{ product | serialize | escape }}, "{{ Reusables.ProductGridItemClassic.Translations.AddToCartErrorMessage }}", "{{ Reusables.ProductGridItemClassic.Translations.UpdateProductQuantityErrorMessage }}")'
Defensive note: initComponent normalizes product.productVariants to [] when it's missing/not an array (catalog data may omit variants for the current visitor), so downstream getters never dereference undefined.Data contract (JS runtime)#
Model shape (storefront example)#
Same product shape as ProductGridItem — see that page's Model shape sample for the sanitized JSON (id, title, alias, mediaItems[], dimension1/dimension2, productVariants[], price fields).JavaScript#
Global object#
Global object: productgriditemclassicreusabledefaultx-data="productgriditemclassicreusabledefault.initComponent(product, addToCartErrorMessage, updateProductQuantityErrorMessage, isProductSection, hydrateOnViewport, listName)"
initSwiper(refs)#
Creates a Swiper instance for dimension pickers (color/size carousels), same as ProductGridItem.initImageSwiper(refs)#
Creates a Swiper instance for the card's product image gallery, enabling loop mode and next/prev navigation.initComponent(product, addToCartErrorMessage, updateProductQuantityErrorMessage, isProductSection, hydrateOnViewport, listName)#
Initializes local Alpine state for dimension/variant selection, quantity handling, and cart syncing. Normalizes product.productVariants to an empty array when missing.init#
Alpine lifecycle method. Sets up dimension selection defaults, registers listeners, and (when hydrateOnViewport is enabled) schedules price/constraint hydration once the card enters the viewport.getTitleCatHeight#
Measures the title/category block and writes a CSS variable used for consistent card-height layout.handleClickEvents#
Wires middle-click/Ctrl-click handling so the card opens in a new tab while still sending GA tracking.sendSelectEvent#
Sends a GA selectItem event for the clicked product.sendSelectEventAndRedirect#
Sends a GA selectItem event and then navigates to the product page.setSelectedColor#
Sets the selected Color dimension id/label and recomputes the selected variant.setSelectedOther#
Sets the selected non-color dimension id/label and recomputes the selected variant.setDimension#
Generic setter for either dimension slot; recomputes the selected variant.updateSelectedVariant#
Finds the variant matching the current dimension selections, updates selectedVariant, and refreshes quantity/active image state.selectedVariant#
Getter that returns the currently selected variant, or null when the selection is incomplete.hasMultipleVariants#
Getter indicating whether the product has more than one variant.allDimensionsSelected#
Getter indicating whether the required dimension selections are complete.isMultiVariant / isSingleVariant#
Getters that classify the product as multi- or single-variant for UI branching.Getter controlling whether the add-to-cart button is shown (desktop: always; mobile: single-variant only).Getter controlling whether the "choose variant" mobile modal trigger is shown.displayStartPrice / displayFinalPrice / displayOriginalPrice#
Getters controlling which price block (range/start, final, original/discounted) is shown for the current selection.isOtherOptionOrderable(otherId)#
For 2-dimension products, checks whether a given "other" dimension value is orderable with the currently selected color.isSingleOptionOrderable(dimId, isDim1)#
Checks whether a single-dimension option is orderable by looking at matching variants' quantity constraints.canAddToCart#
Returns whether the current selection is orderable and not blocked by quantity constraints.handleAddToCart#
Adds the selected variant to the cart via servicesreusabledefault.addToCart(...), then updates quantity constraints and the cart-synced quantity from the response.variantOrDefault(path, displayNullLine)#
Reads a value from the selected variant first, falling back to the product; returns - when displayNullLine is true and the value is missing.getDynamicLabelClass(el)#
Computes a CSS class for availability labels based on variantOrDefault('stockAvailability.label').getDynamicColorStyle#
Computes an inline style (background color/gradient) for color swatches based on textColor values, supporting the two-hex "split" color pattern.Locates the image matching a media item id and slides the image swiper to it.swiperInitSingleProductImages#
Initializes the Swiper/LightGallery integration used when the card renders inside a product-single section.Sticky-positioning helpers used only when the card is rendered inside a product-single section (isProductSection === true).Scrolls smoothly to a section and updates the active sticky button.increaseQuantity / decreaseQuantity#
Adjust the local quantity using the selected variant's additive quantity constraints (step/min/max).debounceUpdateProduct#
Debounces calls to updateQuantity(...) per product id, cancelling in-flight quantity-check requests via Axios cancel tokens.updateQuantity(newQuantity, product, productVariantId, checkQuantity, useDebounce)#
Validates/normalizes the quantity server-side (via servicesreusabledefault.updateProductQuantity) when checkQuantity is true, otherwise updates the local quantity directly.runPriceHydration / schedulePriceHydration#
Hydrate personalized/ERP prices client-side (batched per page) when the card enters the viewport, since the server renders cards from a cached price-free view model.runConstraintsHydration / scheduleConstraintsHydration#
Hydrate personalized quantity constraints client-side (batched per page) when the card enters the viewport; falls back to permissive defaults (min: 1, step: 1, max: 999999) on failure.cartItem#
Getter that finds the matching item already present in $store.cart, used to display the current cart quantity on the card.isInCart#
Getter that returns whether the currently selected variant is already present in the cart.applyCartItemQuantity#
Syncs the local quantity field from the matching cart item, so the card reflects quantities already added.Update the quantity of an item already in the cart (increment, decrement, or direct input) and persist the change via servicesreusabledefault.updateProductCart(...).debouncedUpdateCartItemQuantity#
Debounces cart-quantity updates from the increase/decrease/input handlers before calling the cart update service.removeCartItem#
Removes the matching item from the cart via servicesreusabledefault.removeFromCart(...) when quantity is reduced to zero (or explicitly removed).Global Alpine stores#
$store.cart (reading/updating the item already in the cart)
$store.productModal (used indirectly by nested reusables/quick-add flows that may open the variant modal from this card)
$store.toast (error feedback for add-to-cart/quantity updates)
Services / API calls#
Uses servicesreusabledefault for cart and quantity-related operations, same as ProductGridItem, plus cart-quantity syncing calls used only in the Classic variant:servicesreusabledefault.hydrateProductPrices(products)
servicesreusabledefault.hydrateProductConstraints(product)Falls back to servicesreusabledefault._applyDefaultConstraints(product) when hydration fails, keeping the card orderable with permissive defaults.
servicesreusabledefault.addToCart(...)
servicesreusabledefault.updateProductQuantity(...)
servicesreusabledefault.updateProductCart(...) — used by handleCartIncrease/handleCartDecrease/handleCartQuantityInput to persist quantity changes for an item already in the cart.
servicesreusabledefault.removeFromCart(...) — used by removeCartItem to remove the item when quantity reaches zero.
Dependencies#
Liquid: Reusables/ProductGridItemClassic/Default.liquid
JS: Reusables/ProductGridItemClassic/Default.js
Translations: Reusables/ProductGridItemClassic/Default.json
Nested reusables: Reusables/ProductComparisonButton/Default, Reusables/WishlistButton/Default, Reusables/ShoppingListsButton/Default
Vendor JS: Swiper, LightGallery
Notes#
Functionally equivalent to ProductGridItem; consult that page for the full dimension/variant selection model shape.
Cart-quantity syncing (cartItem, isInCart, applyCartItemQuantity, cart increase/decrease/input handlers, removeCartItem) is Classic-specific and not present in ProductGridItem.
Modified at 2026-07-24 07:19:31