VariantContent reusable renders product variant selection UI (dimensions like Color/Size), plus basic product header info (category/title/image) and an Add to cart action.$store.productModal.product.quickAddModal: true mode (used in Cart for a compact “available variants” experience)Reusables/ProductGridItem/Default.liquid{% render 'Reusables\\VariantContent\\Default' %}Components/Cart/Default.liquid{% render 'Reusables\\VariantContent\\Default', quickAddModal: true %}VariantContent is used in two distinct UI contexts:Components/Cart/Default.liquid with quickAddModal: true.AvailableVariants titleReusables/ProductGridItem/Default.liquid.VariantContent is used as part of the product tile/quick-view behavior.ProductGridItem and the product-modal store; VariantContent itself just reads $store.productModal.product and renders accordingly.quickAddModal (boolean)true, the template shows the “Available variants” title and a product image block at the top.isQuickAddModal = true and resets selection state when the modal product changes.$store.productModal.product<template x-if="$store.productModal.product">
<div x-data="variantcontentreusabledefault.initComponent($store.productModal.product, true)">
...
</div>
</template>$store.productModal.product that you provided (sanitized by collapsing long arrays like searchAttributes and productVariants).{
"vatIncluded": false,
"vatRate": 25,
"reducedVatRate": 5,
"isMpnVisible": true,
"isSkuVisible": true,
"title": "Sample product title",
"alias": "lefki-blouza-gynaikeia",
"status": "Active",
"categoryId": "example-category-id",
"categoryName": "Sample category",
"categoryLink": "/category/blouzes-top",
"availability": "Unavailable",
"id": "example-product-id",
"companyId": "example-company-id",
"link": "product/lefki-blouza-gynaikeia",
"mediaItems": [
{
"id": "example-media-id-1",
"link": "https://example.com/media/product-woman-top-4.jpg",
"position": 0,
"alt": "product-woman-top-4.jpg",
"mediaType": "Image"
},
{
"id": "example-media-id-2",
"link": "https://example.com/media/green.pdf",
"position": 1,
"alt": "green.pdf",
"mediaType": "Attachment"
}
],
"variantCount": 24,
"productVariants": [
{
"id": "example-variant-id-1",
"dimension1ItemId": "example-dim1-item-red",
"dimension2ItemId": "example-dim2-item-m",
"sku": "kokkino-medium",
"sellOutOfStock": true,
"canOrder": true,
"startQuantity": 1,
"price": 0,
"unitPrice": 0
},
{
"id": "example-variant-id-2",
"dimension1ItemId": "example-dim1-item-green",
"dimension2ItemId": "example-dim2-item-l",
"sellOutOfStock": true,
"canOrder": true,
"startQuantity": 1,
"price": 25,
"unitPrice": 25,
"finalPriceText": "25,00 €",
"finalPrice": 25
},
{
"id": "example-variant-id-3",
"dimension1ItemId": "example-dim1-item-black",
"dimension2ItemId": "example-dim2-item-m",
"gtin": "12334566",
"mpn": "12334566",
"sku": "12334566",
"sellOutOfStock": true,
"canOrder": true,
"startQuantity": 1,
"price": 50,
"unitPrice": 50,
"finalPriceText": "50,00 €",
"finalPrice": 50
}
],
"dimension1": {
"name": "Color",
"type": "Color",
"items": [
{ "id": "example-dim1-item-black", "value": "Black", "textColor": "#000000" },
{ "id": "example-dim1-item-red", "value": "Red", "textColor": "#d64343" },
{ "id": "example-dim1-item-green", "value": "Green", "textColor": "#1aa189" }
]
},
"dimension2": {
"name": "Size",
"type": "Size",
"items": [
{ "id": "example-dim2-item-s", "value": "S" },
{ "id": "example-dim2-item-m", "value": "M" },
{ "id": "example-dim2-item-l", "value": "L" },
{ "id": "example-dim2-item-xl", "value": "XL" }
]
},
"hasPriceRange": false,
"inWishlist": false
}product.categoryLink exists).variantOrDefault('title').quickAddModal: true:AvailableVariants heading./product/${product.alias}.product.isMpnVisible → prints variantOrDefault('mpn', true)product.isSkuVisible → prints variantOrDefault('sku', true)displayNullLine = true, variantOrDefault returns -.product.dimension1, product.dimension2).type === 'Color', it is treated as the color dimension.Reusables/VariantContent/Default.js.initComponent that initializes selection state and watches $store.productModal.product to re-initialize when the modal product changes.Reusables/VariantContent/Default.js exposes variantcontentreusabledefault.initComponent(product, isQuickAddModal = false): factory that returns Alpine state + handlers for variant selection.Alpine.store('productModal').product and re-initializes selection when it changes.init(): runs on Alpine mount to set up initial selection state and watchers.selectedVariant: currently resolved variant (falsy when selection is incomplete).updateSelectedVariant(): recomputes selectedVariant when a user changes a dimension selection.allDimensionsSelected(): returns whether the current selection is complete.hasMultipleVariants(): indicates whether the product behaves as multi-variant in the UI.isMultiVariant(): returns whether the current product is treated as multi-variant.isSingleVariant(): returns whether the current product is treated as single-variant.handleClickEvents(...): centralizes click handlers for dimension buttons.displayStartPrice(): returns whether start price should be shown.displayFinalPrice(): returns whether final price should be shown.displayOriginalPrice(): returns whether original price should be shown.updateSelectedVariant() (method): recomputes selectedVariant when a user changes a dimension selection.allDimensionsSelected() (method): returns whether the current selection is complete (based on which dimensions exist).hasMultipleVariants() (method): indicates whether the product behaves as multi-variant in the UI.isMultiVariant() / isSingleVariant() (methods): classify product variant mode for UI branching.handleClickEvents(...) (method): centralizes click handlers for dimension buttons (sets selected ids + triggers recomputation).displayStartPrice() returns whether start price should be shown.displayFinalPrice() returns whether final price should be shown.displayOriginalPrice() returns whether original price should be shown (discount UI).dimension1ItemId must match selectedDim1Id (when product.dimension1 exists)dimension2ItemId must match selectedDim2Id (when product.dimension2 exists)variantOrDefault(path) resolution.product.productVariants[selectedVariantIndex])product)- when displayNullLine = truepath can be either a string like 'sku' or 'quantityConstraints.additive.minimum' or an array of path segments.this.customColorActive when the object being read has:stockAvailability.label === 'Custom' and a truthy stockAvailability.color.handleAddToCart(closeModal = false): clears toasts and proceeds only if canAddToCart() allows it.servicesreusabledefault.addToCart(productId, variantId, minQty, finalPrice, originalPrice, showPopUp)productId is this.product.idvariantId is variant.id (from this.product.productVariants[this.selectedVariantIndex])minQty is variant?.quantityConstraints?.additive?.minimumfinalPrice is variant.finalPriceoriginalPrice is variant.originalPriceshowPopUp is set based on closeModal$store.toast.add(addToCartErrorMessage, 'ic-warning', 'error')$store.productModal (when closeModal is true)response.data.cartItems and updates:variant.quantityConstraints = addedProduct?.quantityConstraintsupdateQuantity(...) using the minimum additive quantity from the server:updateQuantity(addedProduct?.quantityConstraints?.additive?.minimum, addedProduct, addedProduct.productVariantId)$store.productModalproduct$store.toastremoveAll()add(...) (for errors/success feedback)servicesreusabledefault for add-to-cart and quantity-validation operations.servicesreusabledefault.addToCart(...)servicesreusabledefault.updateProductQuantity(data, cancelToken)data shape used by this reusable:productId: product.productId || product.idproductVariantIdrequestedQuantitycancelToken is an Axios cancel token (axios.CancelToken.source().token)addToCartErrorMessage (toast on add-to-cart failure)updateProductQuantityErrorMessage (toast on quantity update failure)response.status < 200 || response.status >= 300 || response?.success === falseReusables/VariantContent/Default.liquidReusables/VariantContent/Default.jsReusables/VariantContent/Default.json (consumed as Reusables.VariantContent.Translations.*)product.mediaItems[0].link when present; otherwise it falls back to GlobalData.Settings.noImage.link or the theme fallback /Assets/images/no-image.svg.item.textColor; the template supports one or two concatenated hex colors (e.g. #RRGGBB#RRGGBB) and renders split halves.