News
Sam Hook - OluKai
Meet Sam Hook! Sam Hook makes timeless leather leis—crafted with care to last a lifetime, blending tradition and modern artistry.
Shop The Story
{"id":6536076034183,
"handle":"pehuea-li-ili-womens-leather-sneakers-white",
"title":"Pehuea Lī ‘Ili - White",
"available":true,
"tags":["arch:standard","archsupport","best sellers","best selling","best:everday_wear","best:special_occasion","best:travel","BF2025","cat:shoes","collection:cart-upsell","collection:leather-sneakers","collection:social-strategist","color:White","drop-in heel®","Everyday Wear","fit:narrow","fit:standard","gender:women","gifts","labor day","land","leather","leather sneaker","material:leather","Pehaue li ilii","Peheau li Ili","peheaua","pehuea collection","pehuea li","pehuea li iii","Pehuea li Ili","pehuea li lili","pehuea li lli","pehuea li'lli","pehuea lii","Phea li ili","RGroup_20433","shoes","size:true","sneakers","Special Occasion","split_color","style:20433","Travel","womens","womenwhitesneakers","YGroup_20433"],
"type":"Women’s Everyday Leather Sneakers",
"options_with_values": [{"name":"Color","position":1,"values":["White"]},{"name":"Size","position":2,"values":["5","6","6.5","7","7.5","8","8.5","9","9.5","10","11","12"]}],
"price":15000,
"price_min":15000,
"price_max":15000,
"compare_at_price":null,"featured_image": "\/\/olukai.ca\/cdn\/shop\/files\/20433_4R4R_001_W_PehueaLiIli_WhtWht_ca2f26fa-7284-4e9d-aca1-eff46ee1e11e.png?v=1762443009",
"featured_image_aspect_ratio": 1.0,
"featured_image_width": 2000,
"featured_image_height": 2000,
"featured_image_id": 34414953169031,
"featured_image_alt": "White",
"hover_image": "\/\/olukai.ca\/cdn\/shop\/files\/20433_4R4R_102_2b7312de-ee38-494e-add7-8b37cae25f16.jpg?v=1744996244",
"hover_image_alt": "White",
"images": [{
"id": 34414953169031,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20433_4R4R_001_W_PehueaLiIli_WhtWht_ca2f26fa-7284-4e9d-aca1-eff46ee1e11e.png?v=1762443009",
"alt": "White",
"width": 2000,
"height": 2000
},{
"id": 34414953201799,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20433_4R4R_102_2b7312de-ee38-494e-add7-8b37cae25f16.jpg?v=1744996244",
"alt": "White",
"width": 1080,
"height": 1080
},{
"id": 34414953234567,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/Pehuea_Li_Ili_-_20433-4R4R_Bright_White_1.jpg?v=1744996244",
"alt": "White",
"width": 2000,
"height": 2000
},{
"id": 34414953267335,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20433_4R4R_101_ce6f5304-2f75-4b89-90f1-e31b939ab49a.jpg?v=1744996244",
"alt": "White",
"width": 1080,
"height": 1080
},{
"id": 34414953300103,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20433_4R4R_002_W_PehueaLiIli_WhtWht_bb0f58f5-8fac-48ee-9182-e398f41be8c6.png?v=1744996244",
"alt": "White",
"width": 2000,
"height": 2000
},{
"id": 34414953365639,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20433_4R4R_301jpg_20a77468-ff74-4d49-8920-944d46146e81.jpg?v=1744996244",
"alt": "White",
"width": 2000,
"height": 2000
},{
"id": 34414953431175,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20433_4R4R_302_38bf346a-d90e-42f1-b397-71f2673b1ec0.jpg?v=1744996244",
"alt": "White",
"width": 2000,
"height": 2000
},{
"id": 34414953496711,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20433-4R4R-PEHUEALI_ILI-W-WHITE_2_c0cefc4e-6435-4dd0-a808-6ea27ba1b74f.jpg?v=1744996244",
"alt": "White",
"width": 2000,
"height": 2000
},{
"id": 34414953562247,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20433_DWDW_001_W_PehueaLiIli_WhtWht.png?v=1744996244",
"alt": "White",
"width": 2000,
"height": 2000
}],
"plp_image_order_positions": "",
"sku": "20433-4R4R-5"
}
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
{"id":5156393975943,
"handle":"paniolo-womens-leather-beach-sandals-natural",
"title":"Paniolo - Natural",
"available":true,
"tags":["2024-gift-guide","arch:high","beach sandals","best sellers","best selling","best:everday_wear","best:special_occasion","best:travel","BF2025","cat:sandals","collection:chief-home-officer","collection:HGG-Slider-W","color:Natural","color:tan","etched cowgirl footbed","etched cowgirl footbef","Everyday Wear","EXP_REC_WOMEN","fit:wide","fliflops","flip flops","flip fls","flip ops","flipflps","gender:women","highest rated flip flop","interest:driving","labor day","ladies sandles","leather","likes:beach","likes:friends","likes:urban-exploring","material:leather","mothers-day","mothersday_2026","pa iolo","pani","panili","panilo","panini","paniolo","Paniolo Lipi","panolo","panpaniolo","pilono","prime","RGroup_20129","size:true","Special Occasion","split_color","style:20129","tag-test","Travel","vacation:city-downtown","vacation:tropical","wide sizes","wide width","Woman flipflops","women","Women fliplflop","women s thongs","womens","womensflip flop","Womenssandal","Womenssandals","women’s","Women’s Sandler","YGroup_20129"],
"type":"Women’s Premium Leather Sandals",
"options_with_values": [{"name":"Color","position":1,"values":["Natural"]},{"name":"Size","position":2,"values":["5","6","7","8","9","10","11"]}],
"price":13500,
"price_min":13500,
"price_max":13500,
"compare_at_price":null,"featured_image": "\/\/olukai.ca\/cdn\/shop\/files\/20129_8787_001_W_Paniolo_NatNat_787114fd-6656-4cd0-8b90-89bf49ab8a57.png?v=1762441342",
"featured_image_aspect_ratio": 1.0,
"featured_image_width": 2000,
"featured_image_height": 2000,
"featured_image_id": 34250981769351,
"featured_image_alt": "Natural",
"hover_image": "\/\/olukai.ca\/cdn\/shop\/files\/Paniolo-Natural-galleryimage-03_ebac92c3-60ce-458a-ac63-836de8339dc0.jpg?v=1743725032",
"hover_image_alt": "Natural",
"images": [{
"id": 34250981769351,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20129_8787_001_W_Paniolo_NatNat_787114fd-6656-4cd0-8b90-89bf49ab8a57.png?v=1762441342",
"alt": "Natural",
"width": 2000,
"height": 2000
},{
"id": 34250981802119,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/Paniolo-Natural-galleryimage-03_ebac92c3-60ce-458a-ac63-836de8339dc0.jpg?v=1743725032",
"alt": "Natural",
"width": 2000,
"height": 2000
},{
"id": 34250981834887,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20129_8787_201_0ae62d8d-1b93-4b94-a0cd-dc207bf243ad.jpg?v=1743725032",
"alt": "Natural",
"width": 2000,
"height": 2000
},{
"id": 34250981867655,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20129_8787_103_20d82b97-e4fe-45b0-87e6-87fdf6529f90.jpg?v=1743725032",
"alt": "Natural",
"width": 1080,
"height": 1080
},{
"id": 34250981900423,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20129_8787_101_W_PANIOLO_NaturalNatural_78141cdd-29cb-4289-a88d-9514594735ab.jpg?v=1743725032",
"alt": "Natural",
"width": 2000,
"height": 2000
},{
"id": 34250981933191,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/Paniolo-Natural-galleryimage-06_53181846-8481-486f-8908-3676869d13ba.jpg?v=1743725032",
"alt": "Natural",
"width": 2000,
"height": 2000
},{
"id": 34250981965959,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20129_8787_203_34c3def7-1883-4c0f-9a62-138da54371d5.jpg?v=1743725032",
"alt": "Natural",
"width": 2000,
"height": 2000
},{
"id": 34250981998727,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20129_8787_004_W_Paniolo_NatNat.png?v=1743725032",
"alt": "Natural",
"width": 2000,
"height": 2000
},{
"id": 34250982031495,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/20129_8787_005_W_Paniolo_NatNat.png?v=1743725032",
"alt": "Natural",
"width": 2000,
"height": 2000
}],
"plp_image_order_positions": "",
"sku": "20129-8787-5"
}
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
{"id":7413264449671,
"handle":"laeahi-li-ili-mens-leather-sneakers-bright-white",
"title":"Lae‘ahi Lī ‘Ili - Bright White",
"available":true,
"tags":["arch:standard","best sellers","best selling","best:everday_wear","cat:shoes","collection:leather-sneakers","color:Bright White","color:White","drop-in heel®","enhanced traction outsole","Everyday Wear","fit:standard","foldable heal","free shipping","gender:men","lad ahi li ili","lae ahi li lli","lae'ahi li'ili","lae'ahi li'lli","laeahi","Laeahi Ili","Laeahi li il","Laeahi Li Ili","laeahiliili","laeai","lae‘ahi collection","lae‘ahi li ili","landleather","lea ahi li ili","leaahi lilli","leather","leather runners","leather shoe","leather sneaker","leather:material","leathersneaker","material:leather","men","mens","prime","RGroup_10455","shoes","size:true","sneakers","Special Occasion","splashproof","split_color","style:10455","water poof","waterprof","waterproof","waterproof leather","white"],
"type":"Men’s Waterproof Leather Sneakers",
"options_with_values": [{"name":"Color","position":1,"values":["Bright White"]},{"name":"Size","position":2,"values":["7","8","8.5","9","9.5","10","10.5","11","11.5","12","13","14","15"]}],
"price":18000,
"price_min":18000,
"price_max":18000,
"compare_at_price":null,"featured_image": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_001_M_LaeahiLiIli_BrightWhiteBrightWhite_2.png?v=1762447392",
"featured_image_aspect_ratio": 1.0,
"featured_image_width": 2000,
"featured_image_height": 2000,
"featured_image_id": 34250982359175,
"featured_image_alt": "Lae‘ahi Lī ‘Ili - Bright White",
"hover_image": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_101_21295cc6-6ecc-42c1-8812-751a6c8b723d.jpg?v=1743725032",
"hover_image_alt": "Bright White",
"images": [{
"id": 34250982359175,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_001_M_LaeahiLiIli_BrightWhiteBrightWhite_2.png?v=1762447392",
"alt": "Lae‘ahi Lī ‘Ili - Bright White",
"width": 2000,
"height": 2000
},{
"id": 34250982457479,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_101_21295cc6-6ecc-42c1-8812-751a6c8b723d.jpg?v=1743725032",
"alt": "Bright White",
"width": 1080,
"height": 1080
},{
"id": 34250982555783,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_201_e73de815-5f18-4a8d-a3a3-a9685dffdcb7.jpg?v=1743725032",
"alt": "Bright White",
"width": 2000,
"height": 2000
},{
"id": 34250982654087,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_102_59639a46-ca4e-481a-9f42-92186ab6d3b5.jpg?v=1743725032",
"alt": "Bright White",
"width": 1080,
"height": 1080
},{
"id": 34250982752391,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_103_2da52d80-a04d-4b3e-b0fd-4d02197242f3.jpg?v=1743725032",
"alt": "Bright White",
"width": 2000,
"height": 2000
},{
"id": 34250982850695,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_102_75046f3c-0fb7-4be0-b237-ea338788fddd.jpg?v=1743725032",
"alt": "Bright White",
"width": 2000,
"height": 2000
},{
"id": 34250982981767,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_101_M_Lae_ahiLiIli_BrightWhite_4ca6a78f-0819-46ec-9c4c-771c4603178d.jpg?v=1743725032",
"alt": "Bright White",
"width": 2000,
"height": 2000
},{
"id": 34250983112839,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_004_M_LaeahiLiIli_BrightWhiteBrightWhite.png?v=1743725032",
"alt": "Bright White",
"width": 2000,
"height": 2000
},{
"id": 34250983243911,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10455_WBWB_005_M_LaeahiLiIli_BrightWhiteBrightWhite.png?v=1743725032",
"alt": "Bright White",
"width": 2000,
"height": 2000
}],
"plp_image_order_positions": "",
"sku": "10455-WBWB-7"
}
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
{"id":6624801489031,
"handle":"laeahi-lauhala-mens-leather-sneakers-fox",
"title":"Lae‘ahi Lauhala - Fox",
"available":true,
"tags":["arch:standard","best selling","best:everday_wear","best:special_occasion","best:travel","business casual","business cauals","cat:shoes","collection:HGG-Slider-M","collection:leather-sneakers","color:brown","drop-in heel®","enhance gip","enhanced traction outsole","Everyday Wear","fit:standard","foldable heal","free shipping","gender:men","interest:driving","laahi","laceless","lae ahi","lae ahi lau hal","lae ahi li lauhala","lae ahilauhala","Lae'ahi","Lae'ahi Lauhala","Laeahi Luahala","laeahi slip on","laeai","lae‘ahi collection","Lae‘ahi Lauhala","lahula","landleather","Lauhala","le ahi","Leaahi Lauhala","Leahi Lauhili","Leahu Lauhala","leather","loafer","loafers","lofer","material:leather","mens","prime","RGroup_10486","shoes","size:large","size:true","slip on shoes","Slip os","Special Occasion","split_color","style:10486","Travel","ygroup_10486"],
"type":"Men’s Woven Leather Shoes",
"options_with_values": [{"name":"Color","position":1,"values":["Fox"]},{"name":"Size","position":2,"values":["7","8","8.5","9","9.5","10","10.5","11","11.5","12","13","14","15"]}],
"price":21000,
"price_min":21000,
"price_max":21000,
"compare_at_price":null,"featured_image": "\/\/olukai.ca\/cdn\/shop\/files\/10486_FXFX_001_M_LaeahiLauhala_FoxFox_1e1b5f5e-f0ac-4963-a419-30619b97d1d4.png?v=1762443910",
"featured_image_aspect_ratio": 1.0,
"featured_image_width": 2000,
"featured_image_height": 2000,
"featured_image_id": 34587123449991,
"featured_image_alt": "Fox",
"hover_image": "\/\/olukai.ca\/cdn\/shop\/files\/LeahLauhala_1_4ac14a45-2850-48b1-9439-bc806bb66d06.jpg?v=1746706357",
"hover_image_alt": "Fox",
"images": [{
"id": 34587123449991,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10486_FXFX_001_M_LaeahiLauhala_FoxFox_1e1b5f5e-f0ac-4963-a419-30619b97d1d4.png?v=1762443910",
"alt": "Fox",
"width": 2000,
"height": 2000
},{
"id": 34587123482759,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/LeahLauhala_1_4ac14a45-2850-48b1-9439-bc806bb66d06.jpg?v=1746706357",
"alt": "Fox",
"width": 1080,
"height": 1080
},{
"id": 34587123515527,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10486_FXFX_201_255b5bd1-a503-406e-bcd5-a048358042d8.jpg?v=1746706357",
"alt": "Fox",
"width": 2000,
"height": 2000
},{
"id": 34587123548295,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10486_FXFX_002_M_LaeahiLauhala_FoxFox.png?v=1746706357",
"alt": "Fox",
"width": 2000,
"height": 2000
},{
"id": 34587123581063,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10486_FXFX_102.jpg?v=1746706357",
"alt": "Fox",
"width": 2000,
"height": 2000
},{
"id": 34587123613831,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10486_FXFX_102B_cd3f8084-3d11-446f-8773-1d6a49c08b88.jpg?v=1746706357",
"alt": "Fox",
"width": 2000,
"height": 2000
},{
"id": 34587123646599,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/LeahLauhala_2_c498676f-9abe-4e0f-9171-eb6f5ef310dc.jpg?v=1746706357",
"alt": "Fox",
"width": 1080,
"height": 1080
},{
"id": 34587123712135,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10486_FXFX_004_M_LaeahiLauhala_FoxFox.png?v=1746706357",
"alt": "Fox",
"width": 2000,
"height": 2000
},{
"id": 34587123777671,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10486_FXFX_005_M_LaeahiLauhala_FoxFox.png?v=1746706357",
"alt": "Fox",
"width": 2000,
"height": 2000
}],
"plp_image_order_positions": "",
"sku": "10486-FXFX-7"
}
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
{"id":5156384866439,
"handle":"mea-ola-mens-leather-beach-sandals-dark-java",
"title":"Mea Ola - Dark Java",
"available":true,
"tags":["arch:high","archsupport","beach sandals","beachbum","best sellers","best selling","best:special_occasion","best:travel","brotherly love","cat:sandals","collection:social-strategist","color:brown","color:Dark Java","dk java","eagles sandals","EXP_REC_MEN","fit:wide","fliflops","flip flops","flip fls","flip ops","flipflop men","flipflps","flipfops","football sandals","free shipping","gender:men","highest rated flip flop","interest:driving","jason kelce","jason keller","jason kelse","kelce","kielce","labor day","leather","likes:beach","likes:friends","likes:urban-exploring","material:leather","mea","mea oa","mea ola","meaola","mei ola","men","men s flipslops","mens","mens flipflops","Mens sansandals","men’s","meola sandal","mrs ola","nature-inspired","prime","RGroup_10138","size:small","size:true","SP-19-COLOR","SP19-Style","Special Occasion","split_color","style:10138","Travel","trendsetter","vacation:city-downtown","vacation:tropical","wide sizes","wide width","YGroup_10138"],
"type":"Men’s Leather Beach Sandals",
"options_with_values": [{"name":"Color","position":1,"values":["Dark Java"]},{"name":"Size","position":2,"values":["7","8","9","10","11","12","13","14","15"]}],
"price":18000,
"price_min":18000,
"price_max":18000,
"compare_at_price":null,"featured_image": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_001_M_MeaOla_DkjDkj_1_b644587c-c3c0-4924-89fe-a23326261020.png?v=1762441217",
"featured_image_aspect_ratio": 1.0,
"featured_image_width": 2000,
"featured_image_height": 2000,
"featured_image_id": 34587122630791,
"featured_image_alt": "Dark Java",
"hover_image": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_101_54a66018-7ade-497d-8722-d309eb7efa9b.jpg?v=1746706357",
"hover_image_alt": "Dark Java",
"images": [{
"id": 34587122630791,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_001_M_MeaOla_DkjDkj_1_b644587c-c3c0-4924-89fe-a23326261020.png?v=1762441217",
"alt": "Dark Java",
"width": 2000,
"height": 2000
},{
"id": 34587122663559,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_101_54a66018-7ade-497d-8722-d309eb7efa9b.jpg?v=1746706357",
"alt": "Dark Java",
"width": 2000,
"height": 2000
},{
"id": 34587122729095,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/MeaOla-10138-4848DarkJava_1.jpg?v=1746706357",
"alt": "Mea Ola - Dark Java",
"width": 2000,
"height": 2000
},{
"id": 34587122827399,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_302_7c6d2ac7-0fcd-430e-b3ed-925a5fef6190.jpg?v=1746706357",
"alt": "Dark Java",
"width": 2000,
"height": 2000
},{
"id": 34587122892935,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_102_1d7fafc8-8b34-4934-8f6b-14c6ed0a977e.jpg?v=1746706357",
"alt": "Dark Java",
"width": 1080,
"height": 1080
},{
"id": 34587122958471,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/MeaOla-10138-4848DarkJava_2.jpg?v=1746706357",
"alt": "Mea Ola - Dark Java",
"width": 2000,
"height": 2000
},{
"id": 34587123056775,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_103_e0f1bd9e-0183-4fab-8373-82c6e55afb72.jpg?v=1746706357",
"alt": "Dark Java",
"width": 1080,
"height": 1080
},{
"id": 34587123122311,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_102_72814dd7-d375-44d0-9602-ba62b2757c79.jpg?v=1746706357",
"alt": "Dark Java",
"width": 2000,
"height": 2000
},{
"id": 34587123220615,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_201_0618e241-6d77-4558-bb23-e6c786fb0303.jpg?v=1746706357",
"alt": "Dark Java",
"width": 2000,
"height": 2000
},{
"id": 34587123286151,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_004_M_MeaOla_DkjDkj.png?v=1746706357",
"alt": "Dark Java",
"width": 2000,
"height": 2000
},{
"id": 34587123384455,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10138_4848_005_M_Meaola_DkjDkj.png?v=1746706357",
"alt": "Dark Java",
"width": 2000,
"height": 2000
}],
"plp_image_order_positions": "",
"sku": "10138-4848-7"
}
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
{"id":5156367827079,
"handle":"hiapo-mens-leather-beach-sandals-rum-dark-wood",
"title":"Hiapo - Rum \/ Dark Wood",
"available":true,
"tags":["arch:high","beach sandals","beachbum","best sellers","best selling","best:travel","cat:sandals","color:brown","color:Rum \/ Dark Wood","EXP_REC_MEN","fit:wide","flip flops","free shipping","gender:men","hiap","hiapo","interest:driving","leather","likes:beach","material:leather","men","mens","men’s","size:small","size:true","split_color","style:10101","Travel","vacation:tropical","wide width","YGroup_10101"],
"type":"Men’s Leather Beach Sandals",
"options_with_values": [{"name":"Color","position":1,"values":["Rum \/ Dark Wood"]},{"name":"Size","position":2,"values":["7","8","9","10","11","12","13","14","15"]}],
"price":18000,
"price_min":18000,
"price_max":18000,
"compare_at_price":null,"featured_image": "\/\/olukai.ca\/cdn\/shop\/files\/10101_SK63_001_M_Hiapo_RumDkw.png?v=1762441012",
"featured_image_aspect_ratio": 1.0,
"featured_image_width": 2000,
"featured_image_height": 2000,
"featured_image_id": 32380641542279,
"featured_image_alt": "Rum \/ Dark Wood",
"hover_image": "\/\/olukai.ca\/cdn\/shop\/files\/10101_SK63_101_M_Hiapo_RumDkw.jpg?v=1736437547",
"hover_image_alt": "Rum \/ Dark Wood",
"images": [{
"id": 32380641542279,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10101_SK63_001_M_Hiapo_RumDkw.png?v=1762441012",
"alt": "Rum \/ Dark Wood",
"width": 2000,
"height": 2000
},{
"id": 32380641968263,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10101_SK63_101_M_Hiapo_RumDkw.jpg?v=1736437547",
"alt": "Rum \/ Dark Wood",
"width": 2000,
"height": 2000
},{
"id": 32470603792519,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/Hiapo-Rum-galleryimage-01_0bf8b45d-88c0-4dbe-8279-6d80fafac005.webp?v=1736437547",
"alt": "Rum \/ Dark Wood",
"width": 2000,
"height": 2000
},{
"id": 33341237493895,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10101_SK63_102.jpg?v=1736437547",
"alt": "Rum \/ Dark Wood",
"width": 1080,
"height": 1080
},{
"id": 32380642033799,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10101_SK63_102_M_Hiapo_RumDkw.jpg?v=1736437547",
"alt": "Rum \/ Dark Wood",
"width": 2000,
"height": 2000
},{
"id": 33341237526663,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10101_SK63_101.jpg?v=1736437547",
"alt": "Rum \/ Dark Wood",
"width": 1080,
"height": 1080
},{
"id": 32470603825287,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/Hiapo-Rum-galleryimage-03_a33fbee9-49c6-4a7d-ab56-816621f9410f.webp?v=1736437547",
"alt": "Rum \/ Dark Wood",
"width": 2000,
"height": 2000
},{
"id": 32380641935495,
"src": "\/\/olukai.ca\/cdn\/shop\/files\/10101_SK63_103_M_Hiapo_RumDkw.jpg?v=1736437547",
"alt": "Rum \/ Dark Wood",
"width": 2000,
"height": 2000
},{
"id": 29185387888775,
"src": "\/\/olukai.ca\/cdn\/shop\/products\/10101_SK63_005_M_Hiapo_RumDkw_4fcb3edc-7565-41db-9997-74938195c9b4.png?v=1736437547",
"alt": "Rum \/ Dark Wood",
"width": 2000,
"height": 2000
}],
"plp_image_order_positions": "",
"sku": "10101-SK63-8"
}
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>
Explore Similar Stories
{ $dispatch('product-item:images-changed', { product: product, source: 'swatch-container' }) })"
@pointercancel="intendedSwatchIndex = null; intendedSwatchEl = null"
x-data="{
wrapper: $el,
selectedSibling:product.siblings[0],
intendedSwatchIndex: null,
intendedSwatchEl: null,
swatchGap: 8,
scrollable: product.siblings.filter(s => !!s.available).length > 9.5,
canPrev: false,
canNext: true,
_edgeReady: false,
imageLimit: 0,
limitImages(nextProduct) {
const images = Array.isArray(nextProduct && nextProduct.images) ? nextProduct.images : [];
const limitedImages = this.imageLimit > 0 ? images.slice(0, this.imageLimit) : images;
const hoverImage = limitedImages[1] || null;
return Object.assign({}, nextProduct, {
images: limitedImages,
hover_image: hoverImage ? hoverImage.src : null,
hover_image_alt: hoverImage ? hoverImage.alt : null
});
},
selectProduct(nextProduct) {
const productRoot = $el.closest('.product-item');
const productData = productRoot && productRoot._x_dataStack && productRoot._x_dataStack[0];
if (productData && typeof productData.setProduct === 'function') {
productData.setProduct(nextProduct);
} else {
$data.product = this.limitImages(nextProduct);
}
},
getSwatchImageSrc(sibling) {
return sibling.images[0].src;
},
buildSwatchSrcset(src) {
if (!src) return '';
const widths = ['48', '96', '192'];
const separator = src.includes('?') ? '&' : '?';
return widths.map(w => `${src}${separator}width=${w} ${w}w`).join(', ');
},
isSwatchPriority(index) {
return window.innerWidth >= 1024 ? index < 3 : index < 2;
},
swatchBg(sibling) {
// swatch_bg precomputed at hydration. Nullish (not ||): null = no colors set
// (fall back to theme default), empty string = invalid combo, no fallback.
return sibling.swatch_bg ?? '#000000';
},
centerSwatch(el) {
if ($data.wrapper.scrollWidth <= $data.wrapper.clientWidth) return false
const wrapperRect = $data.wrapper.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
const centerPoint = wrapperRect.x + (wrapperRect.width / 2)
const behavior = window.innerWidth < 1024 ? 'auto' : 'smooth'
$data.wrapper.scrollTo({
left: $data.wrapper.scrollLeft + (elRect.x - centerPoint + (elRect.width / 2)),
behavior
})
},
selectSwatchFromTap(siblingIndex, el) {
const idx = (this.intendedSwatchIndex !== null && this.intendedSwatchIndex !== undefined)
? this.intendedSwatchIndex
: siblingIndex
const targetEl = this.intendedSwatchEl || el
this.intendedSwatchIndex = null
this.intendedSwatchEl = null
const next = product.siblings[idx]
if (next) product = next
this.centerSwatch(targetEl)
},
scroll(direction = 'forward') {
const swatch = $data.wrapper.querySelector('.product-item__swatch')
const unit = swatch ? swatch.offsetWidth + $data.swatchGap : $data.wrapper.clientWidth * 0.3
let amount = 2 * unit
$data.wrapper.scrollTo({
left: direction == 'forward' ? $data.wrapper.scrollLeft + amount : $data.wrapper.scrollLeft - amount,
behavior:'smooth'
})
},
// Lazily wire the prev/next edge detection on first desktop hover/focus —
// the arrows are a desktop affordance, so no observer is built at load and
// none is ever built on mobile. canPrev/canNext default to the at-start state.
initSwatchEdges() {
if (this._edgeReady || window.innerWidth <= 1023) return
this._edgeReady = true
const root = this.wrapper
const swatches = root.querySelectorAll('.product-item__swatch')
if (swatches.length < 2) return
const first = swatches[0], last = swatches[swatches.length - 1]
const io = new IntersectionObserver((entries) => {
const d = window.Alpine && window.Alpine.$data ? window.Alpine.$data(root) : null
if (!d) return
entries.forEach((e) => {
if (e.target === first) d.canPrev = !e.isIntersecting
if (e.target === last) d.canNext = !e.isIntersecting
})
}, { root, threshold: 0.99 })
io.observe(first)
io.observe(last)
root._swatchEdgeIO = io
}
}"
@pointerenter="initSwatchEdges()"
@focusin="initSwatchEdges()"
>