1<!doctype html> 2<html lang="en"> 3<head prefix="og: http://ogp.me/ns# cms: http://ogp.me/ns/cms#"> 4 <meta charset="utf-8"/> 5<meta name="title" content="Shop Sneakers, Footwear and Clothing Online | Studio 88"/> 6<meta name="description" content="At Studio 88 we make it our business to bring you the latest in top international and local fashion brands. Shop Online - Free shipping for orders over R800. Free Click & Collect over R500."/> 7<meta name="keywords" content="Online Shop, sneakers, clothing, accessories, fashion, shop online, Studio 88, adidas, nike, vans, converse, puma"/> 8<meta name="robots" content="index,follow"/> 9<meta name="viewport" content="width=device-width, initial-scale=1"/> 10<title>Shop Sneakers, Footwear and Clothing Online | Studio 88</title> 11<link rel="stylesheet" type="text/css" media="all" href="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/css/styles.css" /> 12<link rel="stylesheet" type="text/css" media="all" href="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/css/custom-styles.css" /> 13<link rel="stylesheet" type="text/css" media="all" href="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/elegant-icons/style-min.css" />
14<script type="text/javascript" src="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/js/custom-scripts.js"></script>
14 15<!--[if IE 11]>
16<script type="text/javascript" src="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/Klevu_Search/js/klevu/ieUrlPolyfill.js"></script>
16 17<![endif]--> 18<link rel="canonical" href="https://www.studio-88.co.za/" /> 19<link rel="icon" type="image/x-icon" href="https://www.studio-88.co.za/media/favicon/stores/1/favicon-512.png" /> 20<link rel="shortcut icon" type="image/x-icon" href="https://www.studio-88.co.za/media/favicon/stores/1/favicon-512.png" /> 21<meta name="msvalidate.01" content="BF0E6599AD311D014E8773393CE8DA92" /> 22 23<meta name="apple-itunes-app" content="app-id=6736969296, app-argument=studio88://"> 24 25<style> 26.checkout-index-index #checkOut {display: block; visibility: visible;} 27.checkout-index-index .page-wrapper {background: #fff;} 28.checkout-index-index .page-wrapper .page-header {background: #000;} 29.checkout-index-index .page-wrapper .page-header .header.content {padding-top: 20px; background: #000;} 30.checkout-payment-method .payment-method-title .payment-icon {max-width: 120px;} 31.checkout-index-index .logo {margin-bottom: 12px;} 32.checkout-index-index .checkout-header .title {font-weight: 700; color: #000; font-size: 24px; line-height: 32px;} 33.checkout-index-index .checkout-header .description {font-size: 15px; line-height: 23px;} 34html .checkout-index-index .authentication-wrapper button.action-auth-toggle { 35 background: #d1021d !important; 36 font-size: 15px; 37 min-width: 125px; 38 font-weight: 500; 39 border-radius: 3px !important; 40 font-family: 'Roc Grotesk', Montserrat, Arial, sans-serif; /* Changed here */ 41 padding: 5px 15px !important; 42 line-height: 30px; 43 text-transform: uppercase; 44} 45html .checkout-index-index .authentication-wrapper button.action-auth-toggle:hover {color: #fff; background: #B6021A !important;} 46.checkout-index-index .checkout-payment-method .payment-option-title .action-toggle {color: #000; text-decoration: underline; font-size: 15px; line-height: 23px;} 47.checkout-index-index .checkout-payment-method .payment-option-title .action-toggle:hover {color: #B6021A;} 48.checkout-index-index .payment-option-content .actions-toolbar {display: block !important;} 49.checkout-index-index .payment-option-content .actions-toolbar .primary .action-apply { 50 background: #fff !important; 51 border: 1px solid #000 !important; 52 text-transform: uppercase; 53 font-size: 15px; 54 min-width: 165px; 55 color: #000 !important; 56 font-weight: 500; 57 border-radius: 3px !important; 58 font-family: 'Roc Grotesk', Montserrat, Arial, sans-serif; /* Changed here */ 59 padding: 5px 15px; 60} 61.checkout-index-index .payment-option-content .actions-toolbar .primary .action-apply:hover {border-color: #B6021A !important; color: #B6021A !important;} 62.checkout-index-index .columns .column.main {max-width: 100%; padding-bottom: 15px;} 63.checkout-index-index p.text-sm.text-gray-200 {color: #aeaeae; margin: 10px 0; font-size: 14px; line-height: 18px;} 64.checkout-index-index .bg-black.text-center.py-6 {text-align: center; background: #000; padding: 1.5rem;} 65.checkout-index-index a.text-white.underline {color: #fff; text-decoration: underline;} 66.checkout-index-index a.text-white.underline:hover {color: #d1021d;} 67.amcheckout-step-container input[type='checkbox']+label:hover:before, 68.amcheckout-step-container input[type='radio']+label:hover:before, 69.amcheckout-step-container input[type='checkbox']:checked+label:before, 70.amcheckout-step-container input[type='radio']:checked+label:before {border-color: #d1021d; box-shadow: none;} 71._amcheckout-selectable-item.-selected, 72.amcheckout-step-container .amcheckout-shipping-address .billing-address-details.-selected, 73.amcheckout-step-container .amcheckout-shipping-address .shipping-address-details.-selected, 74.amcheckout-step-container .shipping-address-items .shipping-address-item.-selected, 75.amcheckout-step-container .shipping-address-items .shipping-address-item.selected-item, 76.amcheckout-step-container .amcheckout-shipping-methods .amcheckout-method.-selected, 77.amcheckout-step-container .checkout-payment-method .amcheckout-payment-methods .payment-method.-selected, 78.amcheckout-step-container .checkout-payment-method .amcheckout-payment-methods .payment-method._active {border-color: #d1031d;} 79.checkout-index-index .payment-method .payment-method-title span {text-align: right; font-size: 13px; margin-left: 8px; width: 100%;} 80span.payment-type-label, span.payment-type-label-img {width: auto;} 81.amcheckout-step-container .checkout-payment-method .amcheckout-payment-methods .payment-method._active .payment-method-content>* {margin-bottom: 0;}
82.checkout-billing-address .billing-address-same-as-shipping-block {font-size: 14px; font-style: italic;} 83 84.filter-content .items.-am-singleselect .item .items-children 85{ 86 min-width: 150px !important; 87} 88.filter-content .items.am-labels-folding.is-by-click .item { 89 position: relative; 90 padding-left: 1.0rem; 91} 92 93.filter-content .items.-am-singleselect .item .items-children .category-filter-row a, .filter-content .items.amshopby-fly-out-view .item .items-children .category-filter-row a { 94 margin-left: 10px; 95} 96 97.filter-content .items .item .filter-row.category-filter-row a { 98 margin-left: 10px; 99} 100 101.amblog-mobile-aside-wrapper { 102display: none; 103} 104.ozow-happypay-widget.my-3 { 105 display: none; 106} 107</style> 108 109
110<script> 111 //canonical tag script start 112 const urlParams = new URLSearchParams(window.location.search); 113 if (urlParams.has('cat') && urlParams.get('cat').trim() !== '') { 114 var canonical = document.createElement('link'); 115 canonical.setAttribute('rel', 'canonical'); 116 canonical.setAttribute('href', window.location.href); 117 document.head.appendChild(canonical); 118 } 119 //canonical tag script end 120 </script>
120 121 122<meta property="og:image" content="https://www.studio-88.co.za/media/logo/stores/1/studio88-256.png"/> 123<meta property="og:image:type" content="image/png" /> 124<meta property="og:image:width" content="256" /> 125<meta property="og:image:height" content="256" /> 126<meta property="og:image:alt" content="Studio 88 Logo" /> 127 128<!-- GTM-checkout-forceload-test -->
128<script>document.addEventListener("DOMContentLoaded",function(){if(typeof window.callAmGA4GTM==="function"&&!window.amastyGa4GtmForceLoaded){window.callAmGA4GTM();window.amastyGa4GtmForceLoaded=true;}});</script>
128
128<script src="https://sfdr.co/sfdr.js" async></script>
128 129<link rel="preconnect" href="https://js.klevu.com"/>
130<script type="text/javascript"> 131 var klevu_lang = 'en'; 132 var klevu_baseCurrencyCode = 'ZAR'; 133 var klevu_currentCurrencyCode = 'ZAR'; 134 135 var klevu_pubIsInUse = true; 136 var klevu_current_version = '3.6.3';</script>
vendor: 1 bytes, line 136
136
137<script type="text/javascript" src="https://js.klevu.com/core/v2/klevu.js"></script>
137 138
139<script type="text/javascript" id="klevu_jsapikeys"> 140 klevu({"recs":{"apiKey":"klevu-166791328078815785"},"analytics":{"apiKey":"klevu-166791328078815785"},"search":{"apiKey":"klevu-166791328078815785"}}); 141</script>
vendor: 1 bytes, line 141
141
142<script type="text/javascript" id="klevu_jsmodules"> 143 // Add Price Field Suffix for customer group and currency conversion 144 var klevu_addPriceSuffixToQueryControl = { 145 name: 'addPriceSuffixToQuery', 146 fire: function (data, scope) { 147 var localStorage = JSON.parse( 148 window.localStorage.getItem('klv_mage') || '{}' 149 ); 150 var customerData = localStorage.customerData || {}; 151 if ("undefined" !== typeof customerData.customer_group_id) { 152 klevu.search.modules.addPriceSuffixToQuery(data, scope, klevu_baseCurrencyCode, customerData.customer_group_id); 153 } 154 } 155 }; 156 157 (function (klevu) { 158 klevu.extend(true, klevu.search.modules, { 159 addPriceSuffixToQuery: function (data, scope, currencyCode, customerGroupId) { 160 if (typeof data.request.current === "undefined") { 161 return false; 162 } 163 164 klevu.each(data.request.current.recordQueries, function (key, query) { 165 //code to fetch prices 166 klevu.setObjectPath( 167 data, 168 "localOverrides.query." + query.id + ".settings.priceFieldSuffix", 169 currencyCode + '-' + customerGroupId 170 ); 171 }); 172 }, 173 mageConvertPriceRecordCurrencyData: function (productRecords, currencyCode, currencyRates) { 174 if (!productRecords) { 175 return; 176 } 177 178 klevu.each(productRecords, function (recordKey, productRecord) { 179 var fromRate = parseFloat(currencyRates[productRecord.currency] || 0); 180 var toRate = parseFloat(currencyRates[currencyCode] || 0); 181 if (!fromRate || !toRate) { 182 return; 183 } 184 185 var exchangeRate = toRate / fromRate; 186 187 if (klevu.isNumeric(klevu.getObjectPath(productRecord, "price"))) { 188 productRecord.price *= exchangeRate; 189 } 190 if (klevu.isNumeric(klevu.getObjectPath(productRecord, "salePrice"))) { 191 productRecord.salePrice *= exchangeRate; 192 } 193 if (klevu.isNumeric(klevu.getObjectPath(productRecord, "startPrice"))) { 194 productRecord.startPrice *= exchangeRate; 195 } 196 197 productRecord.currency = currencyCode; 198 }); 199 } 200 }); 201 })(klevu); 202</script>
vendor: 1 bytes, line 202
202
203<script type="text/javascript" src="https://js.klevu.com/recs/v2/klevu-recs.js"></script>
vendor: 1 bytes, line 203
203
204<script type="text/javascript" id="klevu_jsinteractive"> 205 klevu.interactive(function () { 206 var options = {"powerUp":{"recsModule":true},"url":{"protocol":"https:","landing":"https:\/\/www.studio-88.co.za\/search\/","search":"https:\/\/eucs31v2.ksearchnet.com\/cs\/v2\/search"},"search":{"minChars":0,"searchBoxSelector":"input[type=text][name=q],input[type=search][name=q],.kuSearchInput"}}; 207 let searchContentElement = document.getElementById("search-content"); 208if (searchContentElement) { 209 let klevuSearchResults_wrapper = document.createElement("div"); 210 klevuSearchResults_wrapper.id = "klevuSearchResults_wrapper"; 211 klevuSearchResults_wrapper.className = "klevuSearchResults_wrapper"; 212 213 let klevuSearchResults = document.createElement("div"); 214 klevuSearchResults.id = "klevuSearchResults"; 215 klevuSearchResults.className = "klevuSearchResults"; 216 217 klevuSearchResults_wrapper.appendChild(klevuSearchResults); 218 searchContentElement.appendChild(klevuSearchResults_wrapper); 219 220 options.search.searchBoxTarget = klevuSearchResults; 221} 222// Magento image path needs to be updated if pubIsInUse 223klevu.coreEvent.attach("setRemoteConfigRecsBaseUpdates", { 224 name: "klevuMagentoImage", 225 fire: function () { 226 klevu.recs.base.getScope().chains.search.control.add({ 227 name: "klevuMagentoImage", 228 fire: function (data, scope) { 229 scope.recsScope.searchObject.getScope().chains.template.process.success.add({ 230 name: "klevuMagentoImage", 231 fire: function (data, scope) { 232 if (typeof window.klevu_pubIsInUse == "undefined") { 233 window.klevu_pubIsInUse = true; 234 } 235 klevu.each(data.template.query, function (key, query) { 236 if (query.result.length > 0) { 237 klevu.each(query.result, function (key, product) { 238 if (window.klevu_pubIsInUse) { 239 product.image = product.image.replace('pub/', ''); 240 product.image = product.image.replace('needtochange/', ''); 241 } else { 242 product.image = product.image.replace('needtochange/', 'pub/'); 243 } 244 }); 245 } 246 }); 247 } 248 }); 249 } 250 }); 251 } 252}); 253 254 255 klevu(options); 256 257 }); 258</script>
vendor: 1 bytes, line 258
258
259<script type="text/javascript" src="https://js.klevu.com/theme/default/v2/quick-search-theme.lazyload.js"></script>
259 260
261<script type="text/javascript">window.dataLayer = window.dataLayer || []; 262window.getAmGa4Cookie = function () { 263 let cookies = {}; 264 document.cookie.split(';').forEach(function (el) { 265 let [key, value] = el.split('='); 266 cookies[key.trim()] = value; 267 }); 268 269 return cookies['amga4_customer'] 270 ? decodeURIComponent(cookies['amga4_customer']) 271 : '{}'; 272}; 273 274(function () { 275 /** 276 * @var {[Object]} 277 */ 278 const userProperties = JSON.parse(window.getAmGa4Cookie()); 279 280 if (userProperties.customerGroup) { 281 window.dataLayer.push({'customerGroup': userProperties.customerGroup}); 282 } 283 if (userProperties.customerId) { 284 window.dataLayer.push({'customerId': userProperties.customerId}); 285 } 286 /** 287 * @var {[Object]} 288 */ 289 const dataToPush = JSON.parse('\u005B\u005D'); 290 291 dataToPush.forEach((dataObject) => { 292 window.dataLayer.push(dataObject); 293 }); 294})(); 295</script>
295 296<!-- Google Tag Manager -->
297<script type="text/javascript">function callAmGA4GTM () { 298(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 299new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 300j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 301'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]'); 302n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f); 303})(window,document,'script','dataLayer', 'GTM-WG3RPG4Q'); 304} 305</script>
305<!-- End Google Tag Manager --> 306
306<script type="text/x-magento-init"> 307 { 308 "*": { 309 "Amasty_GA4/js/gtm-gdpr-cookie": {} 310 } 311 } 312 </script>
vendor: 1 bytes, line 312
312
313<script> 314 var BASE_URL = 'https://www.studio-88.co.za/'; 315 var THEME_PATH = 'https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US'; 316 var COOKIE_CONFIG = { 317 "expires": null, 318 "path": "\u002F", 319 "domain": ".www.studio\u002D88.co.za", 320 "secure": true, 321 "lifetime": "3600", 322 "cookie_restriction_enabled": false }; 323 var CURRENT_STORE_CODE = 'default'; 324 var CURRENT_WEBSITE_ID = '1'; 325 326 window.hyva = window.hyva || {} 327 328 window.cookie_consent_groups = window.cookie_consent_groups || {} 329 window.cookie_consent_groups['necessary'] = true; 330 331 window.cookie_consent_config = window.cookie_consent_config || {}; 332 window.cookie_consent_config['necessary'] = [].concat( 333 window.cookie_consent_config['necessary'] || [], 334 [ 335 'user_allowed_save_cookie', 336 'form_key', 337 'mage-messages', 338 'private_content_version', 339 'mage-cache-sessid', 340 'last_visited_store', 341 'section_data_ids' 342 ] 343 ); 344</script>
vendor: 1 bytes, line 344
344
345<script> 346 'use strict'; 347 (function( hyva, undefined ) { 348 349 function lifetimeToExpires(options, defaults) { 350 351 const lifetime = options.lifetime || defaults.lifetime; 352 353 if (lifetime) { 354 const date = new Date; 355 date.setTime(date.getTime() + lifetime * 1000); 356 return date; 357 } 358 359 return null; 360 } 361 362 function generateRandomString() { 363 364 const allowedCharacters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 365 length = 16; 366 367 let formKey = '', 368 charactersLength = allowedCharacters.length; 369 370 for (let i = 0; i < length; i++) { 371 formKey += allowedCharacters[Math.round(Math.random() * (charactersLength - 1))] 372 } 373 374 return formKey; 375 } 376 377 const sessionCookieMarker = {noLifetime: true} 378 379 const cookieTempStorage = {}; 380 381 const internalCookie = { 382 get(name) { 383 const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); 384 return v ? v[2] : null; 385 }, 386 set(name, value, days, skipSetDomain) { 387 let expires, 388 path, 389 domain, 390 secure, 391 samesite; 392 393 const defaultCookieConfig = { 394 expires: null, 395 path: '/', 396 domain: null, 397 secure: false, 398 lifetime: null, 399 samesite: 'lax' 400 }; 401 402 const cookieConfig = window.COOKIE_CONFIG || {}; 403 404 expires = days && days !== sessionCookieMarker 405 ? lifetimeToExpires({lifetime: 24 * 60 * 60 * days, expires: null}, defaultCookieConfig) 406 : lifetimeToExpires(window.COOKIE_CONFIG, defaultCookieConfig) || defaultCookieConfig.expires; 407 408 path = cookieConfig.path || defaultCookieConfig.path; 409 domain = !skipSetDomain && (cookieConfig.domain || defaultCookieConfig.domain); 410 secure = cookieConfig.secure || defaultCookieConfig.secure; 411 samesite = cookieConfig.samesite || defaultCookieConfig.samesite; 412 413 document.cookie = name + "=" + encodeURIComponent(value) + 414 (expires && days !== sessionCookieMarker ? '; expires=' + expires.toGMTString() : '') + 415 (path ? '; path=' + path : '') + 416 (domain ? '; domain=' + domain : '') + 417 (secure ? '; secure' : '') + 418 (samesite ? '; samesite=' + samesite : 'lax'); 419 }, 420 isWebsiteAllowedToSaveCookie() { 421 const allowedCookies = this.get('user_allowed_save_cookie'); 422 if (allowedCookies) { 423 const allowedWebsites = JSON.parse(unescape(allowedCookies)); 424 425 return allowedWebsites[CURRENT_WEBSITE_ID] === 1; 426 } 427 return false; 428 }, 429 getGroupByCookieName(name) { 430 const cookieConsentConfig = window.cookie_consent_config || {}; 431 let group = null; 432 for (let prop in cookieConsentConfig) { 433 if (!cookieConsentConfig.hasOwnProperty(prop)) continue; 434 if (cookieConsentConfig[prop].includes(name)) { 435 group = prop; 436 break; 437 } 438 } 439 return group; 440 }, 441 isCookieAllowed(name) { 442 const cookieGroup = this.getGroupByCookieName(name); 443 return cookieGroup 444 ? window.cookie_consent_groups[cookieGroup] 445 : this.isWebsiteAllowedToSaveCookie(); 446 }, 447 saveTempStorageCookies() { 448 for (const [name, data] of Object.entries(cookieTempStorage)) { 449 if (this.isCookieAllowed(name)) { 450 this.set(name, data['value'], data['days'], data['skipSetDomain']); 451 delete cookieTempStorage[name]; 452 } 453 } 454 } 455 }; 456 457 hyva.getCookie = (name) => { 458 const cookieConfig = window.COOKIE_CONFIG || {}; 459 460 if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) { 461 return cookieTempStorage[name] ? cookieTempStorage[name]['value'] : null; 462 } 463 464 return internalCookie.get(name); 465 } 466 467 hyva.setCookie = (name, value, days, skipSetDomain) => { 468 const cookieConfig = window.COOKIE_CONFIG || {}; 469 470 if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) { 471 cookieTempStorage[name] = {value, days, skipSetDomain}; 472 return; 473 } 474 return internalCookie.set(name, value, days, skipSetDomain); 475 } 476 477 478 hyva.setSessionCookie = (name, value, skipSetDomain) => { 479 return hyva.setCookie(name, value, sessionCookieMarker, skipSetDomain) 480 } 481 482 hyva.getBrowserStorage = () => { 483 const browserStorage = window.localStorage || window.sessionStorage; 484 if (!browserStorage) { 485 console.warn('Browser Storage is unavailable'); 486 return false;
487 } 488 try { 489 browserStorage.setItem('storage_test', '1'); 490 browserStorage.removeItem('storage_test'); 491 } catch (error) { 492 console.warn('Browser Storage is not accessible', error); 493 return false; 494 } 495 return browserStorage; 496 } 497 498 hyva.postForm = (postParams) => { 499 const form = document.createElement("form"); 500 501 let data = postParams.data; 502 503 if (! postParams.skipUenc && ! data.uenc) { 504 data.uenc = btoa(window.location.href); 505 } 506 form.method = "POST"; 507 form.action = postParams.action; 508 509 Object.keys(postParams.data).map(key => { 510 const field = document.createElement("input"); 511 field.type = 'hidden' 512 field.value = postParams.data[key]; 513 field.name = key; 514 form.appendChild(field); 515 }); 516 517 const form_key = document.createElement("input"); 518 form_key.type = 'hidden'; 519 form_key.value = hyva.getFormKey(); 520 form_key.name="form_key"; 521 form.appendChild(form_key); 522 523 document.body.appendChild(form); 524 525 form.submit(); 526 } 527 528 hyva.getFormKey = function () { 529 let formKey = hyva.getCookie('form_key'); 530 531 if (!formKey) { 532 formKey = generateRandomString(); 533 hyva.setCookie('form_key', formKey); 534 } 535 536 return formKey; 537 } 538 539 hyva.formatPrice = (value, showSign, options = {}) => { 540 const formatter = new Intl.NumberFormat( 541 'en\u002DUS', 542 Object.assign({ 543 style: 'currency', 544 currency: 'ZAR', 545 signDisplay: showSign ? 'always' : 'auto' 546 }, options) 547 ); 548 return (typeof Intl.NumberFormat.prototype.formatToParts === 'function') ? 549 formatter.formatToParts(value).map(({type, value}) => { 550 switch (type) { 551 case 'currency': 552 return 'R\u0020' || value; 553 case 'minusSign': 554 return '- '; 555 case 'plusSign': 556 return '+ '; 557 default : 558 return value; 559 } 560 }).reduce((string, part) => string + part) : 561 formatter.format(value); 562 } 563 564 const formatStr = function (str, nStart) { 565 const args = Array.from(arguments).slice(2); 566 567 return str.replace(/(%+)([0-9]+)/g, (m, p, n) => { 568 const idx = parseInt(n) - nStart; 569 570 if (args[idx] === null || args[idx] === void 0) { 571 return m; 572 } 573 return p.length % 2 574 ? p.slice(0, -1).replace('%%', '%') + args[idx] 575 : p.replace('%%', '%') + n; 576 }) 577 } 578 579 hyva.str = function (string) { 580 const args = Array.from(arguments); 581 args.splice(1, 0, 1); 582 583 return formatStr.apply(undefined, args); 584 } 585 586 hyva.strf = function () { 587 const args = Array.from(arguments); 588 args.splice(1, 0, 0); 589 590 return formatStr.apply(undefined, args); 591 } 592 593 /** 594 * Take a html string as `content` parameter and 595 * extract an element from the DOM to replace in 596 * the current page under the same selector, 597 * defined by `targetSelector` 598 */ 599 hyva.replaceDomElement = (targetSelector, content) => { 600 // Parse the content and extract the DOM node using the `targetSelector` 601 const parser = new DOMParser(); 602 const doc = parser.parseFromString(content, 'text/html'); 603 const contentNode = doc.querySelector(targetSelector); 604 605 // Bail if content can't be found 606 if (!contentNode) { 607 return; 608 } 609 610 hyva.activateScripts(contentNode) 611 612 // Replace the old DOM node with the new content
613 document.querySelector(targetSelector).replaceWith(contentNode); 614 615 // Reload customerSectionData and display cookie-messages if present 616 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 617 hyva.initMessages(); 618 } 619 620 hyva.activateScripts = (contentNode) => { 621 // Extract all the script tags from the content. 622 // Script tags won't execute when inserted into a dom-element directly, 623 // therefore we need to inject them to the head of the document. 624 const tmpScripts = contentNode.getElementsByTagName('script'); 625 626 if (tmpScripts.length > 0) { 627 // Push all script tags into an array 628 // (to prevent dom manipulation while iterating over dom nodes) 629 const scripts = []; 630 for (let i = 0; i < tmpScripts.length; i++) { 631 scripts.push(tmpScripts[i]); 632 } 633 634 // Iterate over all script tags and duplicate+inject each into the head 635 for (let i = 0; i < scripts.length; i++) { 636 let script = document.createElement('script'); 637 script.innerHTML = scripts[i].innerHTML; 638 639 document.head.appendChild(script); 640 641 // Remove the original (non-executing) node from the content 642 scripts[i].parentNode.removeChild(scripts[i]); 643 } 644 } 645 return contentNode; 646 } 647 648 const replace = {['+']: '-', ['/']: '_', ['=']: ','}; 649 hyva.getUenc = () => btoa(window.location.href).replace(/[+/=]/g, match => replace[match]); 650 651 let currentTrap; 652 653 const focusableElements = (rootElement) => { 654 const selector = 'button, [href], input, select, textarea, details, [tabindex]:not([tabindex="-1"]'; 655 return Array.from(rootElement.querySelectorAll(selector)) 656 .filter(el => { 657 return el.style.display !== 'none' 658 && !el.disabled 659 && el.tabIndex !== -1 660 && (el.offsetWidth || el.offsetHeight || el.getClientRects().length) 661 }) 662 } 663 664 const focusTrap = (e) => { 665 const isTabPressed = e.key === 'Tab' || e.keyCode === 9; 666 if (!isTabPressed) return; 667 668 const focusable = focusableElements(currentTrap) 669 const firstFocusableElement = focusable[0] 670 const lastFocusableElement = focusable[focusable.length - 1] 671 672 e.shiftKey 673 ? document.activeElement === firstFocusableElement && (lastFocusableElement.focus(), e.preventDefault()) 674 : document.activeElement === lastFocusableElement && (firstFocusableElement.focus(), e.preventDefault()) 675 }; 676 677 hyva.releaseFocus = (rootElement) => { 678 if (currentTrap && (!rootElement || rootElement === currentTrap)) { 679 currentTrap.removeEventListener('keydown', focusTrap) 680 currentTrap = null 681 } 682 } 683 hyva.trapFocus = (rootElement) => { 684 if (!rootElement) return; 685 hyva.releaseFocus() 686 currentTrap = rootElement 687 rootElement.addEventListener('keydown', focusTrap) 688 const firstElement = focusableElements(rootElement)[0] 689 firstElement && firstElement.focus() 690 } 691 692 hyva.alpineInitialized = (fn) => window.addEventListener('alpine:initialized', fn, {once: true}) 693 window.addEventListener('user-allowed-save-cookie', () => internalCookie.saveTempStorageCookies()) 694 695 }( window.hyva = window.hyva || {} )); 696</script>
vendor: 1 bytes, line 696
696
697<script> 698 if (!window.IntersectionObserver) { 699 window.IntersectionObserver = function (callback) { 700 this.observe = el => el && callback(this.takeRecords()); 701 this.takeRecords = () => [{isIntersecting: true, intersectionRatio: 1}]; 702 this.disconnect = () => {}; 703 this.unobserve = () => {}; 704 } 705 } 706</script>
706 707 708 <meta property="og:type" content="cms"/> 709 710 <meta property="og:title" content="Shop Sneakers, Footwear and Clothing Online | Studio 88"/> 711 712 <meta property="og:description" content="At Studio 88 we make it our business to bring you the latest in top international and local fashion brands. Shop Online - Free shipping for orders over R800. Free Click & Collect over R500."/> 713 714 <meta property="og:url" content="https://www.studio-88.co.za/"/> 715 <meta property="og:site_name" content="Main Website"/>
716<script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"WebPage","@id":"https://www.studio-88.co.za/speed-test#webpage","name":"Studio 88 - Style On The Move"},{"@type":"ImageObject","contentUrl":"https://www.studio-88.co.za/media/slider/Mobile_25years.webp","caption":"Studio 88 x 25 years","mainEntityOfPage":{"@id":"https://www.studio-88.co.za/speed-test#webpage"},"copyrightHolder":{"@type":"Organization","name":"Studio 88"},"creator":{"@type":"Organization","name":"Studio 88"},"creditText":"Studio 88","representativeOfPage":true},{"@type":"ImageObject","contentUrl":"https://www.studio-88.co.za/media/slider/Puma_Karmen_II_Idol_Mobile.webp","caption":"Puma Karmen II Idol Sneakers","mainEntityOfPage":{"@id":"https://www.studio-88.co.za/speed-test#webpage"},"copyrightHolder":{"@type":"Organization","name":"Studio 88"},"creator":{"@type":"Organization","name":"Studio 88"},"creditText":"Studio 88"},{"@type":"ImageObject","contentUrl":"https://www.studio-88.co.za/media/slider/Mobile_Air_Force.webp","caption":"Nike Air Force Sneakers","mainEntityOfPage":{"@id":"https://www.studio-88.co.za/speed-test#webpage"},"copyrightHolder":{"@type":"Organization","name":"Studio 88"},"creator":{"@type":"Organization","name":"Studio 88"},"creditText":"Studio 88"},{"@type":"ImageObject","contentUrl":"https://www.studio-88.co.za/media/slider/Mobile_adidasF50Messi.webp","caption":"adidas F50 Soccer Boots","mainEntityOfPage":{"@id":"https://www.studio-88.co.za/speed-test#webpage"},"copyrightHolder":{"@type":"Organization","name":"Studio 88"},"creator":{"@type":"Organization","name":"Studio 88"},"creditText":"Studio 88"},{"@type":"ImageObject","contentUrl":"https://www.studio-88.co.za/media/brands/adidasoriginals.webp","caption":"adidas original","mainEntityOfPage":{"@id":"https://www.studio-88.co.za/speed-test#webpage"},"copyrightHolder":{"@type":"Organization","name":"Studio 88"},"creator":{"@type":"Organization","name":"Studio 88"},"creditText":"Studio 88"}]}</script>
vendor: 1 bytes, line 716
716
717<script type="text/javascript"> 718 var nowUnixtime = parseInt(Date.now() / 1000); 719 720 function klevufejs_getCookie(name) { 721 if (typeof (name) === "undefined") { 722 name = "klv_mage"; 723 } 724 var c = "", 725 ca = document.cookie.split(';'); 726 727 for (var i = 0; i < ca.length; i++) { 728 c = ca[i]; 729 if (typeof c !== "string") { 730 continue; 731 } 732 var cookiePair = c.split("="); 733 734 if (name === cookiePair[0].trim()) { 735 try { 736 return JSON.parse(decodeURIComponent(cookiePair[1])); 737 } catch (err) { 738 // this is fine, data will be regenerated 739 } 740 } 741 } 742 return { 743 expire_sections: {} 744 }; 745 } 746 747 document.addEventListener('klevu.customerData.loaded', function (e) { 748 var klevufejs_cookie = klevufejs_getCookie(); 749 klevufejs_cookie.expire_sections.customerData = nowUnixtime + 600; 750 751 document.cookie = "klv_mage=" + JSON.stringify(klevufejs_cookie) 752 + "; expires=" + new Date((nowUnixtime + 3600) * 1000).toUTCString() 753 + "; path=/" 754 + "; samesite=strict"; 755 }); 756 757 var klevufejs_cookie = klevufejs_getCookie(); 758 var klevuData = { 759 ...{ 760 customerData: { 761 revalidate_after: -1 762 } 763 }, 764 ...JSON.parse(window.localStorage.getItem('klv_mage') || '{}') 765 }; 766 767 const klevuCustomerDataLoadedEvent = document.createEvent('CustomEvent'); 768 klevuCustomerDataLoadedEvent.initEvent('klevu.customerData.loaded', false, true); 769 const klevuCustomerDataLoadErrorEvent = document.createEvent('CustomEvent'); 770 klevuCustomerDataLoadErrorEvent.initEvent('klevu.customerData.loadError', false, true); 771 772 if (typeof klevufejs_cookie.expire_sections !== "object" 773 || (klevufejs_cookie.expire_sections.customerData || -1) < nowUnixtime 774 || klevuData.customerData.revalidate_after < nowUnixtime 775 ) { 776 var xhttp = new XMLHttpRequest(); 777 xhttp.onerror = function (request) { 778 document.dispatchEvent(klevuCustomerDataLoadErrorEvent); 779 }; 780 xhttp.ontimeout = function (request) { 781 this.onerror(request); 782 }; 783 xhttp.onload = function (request) { 784 if (this.status >= 400 || this.timeout) { 785 this.onerror(request); 786 return; 787 } 788 var klevuData = JSON.parse(window.localStorage.getItem('klv_mage') || '{}'); 789 klevuData.customerData = JSON.parse(this.response); 790 window.localStorage.setItem('klv_mage', JSON.stringify(klevuData)); 791 document.dispatchEvent(klevuCustomerDataLoadedEvent); 792 }; 793 xhttp.open('GET', 'https://www.studio-88.co.za/rest/V1/klevu/customerData', false); 794 xhttp.send(); 795 } else { 796 document.dispatchEvent(klevuCustomerDataLoadedEvent); 797 } 798</script>
798</head> 799<body id="html-body" class="cms-speed-test page-layout-1column cms-index-index page-layout-cms-full-width">
800<script> 801 'use strict'; 802 803 (function () { 804 window.fbqCompatiilityGdprCookie = { 805 allowedCookies: null, 806 disallowedCookies: null, 807 isAllowedToRunScript: null, 808 facebookPixelCookieName: '_fbp', 809 listeners: [ 810 "cookiebar-action-accept-finish", 811 "cookiebar-action-allow-finish" 812 ], 813 814 /** 815 * @public 816 * @params {Object} config 817 * @returns {void} 818 */ 819 init(config) { 820 this.listeners.forEach(listener => { 821 window.addEventListener(listener, () => 822 this.sendFbq(config), 823 {once:true} 824 ); 825 }); 826 827 this.sendFbq(config); 828 }, 829 830 /** 831 * @private 832 * @params {Object} config 833 * @returns {void} 834 */ 835 sendFbq: function (config) { 836 if (config.__sent) { 837 return; 838 } 839 840 this.disallowedCookies = hyva.getCookie('amcookie_disallowed') || ''; 841 this.allowedCookies = hyva.getCookie('amcookie_allowed') || ''; 842 this.isAllowedToRunScript = !this.disallowedCookies 843 || this.disallowedCookies.indexOf(this.facebookPixelCookieName) === -1; 844 845 if (this.isAllowedToRunScript) { 846 config.__sent = true; 847 fbq(config.callMethod, config.arguments, config.advancedInfo ?? {}); 848 } 849 } 850 } 851 })(); 852</script>
852 853
853<script> 854 !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? 855 n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; 856 n.push=n;n.loaded=!0;n.version='2.0';n.queue=[]; 857 t=b.createElement(e);t.async=!0;t.src=v;s=b.getElementsByTagName(e)[0]; 858 s.parentNode.insertBefore(t,s)}(window, 859 document,'script','https://connect.facebook.net/en_US/fbevents.js'); 860 </script>
860 861 <!-- Facebook Pixel Code --> 862 <noscript> 863 <img height="1" width="1" style="display:none" alt="Facebook Pixel" 864 src="https://www.facebook.com/tr?id=1046223977155954&ev=PageView&noscript=1" 865 /> 866 </noscript> 867 <!-- End Facebook Pixel Code --> 868
868<script> 869 window.fbqCompatiilityGdprCookie.init({ 870 "callMethod": "init", 871 "arguments": "1046223977155954", 872 "advancedInfo": {} }) 873 </script>
873 874
874<script> 875 window.fbqCompatiilityGdprCookie.init({ 876 "callMethod": "track", 877 "arguments": "PageView" 878 }) 879 </script>
879 880 881<!-- Google Tag Manager (noscript) -->
vendor: 73 bytes, lines 881-883
881 882<noscript> 883 <iframe src="https://www.googletagmanager.com/ns.html?id=
883GTM-WG3RPG4Q
vendor: 96 bytes, lines 883-885
883" 884 height="0" width="0" style="display:none;visibility:hidden"></iframe> 885</noscript>
886<!-- End Google Tag Manager (noscript) --> 887<input name="form_key" type="hidden" value="sJl1XfJwhO9mLDNr" /> 888 <noscript> 889 <section class="message global noscript border-b-2 border-blue-500 bg-blue-50 shadow-none m-0 px-0 rounded-none font-normal"> 890 <div class="container text-center"> 891 <p> 892 <strong>JavaScript seems to be disabled in your browser.</strong> 893 <span> 894 For the best experience on our site, be sure to turn on Javascript in your browser. </span> 895 </p> 896 </div> 897 </section> 898 </noscript> 899 900
901<script> 902 document.body.addEventListener('touchstart', () => {}, {passive: true}) 903</script>
903 904<div class="widget block block-static-block"> 905 </div> 906<div class="widget block block-static-block"> 907 </div> 908<div class="widget block block-static-block"> 909 </div> 910
911<script> 912 (function (hyva) { 913 'use strict'; 914 915 /** 916 * We use multiple Store objects depending on which Alpine version is 917 */ 918 const isAlpineV3 = true; 919 920 /** 921 * @returns {string} 922 */ 923 function getInitEventName() { 924 return isAlpineV3 ? 'alpine:init' : 'spruce:init'; 925 } 926 927 /** 928 * @returns {Object} 929 */ 930 function getStoreObject() { 931 return isAlpineV3 ? Alpine : Spruce; 932 } 933 934 hyva.amStorage = { 935 /** 936 * @param {string} key 937 * @param {Object} object 938 * @returns {void} 939 */ 940 create: (key, object) => { 941 const eventName = key.toLowerCase() + '-created'; 942 943 document.addEventListener(getInitEventName(), () => { 944 getStoreObject().store(key, object); 945 document.dispatchEvent(new Event(eventName)); 946 }); 947 }, 948 949 /** 950 * @param {string} key 951 * @returns {Object} 952 */ 953 getStorage: (key) => { 954 return getStoreObject().store(key); 955 }, 956 957 /** 958 * @param {string} key 959 * @param {Object} object 960 * @returns {void} 961 */ 962 reInit: (key, object) => { 963 isAlpineV3 964 ? getStoreObject().store(key, object) 965 : getStoreObject().reset(key, object); 966 } 967 }; 968 969 970 }(window.hyva = window.hyva || {})); 971</script>
vendor: 1 bytes, line 971
971
972<script> 973 window.amastyCookieManager = window.amastyCookieManager || {}; 974</script>
974 975<div class="page-wrapper"><header class="page-header dark"><a class="action skip sr-only focus:not-sr-only focus:absolute focus:z-40 focus:bg-white 976 contentarea" 977 href="#contentarea"> 978 <span> 979 Skip to Content </span> 980</a> 981<div data-bind="scope: 'amasty-shipbar-top'"> 982 <!-- ko template: getTemplate() --><!-- /ko --> 983
983<script type="text/x-magento-init"> 984 { 985 "*": { 986 "Magento_Ui/js/core/app": {"components":{"amasty-shipbar-top":{"component":"Amasty_ShippingBar\/js\/bar","currencySymbol":"R "}}} } 987 } 988 </script>
988 989</div>
990<script> 991 function initHeader () { 992 return { 993 searchOpen: false, 994 cart: {}, 995 isCartOpen: false, 996 getData(data) { 997 if (data.cart) { this.cart = data.cart } 998 }, 999 isCartEmpty() { 1000 return !this.cart.summary_count 1001 }, 1002 toggleCart(event) { 1003 if (event.detail && event.detail.isOpen !== undefined) { 1004 this.isCartOpen = event.detail.isOpen 1005 if (!this.isCartOpen && this.$refs && this.$refs.cartButton) { 1006 this.$refs.cartButton.focus() 1007 } 1008 } else { 1009 this.isCartOpen = true 1010 } 1011 } 1012 } 1013 } 1014 function initCompareHeader() { 1015 return { 1016 compareProducts: null, 1017 itemCount: 0, 1018 receiveCompareData(data) { 1019 if (data['compare-products']) { 1020 this.compareProducts = data['compare-products']; 1021 this.itemCount = this.compareProducts.count; 1022 } 1023 } 1024 } 1025 } 1026</script>
1026 1027<div id="header-top" class="relative bg-black border-b-2 border-white"> 1028 <!--Header Top--> 1029 <div class="flex flex-wrap lg:flex-nowrap justify-between px-1 md:px-6 py-1"> 1030 <div class="w-full lg:w-1/2 text-center lg:text-left my-1"> 1031 1032 <p class="text-gray-300 m-0"><style>.cmsb441-text-white { 1033--tw-text-opacity: 1; 1034color: rgb(255 255 255 / var(--tw-text-opacity)) 1035}</style> 1036<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><span class="cmsb441-text-white">Free shipping for orders over R800. Free Click & Collect over R500 | Delivery within 2-5 working days.</span></div></p> 1037 </div> 1038 <div class="w-full lg:w-1/2 hidden lg:block text-left text-center lg:text-right"> 1039 <ul id="topBar" class="p-0 justify-self-stretch lg:justify-self-end my-1"> 1040 <li class="pe-2"><a href="https://www.studio-88.co.za/customer/account/create" class="text-white hover:text-primary">CREATE ACCOUNT</a></li> 1041 <li class="pe-2"><a href="https://www.studio-88.co.za/customer/account" class="text-white hover:text-primary">SIGN IN</a></li> 1042 <li class="pe-2"><a href="https://www.studio-88.co.za/store-locator" class="text-white hover:text-primary">STORE LOCATOR</a></li> 1043 <li><a href="https://www.studio-88.co.za/tracking" class="text-white hover:text-primary">TRACK PARCEL</a></li> 1044 </ul> 1045 </div> 1046 </div> 1047</div> 1048<div id="header" 1049 class="flex header-scroll relative z-30 w-full border-b border-black bg-black" 1050 x-data="initHeader()" 1051 @private-content-loaded.window="getData(event.detail.data)" 1052> 1053 <div class="flex lg:hidden flex-nowrap w-auto items-center justify-start py-1 pl-4"> 1054 <!--Logo--> 1055 <div class="order-1 w-max pb-2 lg:w-auto sm:pb-0"> 1056 <a 1057 class="flex items-center justify-center text-xl font-medium tracking-wide text-gray-800 1058 no-underline hover:no-underline font-title" 1059 href="https://www.studio-88.co.za/" 1060 aria-label="Studio88 Home Page" 1061 > 1062 <img 1063 src="https://www.studio-88.co.za/media/logo/stores/1/Studio_88_Logo_New.png" 1064 alt="Studio 88" 1065 width="115" height="63" /> 1066 </a> 1067</div> 1068 </div> 1069 1070 <div class="flex flex-nowrap items-center justify-end lg:justify-between w-full pr-4 lg:px-6 mx-auto mt-0"> 1071 1072 <div class="hidden lg:flex"> 1073 <!--Logo--> 1074 <div class="order-1 w-max pb-2 lg:w-auto sm:pb-0"> 1075 <a 1076 class="flex items-center justify-center text-xl font-medium tracking-wide text-gray-800 1077 no-underline hover:no-underline font-title" 1078 href="https://www.studio-88.co.za/" 1079 aria-label="Studio88 Home Page" 1080 > 1081 <img 1082 src="https://www.studio-88.co.za/media/logo/stores/1/Studio_88_Logo_New.png" 1083 alt="Studio 88" 1084 width="115" height="63" /> 1085 </a> 1086</div> 1087 </div> 1088 1089 <!--Main Navigation--> 1090 <nav 1091 x-data="initMenuMobile_6ab51b0cbe9fe()" 1092 @load.window="setActiveMenu($root)" 1093 @keydown.window.escape="closeMenu()" 1094 class="z-20 color-dark text-white order-2 navigation lg:hidden w-12 h-12" 1095 aria-label="Site navigation" 1096 role="navigation" 1097> 1098 <!-- mobile --> 1099 <button 1100 x-ref="mobileMenuTrigger" 1101 @click="openMenu()" 1102 :class="{'overflow-x-hidden overflow-y-auto fixed top-0 left-0 w-full text-black' : open}" 1103 type="button" 1104 aria-label="Open menu" 1105 aria-haspopup="menu" 1106 :aria-expanded="open" 1107 :hidden="open" 1108 > 1109 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="p-3" width="48" height="48" :class="{ 'hidden' : open, 'block': !open }" aria-hidden="true"> 1110 <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/> 1111</svg> 1112 </button> 1113 <div x-ref="mobileMenuNavLinks" class="fixed top-0 right-0 w-full h-full p-1 hidden flex-col border-t border-container bg-container-lighter overflow-y-auto overflow-x-hidden":class="{ 'flex': open, 'hidden': !open }":aria-hidden="open ? 'false' : 'true'" role="dialog" aria-modal="true"> 1114 <ul 1115 class="border-t flex flex-col gap-y-1 mt-16" 1116 aria-label="Site navigation links" 1117 > 1118 <li data-child-id="category-node-709-main" class="level-0 text-black"> 1119 <div class="flex items-center transition-transform duration-150 ease-in-out transform":class="{'-translate-x-full' : mobilePanelActiveId,'translate-x-0' : !mobilePanelActiveId}"> 1120 <a class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0" href="https://www.studio-88.co.za/new-arrivals" title="New Arrivals"> 1121 New Arrivals </a> 1122 <button 1123 @click="openSubcategory('category-node-709')" 1124 class="absolute right-0 flex items-center justify-center w-8 h-8 mr-8 cursor-pointer 1125 bg-container-lighter border-container" 1126 aria-label="Open New Arrivals subcategories" 1127 aria-haspopup="true" 1128 :aria-expanded="mobilePanelActiveId === 'category-node-709'" 1129 > 1130 <div class="w-8 h-8 border rounded"> 1131 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-full h-full p-1" width="24" height="24" aria-hidden="true"> 1132 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/> 1133</svg> 1134 </div> 1135 </button> 1136 </div> 1137 <div 1138 data-child-id="category-node-709" 1139 class="absolute top-0 right-0 z-10 flex flex-col gap-1 w-full h-full p-1 bg-container-lighter" 1140 :class="{'hidden': mobilePanelActiveId !== 'category-node-709'}"> 1141 <ul 1142 class="mt-16 transition-transform duration-200 ease-in-out translate-x-full transform" 1143 :class="{ 1144 'translate-x-full' : mobilePanelActiveId !== 'category-node-709', 1145 'translate-x-0' : mobilePanelActiveId === 'category-node-709', 1146 }" 1147 aria-label="Subcategories" 1148 > 1149 <li> 1150 <button 1151 type="button" 1152 class="flex items-center px-8 py-3 border-b cursor-pointer bg-container border-container w-full border-t" 1153 @click="backToMainCategories('category-node-709-main')" 1154 aria-label="Back to main categories" 1155 > 1156 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 1157 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/> 1158</svg>
1159 <span class="ml-4"> 1160 New Arrivals </span> 1161 </button> 1162 </li> 1163 <li> 1164 <a 1165 href="https://www.studio-88.co.za/new-arrivals" 1166 title="New Arrivals" 1167 class="flex items-center w-full px-8 py-3 border-b cursor-pointer 1168 bg-container-lighter border-container 1169 " 1170 > 1171 <span class="ml-10"> 1172 View All </span> 1173 </a> 1174 </li> 1175 <li class="font-bold"> 1176 <a 1177 href="https://www.studio-88.co.za/new-arrivals/new-arrivals" 1178 title="New Arrivals" 1179 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1180 bg-container-lighter border-container 1181 " 1182 > 1183 <span class="ml-10 text-base text-gray-700"> 1184 New Arrivals </span> 1185 </a> 1186 </li> 1187 <ul class="px-4"> 1188 <li> 1189 <a 1190 href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-footwear" 1191 title="New Footwear" 1192 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1193 bg-container-lighter border-container 1194 " 1195 > 1196 <span class="ml-10 text-base text-gray-700"> 1197 New Footwear </span> 1198 </a> 1199 </li> 1200 <li> 1201 <a 1202 href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-in-men" 1203 title="New In Men" 1204 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1205 bg-container-lighter border-container 1206 " 1207 > 1208 <span class="ml-10 text-base text-gray-700"> 1209 New In Men </span> 1210 </a> 1211 </li> 1212 <li> 1213 <a 1214 href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-in-women" 1215 title="New In Women" 1216 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1217 bg-container-lighter border-container 1218 " 1219 >
1220 <span class="ml-10 text-base text-gray-700"> 1221 New In Women </span> 1222 </a> 1223 </li> 1224 <li> 1225 <a 1226 href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-in-kids" 1227 title="New In Kids" 1228 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1229 bg-container-lighter border-container 1230 " 1231 > 1232 <span class="ml-10 text-base text-gray-700"> 1233 New In Kids </span> 1234 </a> 1235 </li> 1236 <li> 1237 <a 1238 href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-in-soccer" 1239 title="New In Soccer" 1240 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1241 bg-container-lighter border-container 1242 " 1243 > 1244 <span class="ml-10 text-base text-gray-700"> 1245 New In Soccer </span> 1246 </a> 1247 </li> 1248 </ul> 1249 </ul> 1250 <button 1251 @click="closeMenu()" 1252 class="absolute flex justify-end text-black w-16 self-end mb-1 transition-none" 1253 aria-label="Close menu" 1254 > 1255 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="hidden p-4" width="64" height="64" :class="{ 'hidden' : !open, 'block': open }" aria-hidden="true"> 1256 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1257</svg> 1258 </button> 1259 </div> 1260 </li> 1261 <li data-child-id="category-node-3-main" class="level-0 text-black"> 1262 <div class="flex items-center transition-transform duration-150 ease-in-out transform":class="{'-translate-x-full' : mobilePanelActiveId,'translate-x-0' : !mobilePanelActiveId}"> 1263 <a class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0" href="https://www.studio-88.co.za/men" title="Men"> 1264 Men </a> 1265 <button 1266 @click="openSubcategory('category-node-3')" 1267 class="absolute right-0 flex items-center justify-center w-8 h-8 mr-8 cursor-pointer 1268 bg-container-lighter border-container" 1269 aria-label="Open Men subcategories" 1270 aria-haspopup="true" 1271 :aria-expanded="mobilePanelActiveId === 'category-node-3'" 1272 > 1273 <div class="w-8 h-8 border rounded"> 1274 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-full h-full p-1" width="24" height="24" aria-hidden="true"> 1275 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/> 1276</svg> 1277 </div> 1278 </button> 1279 </div> 1280 <div 1281 data-child-id="category-node-3" 1282 class="absolute top-0 right-0 z-10 flex flex-col gap-1 w-full h-full p-1 bg-container-lighter" 1283 :class="{'hidden': mobilePanelActiveId !== 'category-node-3'}"> 1284 <ul 1285 class="mt-16 transition-transform duration-200 ease-in-out translate-x-full transform" 1286 :class="{ 1287 'translate-x-full' : mobilePanelActiveId !== 'category-node-3', 1288 'translate-x-0' : mobilePanelActiveId === 'category-node-3', 1289 }" 1290 aria-label="Subcategories" 1291 > 1292 <li> 1293 <button 1294 type="button" 1295 class="flex items-center px-8 py-3 border-b cursor-pointer bg-container border-container w-full border-t" 1296 @click="backToMainCategories('category-node-3-main')" 1297 aria-label="Back to main categories" 1298 > 1299 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 1300 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/> 1301</svg>
1302 <span class="ml-4"> 1303 Men </span> 1304 </button> 1305 </li> 1306 <li> 1307 <a 1308 href="https://www.studio-88.co.za/men" 1309 title="Men" 1310 class="flex items-center w-full px-8 py-3 border-b cursor-pointer 1311 bg-container-lighter border-container 1312 " 1313 > 1314 <span class="ml-10"> 1315 View All </span> 1316 </a> 1317 </li> 1318 <li class="font-bold"> 1319 <a 1320 href="https://www.studio-88.co.za/men/footwear" 1321 title="Footwear" 1322 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1323 bg-container-lighter border-container 1324 " 1325 > 1326 <span class="ml-10 text-base text-gray-700"> 1327 Footwear </span> 1328 </a> 1329 </li> 1330 <ul class="px-4"> 1331 <li> 1332 <a 1333 href="https://www.studio-88.co.za/men/footwear/sneakers" 1334 title="Sneakers" 1335 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1336 bg-container-lighter border-container 1337 " 1338 > 1339 <span class="ml-10 text-base text-gray-700"> 1340 Sneakers </span> 1341 </a> 1342 </li> 1343 <li> 1344 <a 1345 href="https://www.studio-88.co.za/men/footwear/sandals-slides" 1346 title="Sandals/Slides" 1347 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1348 bg-container-lighter border-container 1349 " 1350 > 1351 <span class="ml-10 text-base text-gray-700"> 1352 Sandals/Slides </span> 1353 </a> 1354 </li> 1355 </ul> 1356 <li class="font-bold"> 1357 <a 1358 href="https://www.studio-88.co.za/men/clothing" 1359 title="Clothing" 1360 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1361 bg-container-lighter border-container 1362 " 1363 >
1364 <span class="ml-10 text-base text-gray-700"> 1365 Clothing </span> 1366 </a> 1367 </li> 1368 <ul class="px-4"> 1369 <li> 1370 <a 1371 href="https://www.studio-88.co.za/men/clothing/golfers" 1372 title="Golfers" 1373 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1374 bg-container-lighter border-container 1375 " 1376 > 1377 <span class="ml-10 text-base text-gray-700"> 1378 Golfers </span> 1379 </a> 1380 </li> 1381 <li> 1382 <a 1383 href="https://www.studio-88.co.za/men/clothing/jackets" 1384 title="Jackets" 1385 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1386 bg-container-lighter border-container 1387 " 1388 > 1389 <span class="ml-10 text-base text-gray-700"> 1390 Jackets </span> 1391 </a> 1392 </li> 1393 <li> 1394 <a 1395 href="https://www.studio-88.co.za/men/clothing/jeans" 1396 title="Jeans" 1397 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1398 bg-container-lighter border-container 1399 " 1400 > 1401 <span class="ml-10 text-base text-gray-700"> 1402 Jeans </span> 1403 </a> 1404 </li> 1405 <li> 1406 <a 1407 href="https://www.studio-88.co.za/men/clothing/pants" 1408 title="Pants" 1409 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1410 bg-container-lighter border-container 1411 " 1412 >
1413 <span class="ml-10 text-base text-gray-700"> 1414 Pants </span> 1415 </a> 1416 </li> 1417 <li> 1418 <a 1419 href="https://www.studio-88.co.za/men/clothing/shorts" 1420 title="Shorts" 1421 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1422 bg-container-lighter border-container 1423 " 1424 > 1425 <span class="ml-10 text-base text-gray-700"> 1426 Shorts </span> 1427 </a> 1428 </li> 1429 <li> 1430 <a 1431 href="https://www.studio-88.co.za/men/clothing/t-shirts" 1432 title="T-shirts" 1433 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1434 bg-container-lighter border-container 1435 " 1436 > 1437 <span class="ml-10 text-base text-gray-700"> 1438 T-shirts </span> 1439 </a> 1440 </li> 1441 <li> 1442 <a 1443 href="https://www.studio-88.co.za/men/clothing/tops" 1444 title="Tops" 1445 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1446 bg-container-lighter border-container 1447 " 1448 > 1449 <span class="ml-10 text-base text-gray-700"> 1450 Tops </span> 1451 </a> 1452 </li> 1453 <li> 1454 <a 1455 href="https://www.studio-88.co.za/men/clothing/tracksuits" 1456 title="Tracksuits" 1457 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1458 bg-container-lighter border-container 1459 " 1460 >
1461 <span class="ml-10 text-base text-gray-700"> 1462 Tracksuits </span> 1463 </a> 1464 </li> 1465 </ul> 1466 <li class="font-bold"> 1467 <a 1468 href="https://www.studio-88.co.za/men/accessories" 1469 title="Accessories" 1470 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1471 bg-container-lighter border-container 1472 " 1473 > 1474 <span class="ml-10 text-base text-gray-700"> 1475 Accessories </span> 1476 </a> 1477 </li> 1478 <ul class="px-4"> 1479 <li> 1480 <a 1481 href="https://www.studio-88.co.za/men/accessories/bags" 1482 title="Bags" 1483 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1484 bg-container-lighter border-container 1485 " 1486 > 1487 <span class="ml-10 text-base text-gray-700"> 1488 Bags </span> 1489 </a> 1490 </li> 1491 <li> 1492 <a 1493 href="https://www.studio-88.co.za/men/accessories/headwear" 1494 title="Headwear" 1495 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1496 bg-container-lighter border-container 1497 " 1498 > 1499 <span class="ml-10 text-base text-gray-700"> 1500 Headwear </span> 1501 </a> 1502 </li> 1503 <li> 1504 <a 1505 href="https://www.studio-88.co.za/men/accessories/shoe-care" 1506 title="Shoe Care" 1507 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1508 bg-container-lighter border-container 1509 " 1510 >
1511 <span class="ml-10 text-base text-gray-700"> 1512 Shoe Care </span> 1513 </a> 1514 </li> 1515 <li> 1516 <a 1517 href="https://www.studio-88.co.za/men/accessories/socks" 1518 title="Socks" 1519 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1520 bg-container-lighter border-container 1521 " 1522 > 1523 <span class="ml-10 text-base text-gray-700"> 1524 Socks </span> 1525 </a> 1526 </li> 1527 <li> 1528 <a 1529 href="https://www.studio-88.co.za/men/accessories/underwear" 1530 title="Underwear" 1531 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1532 bg-container-lighter border-container 1533 " 1534 > 1535 <span class="ml-10 text-base text-gray-700"> 1536 Underwear </span> 1537 </a> 1538 </li> 1539 <li> 1540 <a 1541 href="https://www.studio-88.co.za/men/accessories/sandals-slides" 1542 title="Sandals/Slides" 1543 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1544 bg-container-lighter border-container 1545 " 1546 > 1547 <span class="ml-10 text-base text-gray-700"> 1548 Sandals/Slides </span> 1549 </a> 1550 </li> 1551 <li> 1552 <a 1553 href="https://www.studio-88.co.za/men/accessories/jewellery" 1554 title="Jewellery" 1555 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1556 bg-container-lighter border-container 1557 " 1558 >
1559 <span class="ml-10 text-base text-gray-700"> 1560 Jewellery </span> 1561 </a> 1562 </li> 1563 <li> 1564 <a 1565 href="https://www.studio-88.co.za/men/accessories/other" 1566 title="Other" 1567 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1568 bg-container-lighter border-container 1569 " 1570 > 1571 <span class="ml-10 text-base text-gray-700"> 1572 Other </span> 1573 </a> 1574 </li> 1575 </ul> 1576 <li class="font-bold"> 1577 <a 1578 href="https://www.studio-88.co.za/men/soccer" 1579 title="SOCCER" 1580 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1581 bg-container-lighter border-container 1582 " 1583 > 1584 <span class="ml-10 text-base text-gray-700"> 1585 SOCCER </span> 1586 </a> 1587 </li> 1588 <ul class="px-4"> 1589 <li> 1590 <a 1591 href="https://www.studio-88.co.za/men/soccer/jerseys" 1592 title="Jerseys" 1593 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1594 bg-container-lighter border-container 1595 " 1596 > 1597 <span class="ml-10 text-base text-gray-700"> 1598 Jerseys </span> 1599 </a> 1600 </li> 1601 <li> 1602 <a 1603 href="https://www.studio-88.co.za/men/soccer/boots" 1604 title="Boots" 1605 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1606 bg-container-lighter border-container 1607 " 1608 >
1609 <span class="ml-10 text-base text-gray-700"> 1610 Boots </span> 1611 </a> 1612 </li> 1613 <li> 1614 <a 1615 href="https://www.studio-88.co.za/men/soccer/sandals-slides" 1616 title="Sandals/Slides" 1617 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1618 bg-container-lighter border-container 1619 " 1620 > 1621 <span class="ml-10 text-base text-gray-700"> 1622 Sandals/Slides </span> 1623 </a> 1624 </li> 1625 <li> 1626 <a 1627 href="https://www.studio-88.co.za/men/soccer/pants" 1628 title="Pants" 1629 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1630 bg-container-lighter border-container 1631 " 1632 > 1633 <span class="ml-10 text-base text-gray-700"> 1634 Pants </span> 1635 </a> 1636 </li> 1637 <li> 1638 <a 1639 href="https://www.studio-88.co.za/men/soccer/t-shirts" 1640 title="T-Shirts" 1641 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1642 bg-container-lighter border-container 1643 " 1644 > 1645 <span class="ml-10 text-base text-gray-700"> 1646 T-Shirts </span> 1647 </a> 1648 </li> 1649 <li> 1650 <a 1651 href="https://www.studio-88.co.za/men/soccer/jackets" 1652 title="Jackets" 1653 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1654 bg-container-lighter border-container 1655 " 1656 >
1657 <span class="ml-10 text-base text-gray-700"> 1658 Jackets </span> 1659 </a> 1660 </li> 1661 <li> 1662 <a 1663 href="https://www.studio-88.co.za/men/soccer/golfers" 1664 title="Golfers" 1665 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1666 bg-container-lighter border-container 1667 " 1668 > 1669 <span class="ml-10 text-base text-gray-700"> 1670 Golfers </span> 1671 </a> 1672 </li> 1673 <li> 1674 <a 1675 href="https://www.studio-88.co.za/men/soccer/headwear" 1676 title="Headwear" 1677 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1678 bg-container-lighter border-container 1679 " 1680 > 1681 <span class="ml-10 text-base text-gray-700"> 1682 Headwear </span> 1683 </a> 1684 </li> 1685 <li> 1686 <a 1687 href="https://www.studio-88.co.za/men/soccer/shorts" 1688 title="Shorts" 1689 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1690 bg-container-lighter border-container 1691 " 1692 > 1693 <span class="ml-10 text-base text-gray-700"> 1694 Shorts </span> 1695 </a> 1696 </li> 1697 <li> 1698 <a 1699 href="https://www.studio-88.co.za/men/soccer/tops" 1700 title="Tops" 1701 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1702 bg-container-lighter border-container 1703 " 1704 >
1705 <span class="ml-10 text-base text-gray-700"> 1706 Tops </span> 1707 </a> 1708 </li> 1709 </ul> 1710 <li class="font-bold"> 1711 <a 1712 href="https://www.studio-88.co.za/men/sale" 1713 title="SALE" 1714 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1715 bg-container-lighter border-container 1716 " 1717 > 1718 <span class="ml-10 text-base text-gray-700"> 1719 SALE </span> 1720 </a> 1721 </li> 1722 <li class="font-bold"> 1723 <a 1724 href="https://www.studio-88.co.za/men/exclusive" 1725 title="EXCLUSIVE" 1726 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1727 bg-container-lighter border-container 1728 " 1729 > 1730 <span class="ml-10 text-base text-gray-700"> 1731 EXCLUSIVE </span> 1732 </a> 1733 </li> 1734 </ul> 1735 <button 1736 @click="closeMenu()" 1737 class="absolute flex justify-end text-black w-16 self-end mb-1 transition-none" 1738 aria-label="Close menu" 1739 > 1740 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="hidden p-4" width="64" height="64" :class="{ 'hidden' : !open, 'block': open }" aria-hidden="true"> 1741 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1742</svg> 1743 </button> 1744 </div> 1745 </li> 1746 <li data-child-id="category-node-4-main" class="level-0 text-black"> 1747 <div class="flex items-center transition-transform duration-150 ease-in-out transform":class="{'-translate-x-full' : mobilePanelActiveId,'translate-x-0' : !mobilePanelActiveId}"> 1748 <a class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0" href="https://www.studio-88.co.za/women" title="Women"> 1749 Women </a> 1750 <button 1751 @click="openSubcategory('category-node-4')" 1752 class="absolute right-0 flex items-center justify-center w-8 h-8 mr-8 cursor-pointer 1753 bg-container-lighter border-container" 1754 aria-label="Open Women subcategories" 1755 aria-haspopup="true" 1756 :aria-expanded="mobilePanelActiveId === 'category-node-4'" 1757 > 1758 <div class="w-8 h-8 border rounded"> 1759 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-full h-full p-1" width="24" height="24" aria-hidden="true"> 1760 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/> 1761</svg> 1762 </div> 1763 </button> 1764 </div> 1765 <div 1766 data-child-id="category-node-4" 1767 class="absolute top-0 right-0 z-10 flex flex-col gap-1 w-full h-full p-1 bg-container-lighter" 1768 :class="{'hidden': mobilePanelActiveId !== 'category-node-4'}"> 1769 <ul 1770 class="mt-16 transition-transform duration-200 ease-in-out translate-x-full transform" 1771 :class="{ 1772 'translate-x-full' : mobilePanelActiveId !== 'category-node-4', 1773 'translate-x-0' : mobilePanelActiveId === 'category-node-4', 1774 }" 1775 aria-label="Subcategories" 1776 > 1777 <li> 1778 <button 1779 type="button" 1780 class="flex items-center px-8 py-3 border-b cursor-pointer bg-container border-container w-full border-t" 1781 @click="backToMainCategories('category-node-4-main')" 1782 aria-label="Back to main categories" 1783 > 1784 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 1785 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/> 1786</svg>
1787 <span class="ml-4"> 1788 Women </span> 1789 </button> 1790 </li> 1791 <li> 1792 <a 1793 href="https://www.studio-88.co.za/women" 1794 title="Women" 1795 class="flex items-center w-full px-8 py-3 border-b cursor-pointer 1796 bg-container-lighter border-container 1797 " 1798 > 1799 <span class="ml-10"> 1800 View All </span> 1801 </a> 1802 </li> 1803 <li class="font-bold"> 1804 <a 1805 href="https://www.studio-88.co.za/women/footwear" 1806 title="Footwear" 1807 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1808 bg-container-lighter border-container 1809 " 1810 > 1811 <span class="ml-10 text-base text-gray-700"> 1812 Footwear </span> 1813 </a> 1814 </li> 1815 <ul class="px-4"> 1816 <li> 1817 <a 1818 href="https://www.studio-88.co.za/women/footwear/sneakers" 1819 title="Sneakers" 1820 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1821 bg-container-lighter border-container 1822 " 1823 > 1824 <span class="ml-10 text-base text-gray-700"> 1825 Sneakers </span> 1826 </a> 1827 </li> 1828 <li> 1829 <a 1830 href="https://www.studio-88.co.za/women/footwear/sandals-slides" 1831 title="Sandals/Slides" 1832 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1833 bg-container-lighter border-container 1834 " 1835 > 1836 <span class="ml-10 text-base text-gray-700"> 1837 Sandals/Slides </span> 1838 </a> 1839 </li> 1840 </ul> 1841 <li class="font-bold"> 1842 <a 1843 href="https://www.studio-88.co.za/women/clothing" 1844 title="Clothing" 1845 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1846 bg-container-lighter border-container 1847 " 1848 >
1849 <span class="ml-10 text-base text-gray-700"> 1850 Clothing </span> 1851 </a> 1852 </li> 1853 <ul class="px-4"> 1854 <li> 1855 <a 1856 href="https://www.studio-88.co.za/women/clothing/pants" 1857 title="Pants" 1858 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1859 bg-container-lighter border-container 1860 " 1861 > 1862 <span class="ml-10 text-base text-gray-700"> 1863 Pants </span> 1864 </a> 1865 </li> 1866 <li> 1867 <a 1868 href="https://www.studio-88.co.za/women/clothing/t-shirts" 1869 title="T-shirts" 1870 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1871 bg-container-lighter border-container 1872 " 1873 > 1874 <span class="ml-10 text-base text-gray-700"> 1875 T-shirts </span> 1876 </a> 1877 </li> 1878 <li> 1879 <a 1880 href="https://www.studio-88.co.za/women/clothing/tops" 1881 title="Tops" 1882 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1883 bg-container-lighter border-container 1884 " 1885 > 1886 <span class="ml-10 text-base text-gray-700"> 1887 Tops </span> 1888 </a> 1889 </li> 1890 </ul> 1891 <li class="font-bold"> 1892 <a 1893 href="https://www.studio-88.co.za/women/soccer" 1894 title="SOCCER" 1895 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1896 bg-container-lighter border-container 1897 " 1898 >
1899 <span class="ml-10 text-base text-gray-700"> 1900 SOCCER </span> 1901 </a> 1902 </li> 1903 <ul class="px-4"> 1904 <li> 1905 <a 1906 href="https://www.studio-88.co.za/women/soccer/headwear" 1907 title="Headwear" 1908 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1909 bg-container-lighter border-container 1910 " 1911 > 1912 <span class="ml-10 text-base text-gray-700"> 1913 Headwear </span> 1914 </a> 1915 </li> 1916 <li> 1917 <a 1918 href="https://www.studio-88.co.za/women/soccer/jerseys" 1919 title="Jerseys" 1920 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1921 bg-container-lighter border-container 1922 " 1923 > 1924 <span class="ml-10 text-base text-gray-700"> 1925 Jerseys </span> 1926 </a> 1927 </li> 1928 </ul> 1929 <li class="font-bold"> 1930 <a 1931 href="https://www.studio-88.co.za/women/exclusive" 1932 title="EXCLUSIVE" 1933 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1934 bg-container-lighter border-container 1935 " 1936 > 1937 <span class="ml-10 text-base text-gray-700"> 1938 EXCLUSIVE </span> 1939 </a> 1940 </li> 1941 <li class="font-bold"> 1942 <a 1943 href="https://www.studio-88.co.za/women/sale" 1944 title="SALE" 1945 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 1946 bg-container-lighter border-container 1947 " 1948 >
1949 <span class="ml-10 text-base text-gray-700"> 1950 SALE </span> 1951 </a> 1952 </li> 1953 </ul> 1954 <button 1955 @click="closeMenu()" 1956 class="absolute flex justify-end text-black w-16 self-end mb-1 transition-none" 1957 aria-label="Close menu" 1958 > 1959 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="hidden p-4" width="64" height="64" :class="{ 'hidden' : !open, 'block': open }" aria-hidden="true"> 1960 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1961</svg> 1962 </button> 1963 </div> 1964 </li> 1965 <li data-child-id="category-node-248-main" class="level-0 text-black"> 1966 <div class="flex items-center transition-transform duration-150 ease-in-out transform":class="{'-translate-x-full' : mobilePanelActiveId,'translate-x-0' : !mobilePanelActiveId}"> 1967 <a class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0" href="https://www.studio-88.co.za/kids" title="Kids"> 1968 Kids </a> 1969 <button 1970 @click="openSubcategory('category-node-248')" 1971 class="absolute right-0 flex items-center justify-center w-8 h-8 mr-8 cursor-pointer 1972 bg-container-lighter border-container" 1973 aria-label="Open Kids subcategories" 1974 aria-haspopup="true" 1975 :aria-expanded="mobilePanelActiveId === 'category-node-248'" 1976 > 1977 <div class="w-8 h-8 border rounded"> 1978 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-full h-full p-1" width="24" height="24" aria-hidden="true"> 1979 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/> 1980</svg> 1981 </div> 1982 </button> 1983 </div> 1984 <div 1985 data-child-id="category-node-248" 1986 class="absolute top-0 right-0 z-10 flex flex-col gap-1 w-full h-full p-1 bg-container-lighter" 1987 :class="{'hidden': mobilePanelActiveId !== 'category-node-248'}"> 1988 <ul 1989 class="mt-16 transition-transform duration-200 ease-in-out translate-x-full transform" 1990 :class="{ 1991 'translate-x-full' : mobilePanelActiveId !== 'category-node-248', 1992 'translate-x-0' : mobilePanelActiveId === 'category-node-248', 1993 }" 1994 aria-label="Subcategories" 1995 > 1996 <li> 1997 <button 1998 type="button" 1999 class="flex items-center px-8 py-3 border-b cursor-pointer bg-container border-container w-full border-t" 2000 @click="backToMainCategories('category-node-248-main')" 2001 aria-label="Back to main categories" 2002 > 2003 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 2004 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/> 2005</svg>
2006 <span class="ml-4"> 2007 Kids </span> 2008 </button> 2009 </li> 2010 <li> 2011 <a 2012 href="https://www.studio-88.co.za/kids" 2013 title="Kids" 2014 class="flex items-center w-full px-8 py-3 border-b cursor-pointer 2015 bg-container-lighter border-container 2016 " 2017 > 2018 <span class="ml-10"> 2019 View All </span> 2020 </a> 2021 </li> 2022 <li class="font-bold"> 2023 <a 2024 href="https://www.studio-88.co.za/kids/clothing" 2025 title="Clothing" 2026 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2027 bg-container-lighter border-container 2028 " 2029 > 2030 <span class="ml-10 text-base text-gray-700"> 2031 Clothing </span> 2032 </a> 2033 </li> 2034 <ul class="px-4"> 2035 <li> 2036 <a 2037 href="https://www.studio-88.co.za/kids/clothing/shorts" 2038 title="Shorts" 2039 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2040 bg-container-lighter border-container 2041 " 2042 > 2043 <span class="ml-10 text-base text-gray-700"> 2044 Shorts </span> 2045 </a> 2046 </li> 2047 <li> 2048 <a 2049 href="https://www.studio-88.co.za/kids/clothing/t-shirts" 2050 title="T-shirts" 2051 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2052 bg-container-lighter border-container 2053 " 2054 > 2055 <span class="ml-10 text-base text-gray-700"> 2056 T-shirts </span> 2057 </a> 2058 </li> 2059 <li> 2060 <a 2061 href="https://www.studio-88.co.za/kids/clothing/tracksuits" 2062 title="Tracksuits" 2063 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2064 bg-container-lighter border-container 2065 " 2066 >
2067 <span class="ml-10 text-base text-gray-700"> 2068 Tracksuits </span> 2069 </a> 2070 </li> 2071 </ul> 2072 <li class="font-bold"> 2073 <a 2074 href="https://www.studio-88.co.za/kids/footwear" 2075 title="Footwear" 2076 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2077 bg-container-lighter border-container 2078 " 2079 > 2080 <span class="ml-10 text-base text-gray-700"> 2081 Footwear </span> 2082 </a> 2083 </li> 2084 <ul class="px-4"> 2085 <li> 2086 <a 2087 href="https://www.studio-88.co.za/kids/footwear/sandals-slides" 2088 title="Sandals/Slides" 2089 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2090 bg-container-lighter border-container 2091 " 2092 > 2093 <span class="ml-10 text-base text-gray-700"> 2094 Sandals/Slides </span> 2095 </a> 2096 </li> 2097 <li> 2098 <a 2099 href="https://www.studio-88.co.za/kids/footwear/sneakers" 2100 title="Sneakers" 2101 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2102 bg-container-lighter border-container 2103 " 2104 > 2105 <span class="ml-10 text-base text-gray-700"> 2106 Sneakers </span> 2107 </a> 2108 </li> 2109 </ul> 2110 <li class="font-bold"> 2111 <a 2112 href="https://www.studio-88.co.za/kids/soccer" 2113 title="SOCCER" 2114 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2115 bg-container-lighter border-container 2116 " 2117 >
2118 <span class="ml-10 text-base text-gray-700"> 2119 SOCCER </span> 2120 </a> 2121 </li> 2122 <ul class="px-4"> 2123 <li> 2124 <a 2125 href="https://www.studio-88.co.za/kids/soccer/boots" 2126 title="Boots" 2127 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2128 bg-container-lighter border-container 2129 " 2130 > 2131 <span class="ml-10 text-base text-gray-700"> 2132 Boots </span> 2133 </a> 2134 </li> 2135 <li> 2136 <a 2137 href="https://www.studio-88.co.za/kids/soccer/shorts" 2138 title="Shorts" 2139 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2140 bg-container-lighter border-container 2141 " 2142 > 2143 <span class="ml-10 text-base text-gray-700"> 2144 Shorts </span> 2145 </a> 2146 </li> 2147 <li> 2148 <a 2149 href="https://www.studio-88.co.za/kids/soccer/jerseys" 2150 title="Jerseys" 2151 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2152 bg-container-lighter border-container 2153 " 2154 > 2155 <span class="ml-10 text-base text-gray-700"> 2156 Jerseys </span> 2157 </a> 2158 </li> 2159 </ul> 2160 <li class="font-bold"> 2161 <a 2162 href="https://www.studio-88.co.za/kids/sale" 2163 title="SALE" 2164 class="text-gray-500 flex items-center w-full px-8 py-3 border-b cursor-pointer 2165 bg-container-lighter border-container 2166 " 2167 >
2168 <span class="ml-10 text-base text-gray-700"> 2169 SALE </span> 2170 </a> 2171 </li> 2172 </ul> 2173 <button 2174 @click="closeMenu()" 2175 class="absolute flex justify-end text-black w-16 self-end mb-1 transition-none" 2176 aria-label="Close menu" 2177 > 2178 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="hidden p-4" width="64" height="64" :class="{ 'hidden' : !open, 'block': open }" aria-hidden="true"> 2179 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 2180</svg> 2181 </button> 2182 </div> 2183 </li> 2184 <li data-child-id="category-node-81-main" class="level-0 text-black"> 2185 <div class="flex items-center transition-transform duration-150 ease-in-out transform":class="{'-translate-x-full' : mobilePanelActiveId,'translate-x-0' : !mobilePanelActiveId}"> 2186 <a class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0" href="https://www.studio-88.co.za/brands" title="Brands"> 2187 Brands </a> 2188 </div> 2189 </li> 2190 <li class="level-0 text-black"> 2191 <a href="https://www.studio-88.co.za/feature88" title="Brands" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2192 Feature88 </a> 2193 </li> 2194 <li class="level-0 text-black"> 2195 <div class="flex items-center transition-transform duration-150 ease-in-out transform":class="{'-translate-x-full' : mobilePanelActiveId,'translate-x-0' : !mobilePanelActiveId}"> 2196 <a href="https://www.studio-88.co.za/feature88/category/competitions" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2197 What's On </a> 2198 <button 2199 @click="openSubcategory('whats-on-pages')" 2200 class="absolute right-0 flex items-center justify-center w-8 h-8 mr-8 cursor-pointer 2201 bg-container-lighter border-container" 2202 aria-label="Open Whats On subcategories" 2203 aria-haspopup="true" 2204 :aria-expanded="mobilePanelActiveId === 'whats-on-pages'" 2205 > 2206 <div class="w-8 h-8 border rounded"> 2207 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-full h-full p-1" width="24" height="24" aria-hidden="true"> 2208 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/> 2209</svg> 2210 </div> 2211 </button> 2212 </div> 2213 <div data-child-id="whats-on-pages" class="absolute top-0 right-0 z-10 flex flex-col gap-1 w-full h-full p-1 bg-container-lighter":class="{'hidden': mobilePanelActiveId !== 'whats-on-pages'}"> 2214 <ul class="mt-16 transition-transform duration-200 ease-in-out translate-x-full transform" :class="{'translate-x-full' : mobilePanelActiveId !== 'whats-on-pages','translate-x-0' : mobilePanelActiveId === 'whats-on-pages',}" aria-label="Subcategories"> 2215 <li> 2216 <button type="button" class="flex items-center px-8 py-3 border-b cursor-pointer bg-container border-container w-full border-t" @click="backToMainCategories('whats-on-pages-main')" aria-label="Back to main categories" 2217 > 2218 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 2219 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/> 2220</svg>
2221 <span class="ml-4">What's On</span> 2222 </button> 2223 </li> 2224 <style>.cmsb432-ml-10 { 2225margin-left: 2.5rem 2226} 2227.cmsb432-block { 2228display: block 2229} 2230.cmsb432-flex { 2231display: flex 2232} 2233.cmsb432-w-full { 2234width: 100% 2235} 2236.cmsb432-transform { 2237transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) 2238} 2239.cmsb432-cursor-pointer { 2240cursor: pointer 2241} 2242.cmsb432-items-center { 2243align-items: center 2244} 2245.cmsb432-border-b { 2246border-bottom-width: 1px 2247} 2248.cmsb432-px-12 { 2249padding-left: 3rem; 2250padding-right: 3rem 2251} 2252.cmsb432-px-8 { 2253padding-left: 2rem; 2254padding-right: 2rem 2255} 2256.cmsb432-py-4 { 2257padding-top: 1rem; 2258padding-bottom: 1rem 2259} 2260.cmsb432-text-base { 2261font-size: 1rem; 2262line-height: 1.5rem 2263} 2264.cmsb432-text-gray-500 { 2265--tw-text-opacity: 1; 2266color: rgb(107 114 128 / var(--tw-text-opacity)) 2267} 2268.cmsb432-text-gray-700 { 2269--tw-text-opacity: 1; 2270color: rgb(55 65 81 / var(--tw-text-opacity)) 2271} 2272.cmsb432-transition { 2273transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 2274transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 2275transition-duration: 150ms 2276}</style> 2277<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><li> 2278 <a href="https://www.studio-88.co.za/feature88/tag/competition" 2279 title="The Studio88 Fan Club" 2280 class="cmsb432-text-gray-500 cmsb432-flex cmsb432-items-center cmsb432-w-full cmsb432-px-8 cmsb432-py-4 cmsb432-border-b cmsb432-cursor-pointer bg-container-lighter border-container"> 2281 <span class="cmsb432-ml-10 cmsb432-text-base cmsb432-text-gray-700"> 2282 Competitions 2283 </span> 2284 </a> 2285</li> 2286<li> 2287 <a href="https://www.studio-88.co.za/under-the-radar" 2288 title="The Studio88 Fan Club" 2289 class="cmsb432-text-gray-500 cmsb432-flex cmsb432-items-center cmsb432-w-full cmsb432-px-8 cmsb432-py-4 cmsb432-border-b cmsb432-cursor-pointer bg-container-lighter border-container"> 2290 <span class="cmsb432-ml-10 cmsb432-text-base cmsb432-text-gray-700"> 2291 Mix on the Move 2292 </span> 2293 </a> 2294</li> 2295 2296<style> 2297/* --- Mobile Nav Specific Styles --- */ 2298/* Hide mobile submenus by default */ 2299.mobile-submenu { 2300 display: none; 2301 /* For mobile, submenus usually take full width and stack */ 2302 width: 100%; 2303 background-color: #f8f8f8; /* Slightly different background for submenu */ 2304 border-top: 1px solid #e2e8f0; 2305} 2306 2307/* Ensure mobile submenu items fit and align properly */ 2308.mobile-submenu li a { 2309 /* Adjusted padding for nested items */ 2310 padding-left: 3rem; /* Further indent for child items (px-12) */ 2311 background-color: #f8f8f8; /* Inherit or define background */ 2312} 2313 2314/* Style for the mobile submenu arrow */ 2315.mobile-submenu-arrow { 2316 display: inline-block; 2317 transition: transform 0.2s ease-in-out; 2318} 2319 2320/* Rotate arrow when submenu is active */ 2321.has-mobile-submenu.active .mobile-submenu-arrow { 2322 transform: rotate(180deg); 2323} 2324 2325/* Show submenu when active */ 2326.mobile-submenu.active { 2327 display: block; 2328} 2329</style> 2330
2331<script> 2332document.addEventListener('DOMContentLoaded', function() { 2333 const mobileMenuToggles = document.querySelectorAll('.mobile-menu-toggle'); 2334 2335 mobileMenuToggles.forEach(toggle => { 2336 toggle.addEventListener('click', function(e) { 2337 e.preventDefault(); // Prevent default link behavior 2338 const parentLi = this.closest('.has-mobile-submenu'); 2339 const submenu = parentLi.querySelector('.mobile-submenu'); 2340 2341 // Toggle the 'active' class on the submenu 2342 submenu.classList.toggle('active'); 2343 // Toggle the 'active' class on the parent li for arrow rotation 2344 parentLi.classList.toggle('active'); 2345 2346 // Close other open mobile submenus 2347 document.querySelectorAll('.mobile-submenu.active').forEach(openSubmenu => { 2348 if (openSubmenu !== submenu) { 2349 openSubmenu.classList.remove('active'); 2350 openSubmenu.closest('.has-mobile-submenu').classList.remove('active'); 2351 } 2352 }); 2353 }); 2354 }); 2355 2356 // Optional: Close mobile submenu if clicking outside the entire mobile nav 2357 // This depends on your overall mobile nav implementation. 2358 // If your mobile nav closes entirely on outside click, this might not be needed. 2359 // document.addEventListener('click', function(e) { 2360 // if (!e.target.closest('.has-mobile-submenu') && !e.target.closest('YOUR_MAIN_MOBILE_NAV_CONTAINER_CLASS_OR_ID')) { 2361 // document.querySelectorAll('.mobile-submenu.active').forEach(openSubmenu => { 2362 // openSubmenu.classList.remove('active'); 2363 // openSubmenu.closest('.has-mobile-submenu').classList.remove('active'); 2364 // }); 2365 // } 2366 // }); 2367}); 2368</script>
2368 2369 2370 2371</div> </ul> 2372 <button @click="closeMenu()" class="absolute flex justify-end text-black w-16 self-end mb-1 transition-none" aria-label="Close menu"> 2373 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="hidden p-4" width="64" height="64" :class="{ 'hidden' : !open, 'block': open }" aria-hidden="true"> 2374 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 2375</svg> 2376 </button> 2377 </div> 2378 </li> 2379 <li class="level-0 text-black"> 2380 <a href="https://www.studio-88.co.za/sale2" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2381 Sale </a> 2382 </li> 2383 <!-- li class="level-0 text-black"> 2384 <a href="https://www.studio-88.co.za/festive-promo" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2385 Festive Promo </a> 2386 </li --> 2387 <li class="level-0 text-black"> 2388 <a href="https://www.studio-88.co.za/soccer-central" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2389 Soccer Central </a> 2390 </li> 2391 <li class="level-0 text-black"> 2392 <a href="https://www.studio-88.co.za/contact-us" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2393 Contact </a> 2394 </li> 2395 <li class="level-0 text-black"> 2396 <a href="https://www.studio-88.co.za/customer/account" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2397 Sign In </a> 2398 </li> 2399 <li class="level-0 text-black"> 2400 <a href="https://www.studio-88.co.za/customer/account/create" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2401 Create Account </a> 2402 </li> 2403 <li class="level-0 text-black"> 2404 <a href="https://www.studio-88.co.za/tracking" class="flex items-center w-full px-8 py-3 border-b cursor-pointer bg-container-lighter border-container level-0"> 2405 Track Parcel </a> 2406 </li> 2407 </ul> 2408 <button 2409 @click="closeMenu()" 2410 class="absolute flex justify-end text-black w-16 self-end mb-1" 2411 aria-label="Close menu" 2412 type="button" 2413 > 2414 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="hidden p-4" width="64" height="64" :class="{ 'hidden' : !open, 'block': open }" aria-hidden="true"> 2415 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 2416</svg> 2417 </button> 2418 </div> 2419</nav>
2420<script> 2421 'use strict'; 2422 2423 const initMenuMobile_6ab51b0cbe9fe = () => { 2424 return { 2425 mobilePanelActiveId: null, 2426 open: false, 2427 setActiveMenu(menuNode) { 2428 Array.from(menuNode.querySelectorAll('a')).filter(link => { 2429 return link.href === window.location.href.split('?')[0]; 2430 }).map(item => { 2431 item.classList.add('underline'); 2432 item.closest('li.level-0') && 2433 item.closest('li.level-0').querySelector('a.level-0').classList.add('underline'); 2434 }); 2435 }, 2436 openMenu() { 2437 this.open = true 2438 this.$nextTick(() => hyva.trapFocus(this.$refs['mobileMenuNavLinks'])); 2439 // Prevent from body scrolling while mobile menu opened 2440 document.body.style.position = 'fixed'; 2441 }, 2442 closeMenu() { 2443 document.body.style.position = ''; 2444 2445 if (this.open) { 2446 this.$nextTick(() => this.$refs['mobileMenuTrigger'].focus() || hyva.releaseFocus()); 2447 } 2448 2449 this.open = false 2450 this.mobilePanelActiveId = null 2451 }, 2452 openSubcategory(index) { 2453 const menuNodeRef = document.querySelector('[data-child-id=' + index + ']') 2454 this.mobilePanelActiveId = this.mobilePanelActiveId === index ? 0 : index 2455 this.$nextTick(() => hyva.trapFocus(menuNodeRef)) 2456 }, 2457 backToMainCategories(index) { 2458 const menuNodeRef = document.querySelector('[data-child-id=' + index + ']') 2459 this.mobilePanelActiveId = 0 2460 this.$nextTick(() => { 2461 hyva.trapFocus(this.$refs['mobileMenuNavLinks']) 2462 menuNodeRef.querySelector('a').focus() 2463 }) 2464 } 2465 } 2466 } 2467</script>
2467 2468 2469<nav class="main-nav navigation hidden lg:block order-2 sm:order-1 lg:order-2" aria-label="Main menu"> 2470 <ul class="menu gap-x-1"> 2471 2472 <li class="menu-item menu-desktop-menu-category-709 category-709 has-megamenu top-level uppercase"> 2473 <a class="text-white" href="https://www.studio-88.co.za/new-arrivals">New Arrivals <span data-icon="3"></span></a> 2474 <!-- Mega Menu --> 2475 <div class="megamenu shadow"> 2476 <div class="flex flex-wrap flex-row"> 2477 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2478 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/new-arrivals/new-arrivals">New Arrivals</a></h4> 2479 <ul class="mega-second-level"> 2480 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-footwear">New Footwear</a></li> 2481 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-in-men">New In Men</a></li> 2482 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-in-women">New In Women</a></li> 2483 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-in-kids">New In Kids</a></li> 2484 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/new-arrivals/new-arrivals/new-in-soccer">New In Soccer</a></li> 2485 </ul> 2486 </div> 2487 </div> 2488 </div> 2489 </li> 2490 <!--Custom Brands Dropdown--> 2491 <li class="menu-item menu-desktop-menu-category-3 category-3 has-megamenu top-level uppercase"> 2492 <a class="text-white" href="https://www.studio-88.co.za/men">Men <span data-icon="3"></span></a> 2493 <!-- Mega Menu --> 2494 <div class="megamenu shadow"> 2495 <div class="flex flex-wrap flex-row"> 2496 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2497 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/men/footwear">Footwear</a></h4> 2498 <ul class="mega-second-level"> 2499 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/footwear/sneakers">Sneakers</a></li> 2500 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/footwear/sandals-slides">Sandals/Slides</a></li> 2501 </ul> 2502 </div> 2503 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2504 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/men/clothing">Clothing</a></h4> 2505 <ul class="mega-second-level"> 2506 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/clothing/golfers">Golfers</a></li> 2507 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/clothing/jackets">Jackets</a></li> 2508 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/clothing/jeans">Jeans</a></li> 2509 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/clothing/pants">Pants</a></li> 2510 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/clothing/shorts">Shorts</a></li> 2511 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/clothing/t-shirts">T-shirts</a></li> 2512 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/clothing/tops">Tops</a></li> 2513 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/clothing/tracksuits">Tracksuits</a></li> 2514 </ul> 2515 </div> 2516 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2517 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/men/accessories">Accessories</a></h4> 2518 <ul class="mega-second-level"> 2519 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/accessories/bags">Bags</a></li> 2520 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/accessories/headwear">Headwear</a></li> 2521 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/accessories/shoe-care">Shoe Care</a></li> 2522 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/accessories/socks">Socks</a></li> 2523 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/accessories/underwear">Underwear</a></li> 2524 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/accessories/sandals-slides">Sandals/Slides</a></li> 2525 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/accessories/jewellery">Jewellery</a></li> 2526 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/accessories/other">Other</a></li> 2527 </ul> 2528 </div> 2529 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2530 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/men/soccer">SOCCER</a></h4> 2531 <ul class="mega-second-level"> 2532 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/jerseys">Jerseys</a></li> 2533 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/boots">
2533Boots</a></li> 2534 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/sandals-slides">Sandals/Slides</a></li> 2535 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/pants">Pants</a></li> 2536 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/t-shirts">T-Shirts</a></li> 2537 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/jackets">Jackets</a></li> 2538 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/golfers">Golfers</a></li> 2539 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/headwear">Headwear</a></li> 2540 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/shorts">Shorts</a></li> 2541 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/men/soccer/tops">Tops</a></li> 2542 </ul> 2543 </div> 2544 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2545 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/men/sale">SALE</a></h4> 2546 </div> 2547 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2548 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/men/exclusive">EXCLUSIVE</a></h4> 2549 </div> 2550 </div> 2551 </div> 2552 </li> 2553 <!--Custom Brands Dropdown--> 2554 <li class="menu-item menu-desktop-menu-category-4 category-4 has-megamenu top-level uppercase"> 2555 <a class="text-white" href="https://www.studio-88.co.za/women">Women <span data-icon="3"></span></a> 2556 <!-- Mega Menu --> 2557 <div class="megamenu shadow"> 2558 <div class="flex flex-wrap flex-row"> 2559 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2560 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/women/footwear">Footwear</a></h4> 2561 <ul class="mega-second-level"> 2562 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/women/footwear/sneakers">Sneakers</a></li> 2563 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/women/footwear/sandals-slides">Sandals/Slides</a></li> 2564 </ul> 2565 </div> 2566 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2567 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/women/clothing">Clothing</a></h4> 2568 <ul class="mega-second-level"> 2569 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/women/clothing/pants">Pants</a></li> 2570 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/women/clothing/t-shirts">T-shirts</a></li> 2571 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/women/clothing/tops">Tops</a></li> 2572 </ul> 2573 </div> 2574 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2575 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/women/soccer">SOCCER</a></h4> 2576 <ul class="mega-second-level"> 2577 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/women/soccer/headwear">Headwear</a></li> 2578 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/women/soccer/jerseys">Jerseys</a></li> 2579 </ul> 2580 </div> 2581 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2582 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/women/exclusive">EXCLUSIVE</a></h4> 2583 </div> 2584 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2585 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/women/sale">SALE</a></h4> 2586 </div> 2587 </div> 2588 </div> 2589 </li> 2590 <!--Custom Brands Dropdown--> 2591 <li class="menu-item menu-desktop-menu-category-248 category-248 has-megamenu top-level uppercase"> 2592 <a class="text-white" href="https://www.studio-88.co.za/kids">Kids <span data-icon="3"></span></a> 2593 <!-- Mega Menu --> 2594 <div class="megamenu shadow"> 2595 <div class="flex flex-wrap flex-row"> 2596 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2597 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/kids/clothing">Clothing</a></h4> 2598 <ul class="mega-second-level"> 2599 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/kids/clothing/shorts">Shorts</a></li> 2600 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/kids/clothing/t-shirts">T-shirts</a></li> 2601 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/kids/clothing/tracksuits">Tracksuits</a></li> 2602 </ul> 2603 </div> 2604 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2605 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/kids/footwear">Footwear</a></h4> 2606 <ul class="mega-second-level"> 2607 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/kids/footwear/sandals-slides">Sandals/Slides</a></li> 2608 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/kids/footwear/sneakers">Sneakers</a></li> 2609 </ul> 2610 </div> 2611 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2612 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/kids/soccer">SOCCER</a></h4> 2613 <ul class="mega-second-level"> 2614 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/kids/soccer/boots">
2614Boots</a></li> 2615 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/kids/soccer/shorts">Shorts</a></li> 2616 <li><a class="text-gray-600 capitalize" href="https://www.studio-88.co.za/kids/soccer/jerseys">Jerseys</a></li> 2617 </ul> 2618 </div> 2619 <div class="flex flex-wrap flex-col md:w-1/2 lg:w-1/4 mega-col mega-first-level"> 2620 <h4><a class="text-dark font-bold" href="https://www.studio-88.co.za/kids/sale">SALE</a></h4> 2621 </div> 2622 </div> 2623 </div> 2624 </li> 2625 <!--Custom Brands Dropdown--> 2626 <li class="menu-item desktop-menu-category-81 category-81 has-megamenu top-level uppercase"> 2627 <a class="text-white" href="https://www.studio-88.co.za/brands">Brands <span data-icon="3"></span></a> 2628 <!-- Mega Menu --> 2629 <div class="megamenu shadow"> 2630 <style>.cmsb423-flex { 2631display: flex 2632} 2633.cmsb423-hidden { 2634display: none 2635} 2636.cmsb423-flex-row { 2637flex-direction: row 2638} 2639.cmsb423-flex-col { 2640flex-direction: column 2641} 2642.cmsb423-flex-wrap { 2643flex-wrap: wrap 2644}</style> 2645<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-row"> 2646 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2647 <a href="https://www.studio-88.co.za/adidas-original" target="_self"> 2648 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/adidas-original-logo.png" alt="Adidas Originals Brand" width="150" height="150" /> 2649 </a> 2650 </div> 2651 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2652 <a href="https://www.studio-88.co.za/adidas-performance" target="_self"> 2653 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/adidas-performance.png" alt="Adidas Performance Brand" width="150" height="150" /> 2654 </a> 2655 </div> 2656 <!--<div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2657 <a href="https://www.studio-88.co.za/alpha-industries" target="_self"> 2658 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/alpha-industries.png" alt="Alpha Industries Brand" width="150" height="150" /> 2659 </a> 2660 </div>--> 2661 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2662 <a href="https://www.studio-88.co.za/calvin-klein" target="_self"> 2663 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/Calvin_Klein_Logo.jpg" alt="Calvin Klein Brand" width="150" height="150" /> 2664 </a> 2665 </div> 2666 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2667 <a href="https://www.studio-88.co.za/converse" target="_self"> 2668 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/converse.png" alt="Converse Brand" width="150" height="150" /> 2669 </a> 2670 </div> 2671 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2672 <a href="https://www.studio-88.co.za/ed-hardy" target="_self"> 2673 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Ed_Hardy_Brand_Logo.png" alt="Ed Hardy Brand" width="150" height="150" /> 2674 </a> 2675 </div> 2676 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2677 <a href="https://www.studio-88.co.za/ellesse" target="_self"> 2678 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/ellesse_brand_logo_320x320.png" alt="Ellesse Brand" width="150" height="150" /> 2679 </a> 2680 </div> 2681 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2682 <a href="https://www.studio-88.co.za/hi-tec" target="_self"> 2683 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Hi-Tec-Logo.png" alt="Hi Tec Brand" width="150" height="150" /> 2684 </a> 2685 </div> 2686 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2687 <a href="https://www.studio-88.co.za/jockey" target="_self"> 2688 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Jockey_150_Years.png" alt="Jockey" width="150" height="150" /> 2689 </a> 2690 </div> 2691 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2692 <a href="https://www.studio-88.co.za/kappa" target="_self"> 2693 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Kappa_Brand_L
2693ogo.jpg" alt="Kappa" width="150" height="150" /> 2694 </a> 2695 </div> 2696 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2697 <a href="https://www.studio-88.co.za/levis" target="_self"> 2698 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/levis.png" alt="Levis Brand" width="150" height="150" /> 2699 </a> 2700 </div> 2701 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2702 <a href="https://www.studio-88.co.za/lyle-and-scott" target="_self"> 2703 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Lyle_Scott_Logo.png" alt="Lyle Scott Brand" width="150" height="150" /> 2704 </a> 2705 </div> 2706 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2707 <a href="https://www.studio-88.co.za/mitchell-ness" target="_self"> 2708 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Mitchell_Ness_Logo.png" alt="Mitchel & Ness Brand" width="150" height="150" /> 2709 </a> 2710 </div> 2711 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2712 <a href="https://www.studio-88.co.za/nautic-spirit" target="_self"> 2713 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/nautic-spirit.png" alt="Nautic Spirit Brand" width="150" height="150" /> 2714 </a> 2715 </div> 2716 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2717 <a href="https://www.studio-88.co.za/new-balance" target="_self"> 2718 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/New_Balance_Brand_Logo.png" alt="New Balance Brand" width="150" height="150" /> 2719 </a> 2720 </div> 2721 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2722 <a href="https://www.studio-88.co.za/nike" target="_self"> 2723 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/nike.png" alt="Nike Brand" width="150" height="150" /> 2724 </a> 2725 </div> 2726 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2727 <a href="https://www.studio-88.co.za/playboy" target="_self"> 2728 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/playboy.png" alt="Playboy Brand" width="150" height="150" /> 2729 </a> 2730 </div> 2731 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2732 <a href="https://www.studio-88.co.za/primo-sport" target="_self"> 2733 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/Primo_Sport_Brand_Logo.png" alt="Primo Sport Brand" width="150" height="150" /> 2734 </a> 2735 </div> 2736 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2737 <a href="https://www.studio-88.co.za/puma" target="_self"> 2738 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/puma.png" alt="Puma Brand" width="150" height="150" /> 2739 </a> 2740 </div> 2741 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2742 <a href="https://www.studio-88.co.za/reebok" target="_self"> 2743 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/ReebokLogo.png" alt="Reebok Brand" width="150" height="150" /> 2744 </a> 2745 </div> 2746 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2747 <a href="https://www.studio-88.co.za/siksilk" target="_self"> 2748 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/SikSilk_Logo_S88.png" alt="SikSilk Brand" width="150" height="150" /> 2749 </a> 2750 </div> 2751 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2752 <a href="https://www.studio-88.co.za/skhanda-world" target="_self"> 2753 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/Skhanda_World_Logo_320.png" alt="Skhanda World Brand" width="150" height="150" /> 2754 </a> 2755 </div> 2756 <div class hidden="flex flex-wrap flex-col mega-col mega-first-level min-w-36"> 2757 <a href="https://www.studio-88.co.za/superga" target="_self"> 2758 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/superga.png" alt="Superga Brand" width="150" height="150" /> 2759 </a> 2760 </div> 2761 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2762 <a href="https://www.studio-88.co.za/superga" target="_self"> 2763 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Superga.png" alt="Superga Brand" width="150" height="150" /> 2764 </a> 2765 </div> 2766 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2767 <a href="https://www.studio-88.co.za/the-north-face" target="_self"> 2768 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/the-north-face.png" alt="The North Face Brand" width="150" height="150" /> 2769 </a> 2770 </div> 2771 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2772 <a href="https://www.studio-88.co.za/under-armour" target="_self"> 2773 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Under_Armour_Brand_Logo.jpg" alt="Under Armour Brand" width="150" height="150" /> 2774 </a> 2775 </div> 2776 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2777 <a href="https://www.studio-88.co.za/vans" target="_self"> 2778 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Vans_Logo_320x320.png" alt="Vans Brand" width="150" height="150" /> 2779 </a> 2780 </div> 2781 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2782 <a href="https://www.studio-88.co.za/volkswagen" target="_self"> 2783 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brands/volkswagen.png" alt="Volkswagen Logo" width="150" height="150" /> 2784 </a> 2785 </div> 2786 <div class="cmsb423-flex cmsb423-flex-wrap cmsb423-flex-col mega-col mega-first-level min-w-36"> 2787 <a href="https://www.studio-88.co.za/von-dutch" target="_self"> 2788 <img class="mega-brand" src="https://www.studio-88.co.za/media/wysiwyg/brand-logos/Von_Dutch_Brand_L
2788ogo.png" alt="Vond Dutch Logo" width="150" height="150" /> 2789 </a> 2790 </div> 2791 </div></div> </div> 2792 </li> 2793 <!--Implicit Dropdown--> 2794 <!---Tacked On Manually--> 2795 <li class="menu-item feature88 top-level uppercase"> 2796 <a class="text-white" href="https://www.studio-88.co.za/feature88">Feature88</a> 2797 </li> 2798 <li class="menu-item whats-on has-megamenu top-level uppercase"> 2799 <a class="text-white" href="https://www.studio-88.co.za/feature88/category/competitions">What's On <span data-icon="3"></span></a> 2800 <!-- Dropdown --> 2801 <ul class="submenu submenu-first-level shadow"> 2802 <style>.cmsb427-absolute { 2803position: absolute 2804} 2805.cmsb427-relative { 2806position: relative 2807} 2808.cmsb427-block { 2809display: block 2810} 2811.cmsb427-border { 2812border-width: 1px 2813} 2814.cmsb427-capitalize { 2815text-transform: capitalize 2816} 2817.cmsb427-text-gray-600 { 2818--tw-text-opacity: 1; 2819color: rgb(75 85 99 / var(--tw-text-opacity)) 2820} 2821.cmsb427-shadow { 2822--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
2823--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); 2824box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) 2825}</style> 2826<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><li><a class="cmsb427-text-gray-600 cmsb427-capitalize" href="https://www.studio-88.co.za/feature88/tag/competition">Competitions</a></li> 2827<li><a class="cmsb427-text-gray-600 cmsb427-capitalize" href="https://www.studio-88.co.za/under-the-radar">Mix on the Move</a></li> 2828 2829<style> 2830/* Hide submenus by default */ 2831.submenu { 2832 display: none; 2833 position: absolute; /* Essential for positioning the submenu relative to its parent */ 2834 background-color: #ffffff; /* Example background color, adjust as needed */ 2835 border: 1px solid #e2e8f0; /* Example border, adjust as needed */ 2836 padding: 10px; /* Add some padding around the items */ 2837 min-width: 180px; /* Give the submenu a minimum width */ 2838 z-index: 1000; /* Ensure the submenu appears above other content */ 2839 box-shadow: 0 2px 5px rgba(0,0,0,0.1); /* Optional: add a subtle shadow */ 2840 white-space: normal; /* Allow text to wrap naturally within the submenu items */ 2841} 2842 2843/* Ensure submenu items don't take up too much space and allow wrapping */ 2844.submenu li { 2845 padding: 5px 0; /* Add vertical padding to each item */ 2846} 2847 2848.submenu li a { 2849 display: block; /* Make the link fill the available space */ 2850 white-space: normal; /* Explicitly allow wrapping for the link text */ 2851 word-wrap: break-word; /* Break long words if necessary */ 2852} 2853 2854/* Positioning for the parent to ensure the submenu is aligned correctly */ 2855.has-submenu { 2856 position: relative; /* Make sure the parent is positioned for absolute submenu */ 2857} 2858 2859/* Add a class for active submenu */ 2860.submenu.active { 2861 display: block; 2862} 2863</style> 2864
2865<script> 2866document.addEventListener('DOMContentLoaded', function() { 2867 const menuToggles = document.querySelectorAll('.menu-toggle'); 2868 2869 menuToggles.forEach(toggle => { 2870 toggle.addEventListener('click', function(e) { 2871 e.preventDefault(); // Prevent the default link behavior (e.g., navigating to #) 2872 const parentLi = this.closest('.has-submenu'); 2873 const submenu = parentLi.querySelector('.submenu'); 2874 2875 // Toggle the 'active' class 2876 submenu.classList.toggle('active'); 2877 2878 // Close other open submenus if any 2879 document.querySelectorAll('.submenu.active').forEach(openSubmenu => { 2880 if (openSubmenu !== submenu) { 2881 openSubmenu.classList.remove('active'); 2882 } 2883 }); 2884 }); 2885 }); 2886 2887 // Close submenu if clicked outside 2888 document.addEventListener('click', function(e) { 2889 if (!e.target.closest('.has-submenu')) { 2890 document.querySelectorAll('.submenu.active').forEach(openSubmenu => { 2891 openSubmenu.classList.remove('active'); 2892 }); 2893 } 2894 }); 2895}); 2896</script>
2896</div> </ul> 2897 </li> 2898 <li class="menu-item sale top-level uppercase"> 2899 <a class="text-primary" href="https://www.studio-88.co.za/sale2">Sale</a> 2900 </li> 2901 <!-- li class="menu-item sale top-level uppercase"> 2902 <a class="text-primary" href="https://www.studio-88.co.za/festive-promo">Festive Promo</a> 2903 </li --> 2904 <li class="menu-item contact top-level uppercase"> 2905 <a class="text-white" href="https://www.studio-88.co.za/soccer-central">Soccer Central</a> 2906 </li> 2907 <li class="menu-item contact top-level uppercase"> 2908 <a class="text-white" href="https://www.studio-88.co.za/contact-us">Contact</a> 2909 </li> 2910 </ul> 2911</nav> 2912 2913 2914 <div class="additionals flex items-center gap-2 md:gap-1 order-3 md:-mr-1"> 2915 <!--Compare Icon--> 2916 <a id="compare-link" 2917 class="hidden relative inline-block rounded p-1 hover:bg-primary/10 outline-offset-2 invisible" 2918 :class="{ 'invisible': !(itemCount > 0) }" 2919 href="https://www.studio-88.co.za/catalog/product_compare/index/" 2920 title="Compare Products" 2921 x-data="initCompareHeader()" 2922 @private-content-loaded.window="receiveCompareData($event.detail.data)" 2923 :aria-label="` 2924 Compare Products, 2925 ${itemCount > 1 2926 ? hyva.str('\u00251\u0020Items', itemCount) 2927 : hyva.str('\u00251\u0020Item', itemCount) 2928 }`" 2929 > 2930 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="md:h-6 md:w-6" width="28" height="28" aria-hidden="true"> 2931 <path stroke-linecap="round" stroke-linejoin="round" d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3"/> 2932</svg> 2933 <span 2934 x-text="itemCount" 2935 class="absolute -top-1.5 -right-1.5 h-5 px-2 py-1 rounded-full bg-yellow-500 text-white 2936 text-xs font-semibold leading-none text-center uppercase tabular-nums" 2937 aria-hidden="true" 2938 ></span> 2939 </a> 2940 <!--Search Icon--> 2941 <button 2942 id="menu-search-icon" 2943 class="rounded p-1 text-white hover:bg-primary/10 outline-offset-2" 2944 @click.prevent=" 2945 searchOpen = !searchOpen; 2946 $dispatch('search-open'); 2947 " 2948 aria-label="Toggle search form" 2949 aria-haspopup="true" 2950 :aria-expanded="searchOpen" 2951 x-ref="searchButton" 2952 > 2953 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="md:h-6 md:w-6" width="28" height="28" aria-hidden="true"> 2954 <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/> 2955</svg> 2956 </button> 2957 <!-- Additional Header Elements --> 2958 <!--Customer Icon & Dropdown--> 2959 <!--Cart Icon--> 2960 <button 2961 id="menu-cart-icon" 2962 class="relative inline-block rounded p-1 text-white hover:bg-primary/10 outline-offset-2" 2963 x-ref="cartButton" 2964 :aria-disabled="isCartEmpty()" 2965 :aria-label="` 2966 Toggle minicart, 2967 ${isCartEmpty() 2968 ? 'Cart is empty' 2969 : cart.summary_count > 1 2970 ? hyva.str('%1 items', cart.summary_count) 2971 : hyva.str('%1 item', cart.summary_count) 2972 }`" 2973 @click.prevent.stop="() => { 2974 $dispatch('toggle-cart', { isOpen: true }) 2975 }" 2976 @toggle-cart.window="toggleCart($event)" 2977 :aria-expanded="isCartOpen" 2978 aria-haspopup="dialog" 2979 > 2980 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="md:h-6 md:w-6" width="28" height="28" aria-hidden="true"> 2981 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
29810 014 0z"/> 2982</svg> 2983 <span 2984 x-text="cart.summary_count" 2985 x-show="!isCartEmpty()" 2986 x-cloak 2987 class="absolute -top-1.5 -right-1.5 h-5 px-2 py-1 rounded-full bg-primary text-white 2988 text-xs font-semibold leading-none text-center uppercase tabular-nums" 2989 aria-hidden="true" 2990 ></span> 2991 </button> 2992 </div> 2993 </div> 2994 <!--Search--> 2995 <div class="absolute z-10 w-full border-t shadow-sm bg-container-lighter border-container-lighter" 2996 id="search-content" 2997 x-cloak x-show="searchOpen" 2998 @click.outside="searchOpen = false" 2999 @keydown.escape=" 3000 searchOpen = false; 3001 $refs.searchButton.focus(); 3002 " 3003 > 3004
3004<script type="text/javascript"> 3005 'use strict'; 3006 3007 function initMiniSearch() { 3008 return { 3009 minSearchLength: 3, 3010 suggestionsAllowed: false, 3011 suggestions: [], 3012 suggest() { 3013 const search = this.$refs.searchInput; 3014 if (search.value.length >= this.minSearchLength) { 3015 search.setCustomValidity(''); 3016 search.reportValidity(); 3017 // Start Klevu customisation of default Hyvä template 3018 if (this.suggestionsAllowed) { 3019 this.fetchSuggestions(search.value); 3020 } else { 3021 this.suggestions = []; 3022 } 3023 // End Klevu customisation of default Hyvä template 3024 } else { 3025 this.suggestions = []; 3026 } 3027 }, 3028 fetchSuggestions(term) { 3029 fetch( 3030 window.BASE_URL + 'search/ajax/suggest?' + new URLSearchParams({q: term}), 3031 { 3032 headers: { 3033 'X-Requested-With': 'XMLHttpRequest' 3034 } 3035 } 3036 ) 3037 .then(response => response.json()) 3038 .then(result => this.suggestions = result); 3039 }, 3040 search(term) { 3041 const search = this.$refs.searchInput; 3042 term = term || search.value; 3043 if (term.length < this.minSearchLength) { 3044 search.setCustomValidity('Minimum\u0020Search\u0020query\u0020length\u0020is\u00203'); 3045 search.reportValidity(); 3046 } else { 3047 search.setCustomValidity(''); 3048 search.value = term; 3049 this.$refs.form.submit(); 3050 } 3051 }, 3052 focusElement(element) { 3053 if (element && element.nodeName === "DIV") { 3054 element.focus(); 3055 return true; 3056 } else { 3057 return false; 3058 } 3059 } 3060 } 3061 } 3062</script>
3062 3063<div class="container py-2 mx-auto text-black" x-data="initMiniSearch()"> 3064 <form class="form minisearch" id="search_mini_form" x-ref="form" 3065 action="https://www.studio-88.co.za/search/" method="get"> 3066 <label class="hidden" for="search" data-role="minisearch-label"> 3067 <span>Search</span> 3068 </label> 3069 <input id="search" 3070 x-ref="searchInput" 3071 type="search" 3072 autocomplete="off" 3073 name="q" 3074 value="" 3075 placeholder="Search entire store here..." 3076 maxlength="128" 3077 class="w-full p-2 text-lg leading-normal transition appearance-none text-grey-800 3078 focus:outline-none focus:border-transparent lg:text-xl" 3079 /> 3080 <template x-if="suggestions.length > 0"> 3081 <div class="w-full leading-normal transition appearance-none text-grey-800 flex flex-col mt-1"> 3082 <template x-for="suggestion in suggestions"> 3083 <div class="flex justify-between p-2 bg-container-lighter even:bg-container mb-1 cursor-pointer 3084 border border-container hover:bg-container-darker" 3085 tabindex="0" 3086 @click="search(suggestion.title)" 3087 @keydown.enter="search(suggestion.title)" 3088 @keydown.arrow-up.prevent=" 3089 focusElement($event.target.previousElementSibling) || $refs.searchInput.focus() 3090 " 3091 @keydown.arrow-down.prevent="focusElement($event.target.nextElementSibling)" 3092 > 3093 <span x-text="suggestion.title"></span> 3094 <span x-text="suggestion.num_results"></span> 3095 </div> 3096 </template> 3097 </div> 3098 </template> 3099 <button type="submit" 3100 title="Search" 3101 class="action search sr-only" 3102 aria-label="Search" 3103 > 3104 Search </button> 3105 </form> 3106</div> 3107 </div> 3108
3109 <!--Cart Drawer--> 3110
3110<script> 3111 function initCartDrawer() { 3112 return { 3113 open: false, 3114 isLoading: false, 3115 cart: {}, 3116 maxItemsToDisplay: 10, 3117 itemsCount: 0, 3118 getData(data) { 3119 if (data.cart) { 3120 this.cart = data.cart; 3121 this.itemsCount = data.cart.items && data.cart.items.length || 0; 3122 this.setCartItems(); 3123 } 3124 this.isLoading = false; 3125 }, 3126 cartItems: [], 3127 getItemCountTitle() { 3128 return hyva.strf('\u00250\u0020of\u0020\u00251\u0020products\u0020in\u0020cart\u0020displayed', this.maxItemsToDisplay, this.itemsCount) 3129 }, 3130 setCartItems() { 3131 this.cartItems = this.cart.items && this.cart.items.sort((a, b) => b.item_id - a.item_id) || []; 3132 3133 if (this.maxItemsToDisplay > 0) { 3134 this.cartItems = this.cartItems.slice(0, parseInt(this.maxItemsToDisplay, 10)); 3135 } 3136 }, 3137 deleteItemFromCart(itemId) { 3138 this.isLoading = true; 3139 3140 const formKey = hyva.getFormKey(); 3141 const postUrl = BASE_URL + 'checkout/sidebar/removeItem/'; 3142 3143 fetch(postUrl, { 3144 "headers": { 3145 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 3146 }, 3147 "body": "form_key=" + formKey + "&item_id=" + itemId, 3148 "method": "POST", 3149 "mode": "cors", 3150 "credentials": "include" 3151 }).then(response => { 3152 if (response.redirected) { 3153 window.location.href = response.url; 3154 } else if (response.ok) { 3155 return response.json(); 3156 } else { 3157 window.dispatchMessages && window.dispatchMessages([{ 3158 type: 'warning', 3159 text: 'Could\u0020not\u0020remove\u0020item\u0020from\u0020quote.' 3160 }]); 3161 this.isLoading = false; 3162 } 3163 }).then(result => { 3164 window.dispatchMessages && window.dispatchMessages([{ 3165 type: result.success ? 'success' : 'error', 3166 text: result.success 3167 ? 'You\u0020removed\u0020the\u0020item.' 3168 : result.error_message 3169 }], result.success ? 5000 : 0) 3170 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 3171 }); 3172 }, 3173 scrollLock(use = true) { 3174 document.body.style.overflow = use ? "hidden" : ""; 3175 }, 3176 toggleCartDrawer(event) { 3177 if (event.detail && event.detail.isOpen !== undefined) { 3178 if (event.detail.isOpen) { 3179 this.openCartDrawer(); 3180 } else { 3181 this.open = false; 3182 this.scrollLock(false); 3183 this.$refs && this.$refs.cartDialogContent && hyva.releaseFocus(this.$refs.cartDialogContent); 3184 } 3185 } else { 3186 this.openCartDrawer() 3187 } 3188 }, 3189 openCartDrawer() { 3190 this.open = true; 3191 this.scrollLock(true); 3192 this.$nextTick(() => { 3193 this.$refs && this.$refs.cartDialogContent && hyva.trapFocus(this.$refs.cartDialogContent) 3194 }) 3195 }, 3196 closeCartDrawer() { 3197 this.$dispatch('toggle-cart', { isOpen: false }) 3198 }, 3199 getSectionDataExtraActions() { 3200 if (!this.cart.extra_actions) { 3201 return ''; 3202 } 3203 3204 const contentNode = document.createElement('div'); 3205 contentNode.innerHTML = this.cart.extra_actions; 3206 3207 hyva.activateScripts(contentNode); 3208 3209 return contentNode.innerHTML; 3210 } 3211 } 3212 } 3213</script>
3213 3214<section x-cloak 3215 x-show="cart" 3216 id="cart-drawer" 3217 x-data="initCartDrawer()" 3218 @private-content-loaded.window="getData($event.detail.data)" 3219 @toggle-cart.window="toggleCartDrawer($event)" 3220 @keydown.escape="closeCartDrawer" 3221> 3222 <div role="dialog" 3223 aria-labelledby="cart-drawer-title" 3224 aria-modal="true" 3225 :aria-hidden="!open" 3226 class="fixed inset-y-0 right-0 z-30 flex max-w-full"> 3227 <div class="backdrop" 3228 x-show="open" 3229 x-transition:enter="ease-in-out duration-500" 3230 x-transition:enter-start="opacity-0" 3231 x-transition:enter-end="opacity-100" 3232 x-transition:leave="ease-in-out duration-500" 3233 x-transition:leave-start="opacity-100" 3234 x-transition:leave-end="opacity-0" 3235 role="button" 3236 @click="closeCartDrawer" 3237 aria-label="Close minicart"></div> 3238 <div class="relative w-screen max-w-md shadow-2xl" 3239 x-show="open" 3240 x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700" 3241 x-transition:enter-start="translate-x-full" 3242 x-transition:enter-end="translate-x-0" 3243 x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700" 3244 x-transition:leave-start="translate-x-0" 3245 x-transition:leave-end="translate-x-full" 3246 x-ref="cartDialogContent" 3247 role="region" 3248 :tabindex="open ? 0 : -1" 3249 aria-label="My Cart" 3250 3251 > 3252 <div class="flex flex-col h-full max-h-screen bg-white shadow-xl"> 3253 3254 <header class="relative px-4 py-6 sm:px-6"> 3255 <p id="cart-drawer-title" class="text-lg font-medium leading-7 text-gray-900"> 3256 <strong>My Cart</strong> 3257 <span class="items-total text-xs" 3258 x-show="maxItemsToDisplay && maxItemsToDisplay < itemsCount" 3259 x-text="getItemCountTitle()"> 3260 </span> 3261 </p> 3262 </header> 3263 3264 3265 <template x-if="!itemsCount"> 3266 <div class="relative px-4 py-6 bg-white border-bs sm:px-6 border-container"> 3267 Cart is empty </div> 3268 </template> 3269 3270 <template x-if="itemsCount"> 3271 <div class="relative grid gap-6 sm:gap-8 px-1 py-3 sm:px-3 bg-white border-b border-container overflow-y-auto overscroll-y-contain"> 3272 <template x-for="item in cartItems"> 3273 <div class="flex items-start p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 3274 <a :href="item.product_url" 3275 class="w-1/4" 3276 :aria-label="hyva.strf('Product\u0020\u0022\u00250\u0022', item.product_name)" 3277 > 3278 <img 3279 :src="item.product_image.src" 3280 :width="item.product_image.width" 3281 :height="item.product_image.height" 3282 loading="lazy" 3283 alt="" 3284 /> 3285 </a> 3286 <div class="w-3/4 space-y-2"> 3287 <div> 3288 <p class="text-xl"> 3289 <span x-html="item.qty"></span> x <span x-html="item.product_name"></span> 3290 </p> 3291 <p class="text-sm"><span x-html="item.product_sku"></span></p> 3292 </div> 3293 <template x-for="option in item.options"> 3294 <div class="pt-2"> 3295 <p class="font-semibold" x-text="option.label + ':'"></p> 3296 <p class="text-secondary" x-html="option.value"></p> 3297 </div> 3298 </template> 3299 <p><span x-html="item.product_price"></span></p> 3300 <div class="pt-4"> 3301 <a :href="item.configure_url" 3302 x-show="item.product_type !== 'grouped' && item.is_visible_in_site_visibility" 3303 class="inline-flex p-2 mr-2 btn btn-primary" 3304 :aria-label="hyva.strf('Edit\u0020product\u
33040020\u0022\u00250\u0022', item.product_name)" 3305 > 3306 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20" aria-hidden="true"> 3307 <path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/> 3308</svg> 3309 </a> 3310 <button type="button" 3311 class="inline-flex p-2 btn btn-primary" 3312 @click="deleteItemFromCart(item.item_id)" 3313 :aria-label="hyva.strf('Remove\u0020product\u0020\u0022\u00250\u0022\u0020from\u0020cart', item.product_name)" 3314 > 3315 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20" aria-hidden="true"> 3316 <path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/> 3317</svg> 3318 </button> 3319 </div> 3320 3321 </div> 3322 </div> 3323 </template> 3324 </div> 3325 </template> 3326 3327 <template x-if="itemsCount"> 3328 <div> 3329 3330 <div class="relative grid gap-6 sm:gap-8 py-3 px-1 sm:px-3 bg-white"> 3331 <div class="w-full p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 3332 <p>Subtotal: <span x-html="cart.subtotal"></span></p> 3333 </div> 3334 <div class="w-full p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 3335 <a href="https://www.studio-88.co.za/checkout/" 3336 class="inline-flex btn btn-primary" 3337 > 3338 Checkout </a> 3339 <span>or</span> 3340 <a href="https://www.studio-88.co.za/checkout/cart/" 3341 class="underline" 3342 > 3343 View and Edit Cart </a> 3344 </div> 3345 <div x-html="getSectionDataExtraActions()"></div> 3346 </div> 3347 </div> 3348 </template> 3349 3350 </div> 3351 3352 <button 3353 type="button" 3354 @click="closeCartDrawer" 3355 aria-label="Close minicart" 3356 class="absolute top-0 right-2 p-4 mt-2 text-gray-300 transition-colors hover:text-black" 3357 > 3358 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 3359 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 3360</svg> 3361 </button> 3362 </div> 3363 <div class="flex flex-row justify-center items-center w-full h-full fixed select-none z-50" 3364 style="left: 50%;top: 50%;transform: translateX(-50%) translateY(-50%);background: rgba(255,255,255,0.7);" 3365 x-show="isLoading" 3366 x-cloak 3367 x-transition:enter="ease-out duration-200" 3368 x-transition:enter-start="opacity-0" 3369 x-transition:enter-end="opacity-100" 3370 x-transition:leave="ease-in duration-200" 3371 x-transition:leave-start="opacity-100" 3372 x-transition:leave-end="opacity-0"> 3373 <!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL --> 3374<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57 57" stroke="currentColor" class="text-primary" width="57" height="57" role="img"> 3375 <g fill="none" fill-rule="evenodd"> 3376 <g transform="translate(1 1)" stroke-width="2"> 3377 <circle cx="5" cy="50" r="5">
3378 <animate attributeName="cy" begin="0s" dur="2.2s" values="50;5;50;50" calcMode="linear" repeatCount="indefinite"/> 3379 <animate attributeName="cx" begin="0s" dur="2.2s" values="5;27;49;5" calcMode="linear" repeatCount="indefinite"/> 3380 </circle> 3381 <circle cx="27" cy="5" r="5"> 3382 <animate attributeName="cy" begin="0s" dur="2.2s" from="5" to="5" values="5;50;50;5" calcMode="linear" repeatCount="indefinite"/> 3383 <animate attributeName="cx" begin="0s" dur="2.2s" from="27" to="27" values="27;49;5;27" calcMode="linear" repeatCount="indefinite"/> 3384 </circle> 3385 <circle cx="49" cy="50" r="5"> 3386 <animate attributeName="cy" begin="0s" dur="2.2s" values="50;50;5;50" calcMode="linear" repeatCount="indefinite"/> 3387 <animate attributeName="cx" from="49" to="49" begin="0s" dur="2.2s" values="49;5;27;49" calcMode="linear" repeatCount="indefinite"/> 3388 </circle> 3389 </g> 3390 </g> 3391<title>loader</title></svg> 3392 <div class="ml-10 text-primary text-xl"> 3393 Loading... </div> 3394</div> 3395 </div> 3396 3397</section> 3398 3399 <!--Authentication Pop-Up--> 3400 </div> 3401 3402</header><main id="maincontent" class="page-main-full-width"><div id="contentarea" tabindex="-1"></div> 3403<div class="page messages">
3403<script> 3404 function initMessages() { 3405 "use strict"; 3406 return { 3407 messages: window.mageMessages || [], 3408 isEmpty() { 3409 return this.messages.reduce( 3410 function (isEmpty, message) { 3411 return isEmpty && message === undefined 3412 }, true 3413 ) 3414 }, 3415 removeMessage(messageIndex) { 3416 this.messages[messageIndex] = undefined; 3417 }, 3418 addMessages(messages, hideAfter) { 3419 messages.map((message) => { 3420 this.messages = this.messages.concat(message); 3421 if (hideAfter === undefined && message.type === 'success' && window.defaultSuccessMessageTimeout) { 3422 hideAfter = window.defaultSuccessMessageTimeout; 3423 } 3424 if (hideAfter) { 3425 this.setHideTimeOut(this.messages.length -1, hideAfter); 3426 } 3427 }); 3428 }, 3429 setHideTimeOut(messageIndex, hideAfter) { 3430 setTimeout((messageIndex) => { 3431 this.removeMessage(messageIndex); 3432 }, hideAfter, messageIndex); 3433 }, 3434 eventListeners: { 3435 ['@messages-loaded.window'](event) { 3436 this.addMessages(event.detail.messages, event.detail.hideAfter) 3437 }, 3438 ['@private-content-loaded.window'](event) { 3439 const data = event.detail.data; 3440 if ( 3441 data.messages && 3442 data.messages.messages && 3443 data.messages.messages.length 3444 ) { 3445 this.addMessages(data.messages.messages); 3446 } 3447 }, 3448 ['@clear-messages.window']() { 3449 this.messages = []; 3450 } 3451 } 3452 } 3453 } 3454</script>
3454 3455<section id="messages" 3456 x-data="initMessages()" 3457 x-bind="eventListeners" 3458 aria-live="assertive" 3459 role="alert" 3460> 3461 <template x-if="!isEmpty()"> 3462 <div class="w-full"> 3463 <div class="messages container mx-auto py-3"> 3464 <template x-for="(message, index) in messages" :key="index"> 3465 <div> 3466 <template x-if="message"> 3467 <div class="message" :class="message.type" 3468 :ui-id="'message-' + message.type" 3469 > 3470 <span x-html="message.text"></span> 3471 <button 3472 type="button" 3473 class="text-gray-600 hover:text-black" 3474 aria-label="Close message" 3475 @click.prevent="removeMessage(index)" 3476 > 3477 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="stroke-current" width="18" height="18" aria-hidden="true"> 3478 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 3479</svg> 3480 </button> 3481 </div> 3482 </template> 3483 </div> 3484 </template> 3485 </div> 3486 </div> 3487 </template> 3488</section> 3489</div><div class="columns"><div class="column main"><style>.cmsp363-container { 3490width: 100% 3491} 3492@media (min-width: 640px) { 3493.cmsp363-container { 3494max-width: 640px 3495} 3496} 3497@media (min-width: 768px) { 3498.cmsp363-container { 3499max-width: 768px 3500} 3501} 3502@media (min-width: 1024px) { 3503.cmsp363-container { 3504max-width: 1024px 3505} 3506} 3507@media (min-width: 1280px) { 3508.cmsp363-container { 3509max-width: 1280px 3510} 3511} 3512@media (min-width: 1536px) { 3513.cmsp363-container { 3514max-width: 1536px 3515} 3516} 3517.cmsp363-visible { 3518visibility: visible 3519} 3520.cmsp363-absolute { 3521position: absolute 3522} 3523.cmsp363-relative { 3524position: relative 3525} 3526.cmsp363-bottom-4 { 3527bottom: 1rem 3528} 3529.cmsp363-left-0 { 3530left: 0px 3531} 3532.cmsp363-left-4 { 3533left: 1rem 3534} 3535.cmsp363-right-0 { 3536right: 0px 3537} 3538.cmsp363-right-4 { 3539right: 1rem 3540} 3541.cmsp363-top-1\/2 { 3542top: 50% 3543} 3544.cmsp363-z-10 { 3545z-index: 10 3546} 3547.cmsp363-mx-auto { 3548margin-left: auto; 3549margin-right: auto 3550} 3551.cmsp363-my-12 { 3552margin-top: 3rem; 3553margin-bottom: 3rem 3554} 3555.cmsp363-my-4 { 3556margin-top: 1rem; 3557margin-bottom: 1rem 3558} 3559.cmsp363-my-8 { 3560margin-top: 2rem; 3561margin-bottom: 2rem 3562} 3563.cmsp363-mt-4 { 3564margin-top: 1rem 3565} 3566.cmsp363-mt-6 { 3567margin-top: 1.5rem 3568} 3569.cmsp363-block { 3570display: block 3571} 3572.cmsp363-inline-block { 3573display: inline-block 3574} 3575.cmsp363-flex { 3576display: flex 3577} 3578.cmsp363-grid { 3579display: grid 3580} 3581.cmsp363-hidden { 3582display: none 3583} 3584.cmsp363-h-full { 3585height: 100% 3586} 3587.cmsp363-w-full { 3588width: 100% 3589} 3590.cmsp363-max-w-full { 3591max-width: 100% 3592} 3593.cmsp363-max-w-screen-2xl { 3594max-width: 1536px 3595} 3596.cmsp363-flex-shrink-0 { 3597flex-shrink: 0 3598} 3599.cmsp363--translate-y-1\/2 { 3600--tw-translate-y: -50%; 3601transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) 3602} 3603.cmsp363-transform { 3604transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) 3605} 3606.cmsp363-cursor-pointer { 3607cursor: pointer 3608} 3609.cmsp363-grid-cols-1 { 3610grid-template-columns: repeat(1, minmax(0, 1fr)) 3611} 3612.cmsp363-justify-center { 3613justify-content: center 3614} 3615.cmsp363-gap-2 { 3616gap: 0.5rem 3617} 3618.cmsp363-gap-4 { 3619gap: 1rem 3620} 3621.cmsp363-overflow-hidden { 3622overflow: hidden 3623} 3624.cmsp363-rounded-lg { 3625border-radius: 0.5rem 3626} 3627.cmsp363-rounded-md { 3628border-radius: 0.375rem 3629} 3630.cmsp363-bg-black { 3631--tw-bg-opacity: 1; 3632background-color: rgb(0 0 0 / var(--tw-bg-opacity)) 3633} 3634.cmsp363-bg-red-600 { 3635--tw-bg-opacity: 1; 3636background-color: rgb(220 38 38 / var(--tw-bg-opacity)) 3637} 3638.cmsp363-bg-opacity-50 { 3639--tw-bg-opacity: 0.5 3640} 3641.cmsp363-object-cover { 3642object-fit: cover 3643} 3644.cmsp363-p-3 { 3645padding: 0.75rem 3646} 3647.cmsp363-px-6 { 3648padding-left: 1.5rem; 3649padding-right: 1.5rem 3650}
3651.cmsp363-py-3 { 3652padding-top: 0.75rem; 3653padding-bottom: 0.75rem 3654} 3655.cmsp363-text-center { 3656text-align: center 3657} 3658.cmsp363-text-2xl { 3659font-size: 1.5rem; 3660line-height: 2rem 3661} 3662.cmsp363-font-bold { 3663font-weight: 700 3664} 3665.cmsp363-text-gray-600 { 3666--tw-text-opacity: 1; 3667color: rgb(75 85 99 / var(--tw-text-opacity)) 3668} 3669.cmsp363-text-gray-900 { 3670--tw-text-opacity: 1; 3671color: rgb(17 24 39 / var(--tw-text-opacity)) 3672} 3673.cmsp363-text-white { 3674--tw-text-opacity: 1; 3675color: rgb(255 255 255 / var(--tw-text-opacity)) 3676} 3677.cmsp363-underline { 3678text-decoration-line: underline 3679} 3680.cmsp363-no-underline { 3681text-decoration-line: none 3682} 3683.cmsp363-blur { 3684--tw-blur: blur(8px); 3685filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) 3686} 3687.cmsp363-transition { 3688transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 3689transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 3690transition-duration: 150ms 3691} 3692.cmsp363-transition-transform { 3693transition-property: transform; 3694transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 3695transition-duration: 150ms 3696} 3697.cmsp363-duration-500 { 3698transition-duration: 500ms 3699} 3700.cmsp363-hover\:scale-110:hover { 3701--tw-scale-x: 1.1; 3702--tw-scale-y: 1.1; 3703transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) 3704} 3705.cmsp363-hover\:bg-red-700:hover { 3706--tw-bg-opacity: 1; 3707background-color: rgb(185 28 28 / var(--tw-bg-opacity)) 3708} 3709.cmsp363-hover\:bg-opacity-80:hover { 3710--tw-bg-opacity: 0.8 3711} 3712@media (min-width: 768px) { 3713.cmsp363-md\:grid-cols-4 { 3714grid-template-columns: repeat(4, minmax(0, 1fr)) 3715} 3716}</style> 3717<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><style> 3718 .amslider-header-container { 3719 display: none; 3720 } 3721 3722 .ambrands-inner { 3723 background-color: #fff; 3724 } 3725 3726 .amasty-contain.post-item-details.equal-two h4 { 3727 color: #000000; 3728 font-weight: 700; 3729 font-size: 18px; 3730 line-height: 26px; 3731 margin-bottom: 8px; 3732 margin-top: 12px; 3733 } 3734 3735 .amasty-contain.post-item-details.equal-two { 3736 padding: 0.75rem 1rem; 3737 border-width: 1px; 3738 border-style: solid; 3739 border-color: rgb(222, 222, 222); 3740 border-image: initial; 3741 } 3742 3743 a.post-title.amblog-headline { 3744 text-decoration: underline; 3745 } 3746 3747 a.post-title.amblog-headline:hover { 3748 color: #DC2626; 3749 } 3750 3751 .breadcrumbs { 3752 display: none; 3753 } 3754 3755 /* Slider bullet points styling */ 3756 .slider-dots { 3757 display: flex; 3758 justify-content: center; 3759 margin-top: 10px; 3760 } 3761 3762 .dot { 3763 width: 12px; 3764 height: 12px; 3765 border-radius: 50%; 3766 background-color: #bbbbbb; 3767 margin: 0 5px; 3768 cursor: pointer; 3769 transition: background-color 0.3s ease; 3770 } 3771 3772 .dot.active { 3773 background-color: #B81C1D; 3774 } 3775</style> 3776 3777<!-- Preload LCP images with responsive support --> 3778<link rel="preload" as="image" href="https://www.studio-88.co.za/media/slider/Desktop_25years.webp" media="(min-width: 768px)" 3779 fetchpriority="high" /> 3780<link rel="preload" as="image" href="https://www.studio-88.co.za/media/slider/Mobile_25years.webp" media="(max-width: 767px)" 3781 fetchpriority="high" /> 3782 3783<!-- Slider --> 3784<div class="cmsp363-relative cmsp363-w-full cmsp363-overflow-hidden"> 3785 <button role="button" 3786 class="cmsp363-absolute cmsp363-z-10 cmsp363-top-1/2 cmsp363-left-4 cmsp363-transform cmsp363--translate-y-1/2 cmsp363-bg-black cmsp363-bg-opacity-50 cmsp363-text-white cmsp363-p-3 cmsp363-rounded-md cmsp363-hover:bg-opacity-80" 3787 aria-label="Previous slide" id="prev">â®</button> 3788 <div class="cmsp363-flex cmsp363-transition-transform duration-2000" id="slider" role="region" aria-label="Image slider"> 3789 3790 3791 <!-- First slide (LCP candidate) with responsive images --> 3792 <a href="https://www.studio-88.co.za/sale2" 3793 class="cmsp363-w-full cmsp363-flex-shrink-0"> 3794 <picture class="cmsp363-w-full cmsp363-flex-shrink-0"> 3795 <source media="(min-width: 768px)" srcset="https://www.studio-88.co.za/media/slider/Desktop_25years.webp" 3796 type="image/webp"> 3797 <source media="(max-width: 767px)" srcset="https://www.studio-88.co.za/media/slider/Mobile_25years.webp" 3798 type="image/webp"> 3799 <img loading="lazy" class="cmsp363-w-full cmsp363-object-cover" 3800 src="https://www.studio-88.co.za/media/slider/Mobile_25years.webp" alt="Studio 88 x 25 years" 3801 decoding="async"> 3802 </picture> 3803 </a> 3804 3805 <a href="puma-karmen-ii-idols-charm-sneakers" 3806 class="cmsp363-w-full cmsp363-flex-shrink-0"> 3807 <picture class="cmsp363-w-full cmsp363-flex-shrink-0"> 3808 <source media="(min-width: 768px)" srcset="https://www.studio-88.co.za/media/slider/Puma_Karmen_II_Idol_Desktop.webp" 3809 type="image/webp"> 3810 <source media="(max-width: 767px)" srcset="https://www.studio-88.co.za/media/slider/Puma_Karmen_II_Idol_Mobile.webp" 3811 type="image/webp"> 3812 <img loading="lazy" class="cmsp363-w-full cmsp363-object-cover" 3813 src="https://www.studio-88.co.za/media/slider/Puma_Karmen_II_Idol_Mobile.webp" alt="Puma Karmen II Idol Sneakers" 3814 decoding="async"> 3815 </picture> 3816 </a> 3817 3818 <a href="https://www.studio-88.co.za/nike-air-force-1-07-mens-sneaker-white" 3819 class="cmsp363-w-full cmsp363-flex-shrink-0"> 3820 <picture class="cmsp363-w-full cmsp363-flex-shrink-0"> 3821 <source media="(min-width: 768px)" srcset="https://www.studio-88.co.za/media/slider/Desktop_Air_Force.webp" 3822 type="image/webp"> 3823 <source media="(max-width: 767px)" srcset="https://www.studio-88.co.za/media/slider/Mobile_Air_Force.webp" 3824 type="image/webp"> 3825 <img loading="lazy" class="cmsp363-w-full cmsp363-object-cover" 3826 src="https://www.studio-88.co.za/media/slider/Mobile_Air_Force.webp" alt="Nike Air Force Sneakers" 3827 decoding="async"> 3828 </picture> 3829 </a> 3830
3831 <a href="https://www.studio-88.co.za/adidas-performance-f50-messi-club-fg-mg-soccer-boots-mens-red-ruby" 3832 class="cmsp363-w-full cmsp363-flex-shrink-0"> 3833 <picture class="cmsp363-w-full cmsp363-flex-shrink-0"> 3834 <source media="(min-width: 768px)" srcset="https://www.studio-88.co.za/media/slider/Desktop_adidasF50Messi.webp" 3835 type="image/webp"> 3836 <source media="(max-width: 767px)" srcset="https://www.studio-88.co.za/media/slider/Mobile_adidasF50Messi.webp" 3837 type="image/webp"> 3838 <img loading="lazy" class="cmsp363-w-full cmsp363-object-cover" 3839 src="https://www.studio-88.co.za/media/slider/Mobile_adidasF50Messi.webp" alt="adidas F50 Soccer Boots" 3840 decoding="async"> 3841 </picture> 3842 </a> 3843 3844 </div> 3845 <!-- Bullet points container --> 3846 <div class="slider-dots cmsp363-absolute cmsp363-bottom-4 cmsp363-left-0 cmsp363-right-0 cmsp363-flex cmsp363-justify-center cmsp363-gap-2" role="navigation" 3847 aria-label="Slide indicators"></div> 3848 <button role="button" 3849 class="cmsp363-absolute cmsp363-z-10 cmsp363-top-1/2 cmsp363-right-4 cmsp363-transform cmsp363--translate-y-1/2 cmsp363-bg-black cmsp363-bg-opacity-50 cmsp363-text-white cmsp363-p-3 cmsp363-rounded-md cmsp363-hover:bg-opacity-80" 3850 aria-label="Next slide" id="next">â¯</button> 3851</div> 3852 3853<!-- Updated CSS for slider --> 3854<style> 3855 #slider { 3856 width: 100%; 3857 display: flex; 3858 transition: transform 2s ease; 3859 } 3860 3861 #slider picture { 3862 min-width: 100%; 3863 flex: 0 0 100%; 3864 } 3865 3866 .slider-dots { 3867 display: flex; 3868 justify-content: center; 3869 margin-top: 10px; 3870 } 3871 3872 .dot { 3873 width: 12px; 3874 height: 12px; 3875 border-radius: 50%; 3876 background-color: #bbbbbb; 3877 margin: 0 5px; 3878 cursor: pointer; 3879 transition: background-color 0.3s ease; 3880 } 3881 3882 .dot.active { 3883 background-color: #B81C1D; 3884 } 3885</style> 3886
3887<script defer> 3888 document.addEventListener("DOMContentLoaded", () => { 3889 let currentIndex = 0; 3890 const slider = document.getElementById("slider"); 3891 const slides = slider.querySelectorAll("picture"); 3892 const totalSlides = slides.length; 3893 const nextButton = document.getElementById("next"); 3894 const prevButton = document.getElementById("prev"); 3895 const dotsContainer = document.querySelector(".slider-dots"); 3896 3897 if (!slider || !slides.length) { 3898 console.error("Slider elements not found"); 3899 return; 3900 } 3901 3902 // Create bullet points 3903 for (let i = 0; i < totalSlides; i++) { 3904 const dot = document.createElement("div"); 3905 dot.classList.add("dot"); 3906 if (i === 0) dot.classList.add("active"); 3907 dot.setAttribute("role", "button"); 3908 dot.setAttribute("aria-label", `Go to slide ${i + 1}`); 3909 // Reset autoplay when navigating via dot 3910 dot.addEventListener("click", () => { 3911 showSlide(i); 3912 startAutoplay(); 3913 }); 3914 dotsContainer.appendChild(dot); 3915 } 3916 3917 const dots = document.querySelectorAll(".dot"); 3918 3919 function showSlide(index) { 3920 // Ensure index is within bounds 3921 currentIndex = ((index % totalSlides) + totalSlides) % totalSlides; 3922 3923 // Apply transform to move slider 3924 slider.style.transform = `translateX(-${currentIndex * 100}%)`; 3925 3926 // Update active dot 3927 dots.forEach((dot, i) => { 3928 dot.classList.toggle("active", i === currentIndex); 3929 dot.setAttribute("aria-selected", i === currentIndex ? "true" : "false"); 3930 }); 3931 3932 // Update ARIA live region 3933 slider.setAttribute("aria-live", "polite"); 3934 } 3935 3936 // --- Autoplay with resettable timer (prevents "skip" after manual navigation) --- 3937 const AUTOPLAY_DELAY = 5000; 3938 let autoTimerId = null; 3939 3940 function stopAutoplay() { 3941 if (autoTimerId !== null) { 3942 clearTimeout(autoTimerId); 3943 autoTimerId = null; 3944 } 3945 } 3946 3947 function startAutoplay() { 3948 stopAutoplay(); 3949 autoTimerId = setTimeout(tick, AUTOPLAY_DELAY); 3950 } 3951 3952 function tick() { 3953 showSlide(currentIndex + 1); 3954 startAutoplay(); // schedule next tick 3955 } 3956 // ------------------------------------------------------------------------------ 3957 3958 // Add event listeners to buttons 3959 if (nextButton) { 3960 nextButton.addEventListener("click", () => { 3961 showSlide(currentIndex + 1); 3962 startAutoplay(); // reset timer on manual nav 3963 }); 3964 } 3965 3966 if (prevButton) { 3967 prevButton.addEventListener("click", () => { 3968 showSlide(currentIndex - 1); 3969 startAutoplay(); // reset timer on manual nav 3970 }); 3971 } 3972 3973 // Start autoplay initially 3974 startAutoplay(); 3975 3976 // Pause autoplay on hover or focus 3977 slider.addEventListener("mouseenter", () => stopAutoplay()); 3978 slider.addEventListener("focus", () => stopAutoplay(), true); 3979 3980 slider.addEventListener("mouseleave", () => { 3981 startAutoplay(); 3982 }); 3983 3984 slider.addEventListener("blur", () => { 3985 startAutoplay(); 3986 }, true); 3987 3988 // Also pause when tab is hidden, resume when visible 3989 document.addEventListener("visibilitychange", () => { 3990 if (document.hidden) { 3991 stopAutoplay(); 3992 } else { 3993 startAutoplay(); 3994 } 3995 }); 3996 3997 // Add keyboard navigation (reset autoplay) 3998 document.addEventListener("keydown", (e) => { 3999 if (e.key === "ArrowLeft") { 4000 showSlide(currentIndex - 1); 4001 startAutoplay(); 4002 } 4003 if (e.key === "ArrowRight") { 4004 showSlide(currentIndex + 1); 4005 startAutoplay(); 4006 } 4007 }); 4008 4009 // Add touch support 4010 let touchStartX = 0; 4011 let touchEndX = 0; 4012 4013 slider.addEventListener("touchstart", (e) => { 4014 touchStartX = e.changedTouches[0].screenX; 4015 stopAutoplay(); // Pause auto-rotation on touch 4016 }, { passive: true });
4017 4018 slider.addEventListener("touchend", (e) => { 4019 touchEndX = e.changedTouches[0].screenX; 4020 handleSwipe(); 4021 // Resume auto-rotation after touch 4022 startAutoplay(); 4023 }, { passive: true }); 4024 4025 function handleSwipe() { 4026 if (touchStartX - touchEndX > 50) { 4027 showSlide(currentIndex + 1); // Swipe left 4028 } else if (touchEndX - touchStartX > 50) { 4029 showSlide(currentIndex - 1); // Swipe right 4030 } 4031 } 4032 }); 4033</script>
4033 4034 4035<div class="cmsp363-max-w-screen-2xl cmsp363-mx-auto"> 4036 4037 <div class="widget block block-static-block"> 4038 <style>.cmsb463-container { 4039width: 100% 4040} 4041@media (min-width: 640px) { 4042.cmsb463-container { 4043max-width: 640px 4044} 4045} 4046@media (min-width: 768px) { 4047.cmsb463-container { 4048max-width: 768px 4049} 4050} 4051@media (min-width: 1024px) { 4052.cmsb463-container { 4053max-width: 1024px 4054} 4055} 4056@media (min-width: 1280px) { 4057.cmsb463-container { 4058max-width: 1280px 4059} 4060} 4061@media (min-width: 1536px) { 4062.cmsb463-container { 4063max-width: 1536px 4064} 4065} 4066.cmsb463-absolute { 4067position: absolute 4068} 4069.cmsb463-relative { 4070position: relative 4071} 4072.cmsb463-left-2 { 4073left: 0.5rem 4074} 4075.cmsb463-right-2 { 4076right: 0.5rem 4077} 4078.cmsb463-top-1\/2 { 4079top: 50% 4080} 4081.cmsb463-z-10 { 4082z-index: 10 4083} 4084.cmsb463-mx-auto { 4085margin-left: auto; 4086margin-right: auto 4087} 4088.cmsb463-mt-4 { 4089margin-top: 1rem 4090} 4091.cmsb463-flex { 4092display: flex 4093} 4094.cmsb463-h-24 { 4095height: 6rem 4096} 4097.cmsb463-h-5 { 4098height: 1.25rem 4099} 4100.cmsb463-w-5 { 4101width: 1.25rem 4102} 4103.cmsb463-flex-shrink-0 { 4104flex-shrink: 0 4105} 4106.cmsb463--translate-y-1\/2 { 4107--tw-translate-y: -50%; 4108transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) 4109} 4110.cmsb463-transform { 4111transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) 4112} 4113.cmsb463-cursor-grab { 4114cursor: grab 4115} 4116.cmsb463-cursor-pointer { 4117cursor: pointer 4118} 4119.cmsb463-resize { 4120resize: both 4121} 4122.cmsb463-justify-center { 4123justify-content: center 4124} 4125.cmsb463-overflow-hidden { 4126overflow: hidden 4127} 4128.cmsb463-rounded-full { 4129border-radius: 9999px 4130} 4131.cmsb463-bg-white\/90 { 4132background-color: rgb(255 255 255 / 0.9) 4133} 4134.cmsb463-object-contain { 4135object-fit: contain 4136} 4137.cmsb463-p-2 { 4138padding: 0.5rem 4139} 4140.cmsb463-px-2 { 4141padding-left: 0.5rem; 4142padding-right: 0.5rem 4143} 4144.cmsb463-px-4 { 4145padding-left: 1rem; 4146padding-right: 1rem 4147} 4148.cmsb463-py-8 { 4149padding-top: 2rem; 4150padding-bottom: 2rem 4151} 4152.cmsb463-text-gray-800 { 4153--tw-text-opacity: 1; 4154color: rgb(31 41 55 / var(--tw-text-opacity)) 4155} 4156.cmsb463-shadow-md { 4157--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
4158--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); 4159box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) 4160} 4161.cmsb463-transition { 4162transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 4163transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 4164transition-duration: 150ms 4165} 4166.cmsb463-transition-all { 4167transition-property: all; 4168transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 4169transition-duration: 150ms 4170} 4171.cmsb463-transition-transform { 4172transition-property: transform; 4173transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 4174transition-duration: 150ms 4175} 4176.cmsb463-duration-200 { 4177transition-duration: 200ms 4178} 4179.cmsb463-duration-500 { 4180transition-duration: 500ms 4181} 4182.cmsb463-ease-in-out { 4183transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) 4184} 4185.cmsb463-hover\:scale-110:hover { 4186--tw-scale-x: 1.1; 4187--tw-scale-y: 1.1; 4188transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) 4189} 4190.cmsb463-hover\:bg-white:hover { 4191--tw-bg-opacity: 1; 4192background-color: rgb(255 255 255 / var(--tw-bg-opacity)) 4193} 4194.cmsb463-active\:cursor-grabbing:active { 4195cursor: grabbing 4196} 4197.cmsb463-disabled\:cursor-not-allowed:disabled {
4198cursor: not-allowed 4199} 4200.cmsb463-disabled\:opacity-50:disabled { 4201opacity: 0.5 4202} 4203@media (min-width: 640px) { 4204.cmsb463-sm\:px-4 { 4205padding-left: 1rem; 4206padding-right: 1rem 4207} 4208} 4209@media (min-width: 768px) { 4210.cmsb463-md\:h-32 { 4211height: 8rem 4212} 4213}</style> 4214<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><div> 4215 <div class="cmsb463-container cmsb463-mx-auto cmsb463-px-4 cmsb463-py-8"> 4216 4217 <div class="cmsb463-relative cmsb463-overflow-hidden"> 4218 4219 <!-- Left Arrow --> 4220 <button id="prevBtn" class="cmsb463-absolute cmsb463-left-2 cmsb463-top-1/2 cmsb463--translate-y-1/2 cmsb463-z-10 cmsb463-bg-white/90 cmsb463-hover:bg-white cmsb463-rounded-full cmsb463-p-2 cmsb463-shadow-md cmsb463-transition-all cmsb463-duration-200 cmsb463-hover:scale-110 cmsb463-disabled:opacity-50 cmsb463-disabled:cursor-not-allowed"> 4221 <svg class="cmsb463-w-5 cmsb463-h-5 cmsb463-text-gray-800" fill="none" stroke="currentColor" viewbox="0 0 24 24"> 4222 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path> 4223 </svg> 4224 </button> 4225 4226 <!-- Right Arrow --> 4227 <button id="nextBtn" class="cmsb463-absolute cmsb463-right-2 cmsb463-top-1/2 cmsb463--translate-y-1/2 cmsb463-z-10 cmsb463-bg-white/90 cmsb463-hover:bg-white cmsb463-rounded-full cmsb463-p-2 cmsb463-shadow-md cmsb463-transition-all cmsb463-duration-200 cmsb463-hover:scale-110 cmsb463-disabled:opacity-50 cmsb463-disabled:cursor-not-allowed"> 4228 <svg class="cmsb463-w-5 cmsb463-h-5 cmsb463-text-gray-800" fill="none" stroke="currentColor" viewbox="0 0 24 24"> 4229 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path> 4230 </svg> 4231 </button> 4232 4233 <div id="sliderTrack" class="cmsb463-flex cmsb463-transition-transform cmsb463-duration-500 cmsb463-ease-in-out cmsb463-cursor-grab cmsb463-active:cursor-grabbing"> 4234 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/adidasoriginals.webp" data-url="https://www.studio-88.co.za/adidas-original/" alt="adidas original"> 4235 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/adidasperfomance.webp" data-url="https://www.studio-88.co.za/adidas-performance/" alt="adidas performance"> 4236 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/%C3%A0lpha.webp" data-url="https://www.studio-88.co.za/alpha-industries/" alt="alpha industries"> 4237 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/converse.webp" data-url="https://www.studio-88.co.za/converse/" alt="converse"> 4238 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/edhardy.webp" data-url="https://www.studio-88.co.za/ed-hardy/" alt="ed-hardy"> 4239 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/ellesse.webp" data-url="https://www.studio-88.co.za/ellesse/" alt="ellesse"> 4240 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/hi-tec.webp" data-url="https://www.studio-88.co.za/hi-tec/" alt="hi-tec"> 4241 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/jockey.webp" data-url="https://www.studio-88.co.za/jockey/" alt="Jockey"> 4242 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/kappa.webp" data-url="https://www.studio-88.co.za/kappa/" alt="Kappa"> 4243 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/levis.webp" data-url="https://www.studio-88.co.za/levis/" alt="levis"> 4244 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/nauticspirit.webp" data-url="https://www.studio-88.co.za/nautic-spirit/" alt="Nautic Spirit"> 4245 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/newbalance.webp" data-url="https://www.studio-88.co.za/new-balance/" alt="new balance"> 4246 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/nike.webp" data-url="https://www.studio-88.co.za/nike/" alt="nike"> 4247 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/playboy.webp" data-url="https://www.studio-88.co.za/playboy/" alt="playboy"> 4248 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/puma.webp" data-url="https://www.studio-88.co.za/puma/" alt="puma"> 4249 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/siksilk.webp" data-url="https://www.studio-88.co.za/siksilk/" alt="Siksilk"> 4250 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/skhandaworld.webp" data-url="https://www.studio-88.co.za/skhanda-world/" alt="skhandaworld"> 4251 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/superga.webp" data-url="https://www.studio-88.co.za/superga/" alt="Superga"> 4252 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/thenorthface.webp" data-url="https://www.studio-88.co.za/the-north-face/" alt="the north face"> 4253 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/vans.webp" data-url="https://www.studio-88.co.za/vans/" alt="vans"> 4254 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/volkswagen.webp" data-url="https://www.studio-88.co.za/volkswagen/" alt="volkswagen"> 4255 <img class="w-1/8 cmsb463-flex-shrink-0 cmsb463-h-24 cmsb463-md:h-32 cmsb463-object-contain cmsb463-px-2 cmsb463-sm:px-4 cmsb463-cursor-pointer" src="https://www.studio-88.co.za/media/brands/vondutch.webp" data-url="https://www.studio-88.co.za/von-dutch/" alt="Von Dutch"> 4256 </div> 4257 4258 4259 <div id="dotsContainer" class="cmsb463-flex cmsb463-justify-center cmsb463-mt-4"> 4260 </div> 4261 </div> 4262 4263 </div> 4264</div> 4265
4266<script> 4267 document.addEventListener('DOMContentLoaded', () => { 4268 const sliderTrack = document.getElementById('sliderTrack'); 4269 const dotsContainer = document.getElementById('dotsContainer'); 4270 const prevBtn = document.getElementById('prevBtn'); 4271 const nextBtn = document.getElementById('nextBtn'); 4272 const slides = sliderTrack.querySelectorAll('img'); 4273 4274 let totalSlides = slides.length; 4275 let currentIndex = 0; 4276 let perView = 1; 4277 let maxIndex = 0; 4278 let autoSlideInterval; 4279 4280 // Drag state 4281 let isDragging = false; 4282 let startX = 0; 4283 let startTranslateX = 0; 4284 let currentTranslateX = 0; 4285 let didDrag = false; 4286 4287 // Prevent default image drag 4288 slides.forEach((slide) => { 4289 slide.addEventListener('dragstart', (e) => e.preventDefault()); 4290 }); 4291 4292 function getViewportEl() { 4293 return sliderTrack.parentElement; 4294 } 4295 4296 function getSlideWidth() { 4297 return slides[0] ? slides[0].offsetWidth : 0; 4298 } 4299 4300 function recalc() { 4301 totalSlides = slides.length; 4302 const viewportWidth = getViewportEl().clientWidth; 4303 const slideWidth = getSlideWidth() || 1; 4304 perView = Math.max(1, Math.floor(viewportWidth / slideWidth)); 4305 maxIndex = Math.max(0, totalSlides - perView); 4306 4307 if (currentIndex > maxIndex) { 4308 currentIndex = maxIndex; 4309 } 4310 4311 renderDots(); 4312 goToSlide(currentIndex, false); 4313 } 4314 4315 function renderDots() { 4316 dotsContainer.innerHTML = ''; 4317 for (let i = 0; i <= maxIndex; i++) { 4318 const dot = document.createElement('span'); 4319 dot.classList.add('slider-dot'); 4320 dot.dataset.index = i; 4321 dot.addEventListener('click', () => { 4322 stopAutoSlide(); 4323 goToSlide(i, true); 4324 startAutoSlide(); 4325 }); 4326 dotsContainer.appendChild(dot); 4327 } 4328 updateDots(); 4329 } 4330 4331 function updateDots() { 4332 const dots = dotsContainer.querySelectorAll('.slider-dot'); 4333 dots.forEach((dot, index) => { 4334 if (index === currentIndex) { 4335 dot.classList.add('active'); 4336 } else { 4337 dot.classList.remove('active'); 4338 } 4339 }); 4340 } 4341 4342 function updateArrows() { 4343 // Disable left arrow at start 4344 prevBtn.disabled = currentIndex === 0; 4345 // Disable right arrow at end 4346 nextBtn.disabled = currentIndex >= maxIndex; 4347 } 4348 4349 function goToSlide(index, animate = true) { 4350 currentIndex = Math.max(0, Math.min(index, maxIndex)); 4351 const slideWidth = getSlideWidth(); 4352 const offset = -currentIndex * slideWidth; 4353 4354 if (animate) { 4355 sliderTrack.style.transition = 'transform 0.5s ease-in-out'; 4356 } else { 4357 sliderTrack.style.transition = 'none'; 4358 } 4359 4360 sliderTrack.style.transform = `translateX(${offset}px)`; 4361 currentTranslateX = offset; 4362 updateDots(); 4363 updateArrows(); 4364 } 4365 4366 // Arrow button handlers 4367 prevBtn.addEventListener('click', () => { 4368 stopAutoSlide(); 4369 goToSlide(currentIndex - 1, true); 4370 startAutoSlide(); 4371 }); 4372 4373 nextBtn.addEventListener('click', () => { 4374 stopAutoSlide(); 4375 goToSlide(currentIndex + 1, true); 4376 startAutoSlide(); 4377 }); 4378 4379 // Click handler
4380 slides.forEach(slide => { 4381 slide.addEventListener('click', (e) => { 4382 if (didDrag) { 4383 e.preventDefault(); 4384 return; 4385 } 4386 const url = slide.dataset.url; 4387 if (url) { 4388 window.location.href = url; 4389 } 4390 }); 4391 }); 4392 4393 // Drag start 4394 function onDragStart(clientX) { 4395 isDragging = true; 4396 didDrag = false; 4397 startX = clientX; 4398 startTranslateX = currentTranslateX; 4399 sliderTrack.style.transition = 'none'; 4400 stopAutoSlide(); 4401 } 4402 4403 // Drag move 4404 function onDragMove(clientX) { 4405 if (!isDragging) return; 4406 4407 const deltaX = clientX - startX; 4408 didDrag = Math.abs(deltaX) > 5; 4409 4410 // Calculate new position with bounds 4411 const slideWidth = getSlideWidth(); 4412 const minTranslate = -maxIndex * slideWidth; 4413 const maxTranslate = 0; 4414 4415 let newTranslate = startTranslateX + deltaX; 4416 4417 // Add resistance at edges 4418 if (newTranslate > maxTranslate) { 4419 newTranslate = maxTranslate + (newTranslate - maxTranslate) * 0.3; 4420 } else if (newTranslate < minTranslate) { 4421 newTranslate = minTranslate + (newTranslate - minTranslate) * 0.3; 4422 } 4423 4424 currentTranslateX = newTranslate; 4425 sliderTrack.style.transform = `translateX(${newTranslate}px)`; 4426 } 4427 4428 // Drag end 4429 function onDragEnd() { 4430 if (!isDragging) return; 4431 isDragging = false; 4432 4433 // Calculate which slide we're closest to 4434 const slideWidth = getSlideWidth(); 4435 const movedSlides = -currentTranslateX / slideWidth; 4436 const targetIndex = Math.round(movedSlides); 4437 4438 // Go to nearest slide 4439 goToSlide(targetIndex, true); 4440 4441 // Restart auto-slide after animation 4442 setTimeout(() => { 4443 startAutoSlide(); 4444 }, 500); 4445 } 4446 4447 // Mouse events 4448 sliderTrack.addEventListener('mousedown', (e) => { 4449 onDragStart(e.clientX); 4450 }); 4451 4452 document.addEventListener('mousemove', (e) => { 4453 onDragMove(e.clientX); 4454 }); 4455 4456 document.addEventListener('mouseup', () => { 4457 onDragEnd(); 4458 }); 4459 4460 // Touch events 4461 sliderTrack.addEventListener('touchstart', (e) => { 4462 onDragStart(e.touches[0].clientX); 4463 }, { passive: true }); 4464 4465 sliderTrack.addEventListener('touchmove', (e) => { 4466 if (isDragging) { 4467 e.preventDefault(); 4468 onDragMove(e.touches[0].clientX); 4469 } 4470 }, { passive: false }); 4471 4472 sliderTrack.addEventListener('touchend', () => { 4473 onDragEnd(); 4474 }, { passive: true }); 4475 4476 // Auto-slide 4477 function startAutoSlide() { 4478 stopAutoSlide(); 4479 autoSlideInterval = setInterval(() => { 4480 const nextIndex = currentIndex < maxIndex ? currentIndex + 1 : 0; 4481 goToSlide(nextIndex, true); 4482 }, 3000); 4483 } 4484 4485 function stopAutoSlide() { 4486 if (autoSlideInterval) { 4487 clearInterval(autoSlideInterval); 4488 autoSlideInterval = null; 4489 } 4490 } 4491 4492 // Resize handler 4493 let resizeTimeout; 4494 window.addEventListener('resize', () => { 4495 clearTimeout(resizeTimeout); 4496 resizeTimeout = setTimeout(() => { 4497 recalc(); 4498 }, 150); 4499 }); 4500 4501 // Initialize 4502 recalc(); 4503 startAutoSlide(); 4504 }); 4505</script>
4505</div></div> 4506 4507 4508 4509 <h1 class="cmsp363-font-bold cmsp363-text-2xl cmsp363-text-gray-900 cmsp363-w-full cmsp363-my-4 cmsp363-text-center" style="height: 32px;"> 4510 <a href="https://www.studio-88.co.za/new-arrivals" class="cmsp363-text-gray-900 cmsp363-no-underline">NEW ARRIVALS</a> 4511 </h1> 4512 4513 <div> 4514 <section class="product-slider generic-product-slider"> 4515
4515<script> 4516 'use strict'; 4517 4518 function initSliderComponent() { 4519 return { 4520 active: 0, 4521 itemCount: 0, 4522 getSlider() { 4523 return this.$root.querySelector('.js_slides'); 4524 }, 4525 pageSize: 4, 4526 pageFillers: 0, 4527 calcPageSize() { 4528 const slider = this.getSlider(); 4529 if (slider) { 4530 this.itemCount = slider.querySelectorAll('.js_slide').length; 4531 this.pageSize = Math.round(slider.clientWidth / slider.querySelector('.js_slide').clientWidth); 4532 this.pageFillers = ( 4533 this.pageSize * Math.ceil(this.itemCount / this.pageSize) 4534 ) - this.itemCount; 4535 } 4536 }, 4537 calcActive() { 4538 const slider = this.getSlider(); 4539 if (slider) { 4540 const sliderItems = this.itemCount + this.pageFillers; 4541 const calculatedActiveSlide = slider.scrollLeft / (slider.scrollWidth / sliderItems); 4542 this.active = Math.round(calculatedActiveSlide / this.pageSize) * this.pageSize; 4543 } 4544 }, 4545 scrollPrevious() { 4546 this.scrollTo(this.active - this.pageSize); 4547 }, 4548 scrollNext() { 4549 this.scrollTo(this.active + this.pageSize); 4550 }, 4551 scrollTo(idx) { 4552 const slider = this.getSlider(); 4553 if (slider) { 4554 const slideWidth = slider.scrollWidth / (this.itemCount + this.pageFillers); 4555 slider.scrollLeft = Math.floor(slideWidth) * idx; 4556 this.active = idx; 4557 } 4558 }, 4559 skipCarouselToNavigation(navSelector) { 4560 const element = document.getElementById(navSelector) 4561 if (element) { 4562 element.scrollIntoView({behavior: 'smooth', block: 'end'}); 4563 const button = element.querySelector('button:not([disabled])'); 4564 this.$nextTick(() => button && button.focus({preventScroll: true})) 4565 } 4566 } 4567 } 4568 } 4569</script>
4569 4570<div class="mb-5 text-gray-700 body-font" x-data="initSliderComponent()" x-init="calcPageSize();" x-id="['slider-nav', 'slider-desc', 'slider-id']" @resize.window.debounce="calcPageSize(); $nextTick( function() { calcActive() })" role="group" aria-roledescription="Carousel" aria-label="Carousel " :aria-describedby="$id('slider-desc')" x-defer="intersect"> 4571 <div class="relative container mx-auto"> 4572 <span class="sr-only" :id="$id('slider-desc')" tabindex="-1"> 4573 Navigating through the elements of the carousel is possible using the tab key. You can skip the carousel or go straight to carousel navigation using the skip links. </span> 4574 4575 <a href="#magento%5Cframework%5Cview%5Celement%5Ctemplate_1-slider-end" class="action skip sr-only focus:not-sr-only focus:absolute focus:z-30 focus:bg-white"> 4576 Press to skip carousel </a> 4577 <button x-show="itemCount > pageSize" type="button" class="action skip sr-only focus:not-sr-only focus:absolute focus:z-30 focus:bg-white" @click.prevent="skipCarouselToNavigation($id('slider-nav'))"> 4578 Press to go to carousel navigation </button> 4579 <div class="flex-none relative w-full overflow-x-hidden"> 4580 <div class="relative flex flex-row w-full overflow-auto js_slides snap" @scroll.debounce="calcActive"> 4581 <div class="js_slide flex flex-col shrink-0 w-1/2 mr-2 py-1 lg:w-1/3 xl:w-1/4" role="group" :aria-hidden="1 > active && 1 <= (active + pageSize) ? 'false' : 'true'" aria-label="Item 1" :aria-describedby="`slide-desc-371457-${$id('slider-id')}`"> 4582 4583<form method="post" action="https://www.studio-88.co.za/checkout/cart/add/uenc/%25uenc%25/product/371457/" class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full "> 4584 <input name="form_key" type="hidden" value="jQaPIgQq8BM99Mzs"> <input type="hidden" name="product" value="371457"> 4585 <a href="https://www.studio-88.co.za/puma-shuffle-downtown-low-sneaker-youth-white-royal" title="Puma Shuffle Downtown Low Sneaker Youth White/Royal" class="product photo product-item-photo block mx-auto mb-3 " tabindex="-1"> 4586 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-371457.window="$root.src = $event.detail" src="https://www.studio-88.co.za/media/catalog/product/cache/bd5b7e5d15290d4083c2c8fbcfc68244/p/m/pma7657yry-puma-shuffle-downtown-40373911-v1_jpg.jpg" loading="lazy" width="360" height="360" alt="PMA7657YRY-PUMA-SHUFFLE-DOWNTOWN-40373911-V1" title="PMA7657YRY-PUMA-SHUFFLE-DOWNTOWN-40373911-V1"> 4587 </a> 4588 <div class="product-info equal flex flex-col grow"> 4589 <div class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 4590 <a class="product-item-link" href="https://www.studio-88.co.za/puma-shuffle-downtown-low-sneaker-youth-white-royal" :id="`slide-desc-371457-${$id('slider-id')}`"> 4591 Puma Shuffle Downtown Low Sneaker Youth White/Royal </a> 4592 </div> 4593 4594 <div class="py-1 mx-auto "> 4595 </div> 4596 4597 4598
4598<script> 4599 function initConfigurableSwatchOptions_371457() { 4600 const configurableOptionsComponent = initConfigurableOptions( 4601 '371457', 4602 {"attributes":{"170":{"id":"170","code":"size","label":"Size","options":[{"id":"122","label":"Size 3","products":["370790"]},{"id":"123","label":"Size 4","products":["370791"]},{"id":"124","label":"Size 5","products":["370792"]},{"id":"125","label":"Size 6","products":["370793"]}],"position":"0"}},"template":"R \u00a0<%- data.price %>","currencyFormat":"R \u00a0%s","optionPrices":{"370790":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99},"tierPrices":[],"msrpPrice":{"amount":0}},"370791":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99},"tierPrices":[],"msrpPrice":{"amount":0}},"370792":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99},"tierPrices":[],"msrpPrice":{"amount":0}},"370793":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99},"tierPrices":[],"msrpPrice":{"amount":0}}},"priceFormat":{"pattern":"R \u00a0%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"prices":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99}},"productId":"371457","chooseText":"Choose an Option...","images":[],"index":{"370790":{"170":"122"},"370791":{"170":"123"},"370792":{"170":"124"},"370793":{"170":"125"}},"salable":[],"canDisplayShowOutOfStockStatus":false,"channel":"website","salesChannelCode":"base","sku":{"370790":"PMA7657YRY_030","370791":"PMA7657YRY_040","370792":"PMA7657YRY_050","370793":"PMA7657YRY_060"},"label_reload":"https:\/\/www.studio-88.co.za\/amasty_label\/ajax\/label\/","label_category":".product-image-container, .product-item-photo, .cdz-product-top","label_product":".fotorama__stage, #amasty-main-container","original_product_id":"371457","amastyAdditionalConfig":{"isShowDefaultStatus":true,"outOfStockVisibility":1,"isProductPage":false}} ); 4603 const swatchOptionsComponent = initSwatchOptions({"170":{"122":{"type":"0","value":"Size 3","label":"Size 3"},"123":{"type":"0","value":"Size 4","label":"Size 4"},"124":{"type":"0","value":"Size 5","label":"Size 5"},"125":{"type":"0","value":"Size 6","label":"Size 6"},"additional_data":"{\"swatch_input_type\":\"text\",\"update_product_preview_image\":\"0\",\"use_product_image_for_swatch\":0}"}}); 4604 4605 return Object.assign( 4606 configurableOptionsComponent, 4607 swatchOptionsComponent, 4608 { 4609 mediaCallback: "https\u003A\u002F\u002Fwww.studio\u002D88.co.za\u002Fswatches\u002Fajax\u002Fmedia\u002F", 4610 changeOption(optionId, value, skipUpdateGallery) { 4611 if (value === '') { 4612 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, optionId) 4613 } else { 4614 this.selectedValues[optionId] = value; 4615 } 4616 this.findSimpleIndex(); 4617 this.findAllowedAttributeOptions(); 4618 this.updatePrices(); 4619 !skipUpdateGallery && this.updateGallery(); 4620 window.dispatchEvent(new CustomEvent('listing-configurable-selection-changed', { 4621 detail: { 4622 productId: this.productId, 4623 optionId, 4624 value, 4625 productIndex: this.productIndex, 4626 selectedValues: this.selectedValues, 4627 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 4628 } 4629 })); 4630 }, 4631 updateGallery() { 4632 if (!this.productIndex) { 4633 return; 4634 } 4635 4636 fetch(`${this.mediaCallback}?product_id=${this.productIndex}&isAjax=true`, { 4637 method: 'GET', 4638 headers: { 4639 'Content-Type': 'application/json', 4640 'X-Requested-With': 'XMLHttpRequest' 4641 } 4642 } 4643 ).then(response => { 4644 return response.json() 4645 } 4646 ).then(data => { 4647 if (data.errors) { 4648 // non critical failure only console logged 4649 console.warn(data.errors); 4650 } else { 4651 const image = data && data.medium; 4652 4653 image && window.dispatchEvent( 4654 new CustomEvent( 4655 "update-gallery-371457", 4656 {detail: image} 4657 ) 4658 ); 4659 } 4660 }).catch(error => { 4661 console.warn(error) 4662 }); 4663 }, 4664 preselectQuerystringItems() { 4665 // pre-select option like ?size=167 4666 const urlQueryParams = new URLSearchParams(window.location.search.replace('?', '')); 4667 Object.values(this.optionConfig.attributes).map(attribute => { 4668 // Don't update images on load, since PLPs already set the main image to the selected options 4669 const skipUpdateGallery = true; 4670 urlQueryParams.get(attribute.code) && 4671 this.changeOption(attribute.id, urlQueryParams.get(attribute.code), skipUpdateGallery); 4672 }); 4673 }, 4674 mouseDown: false, 4675 startX: 0, 4676 maxScroll: 0, 4677 scrollLeft: null, 4678 slider: null, 4679 scrollEvents: { 4680 ['@mousedown'](e) { 4681 this.slider = e.target.closest('.snap'); 4682 if (!this.slider) { 4683 return; 4684 } 4685 this.maxScroll = this.slider.scrollWidth - this.slider.offsetWidth; 4686 this.startX = e.pageX - this.slider.offsetLeft; 4687 this.scrollLeft = this.slider.scrollLeft; 4688 this.mouseDown = true; 4689 }, 4690 ['@mouseout.self']() { 4691 this.mouseDown = false;
4692 }, 4693 ['@mouseup']() { 4694 this.mouseDown = false; 4695 }, 4696 ['@mousemove'](e) { 4697 e.preventDefault(); 4698 if (!this.mouseDown) { 4699 return; 4700 } 4701 const x = e.pageX - this.slider.offsetLeft; 4702 const scroll = x - this.startX; 4703 const scrollLeft = this.scrollLeft - scroll; 4704 4705 if (scrollLeft > this.maxScroll) { 4706 this.slider.scrollLeft = this.maxScroll; 4707 return 4708 } 4709 this.slider.scrollLeft = this.scrollLeft - scroll; 4710 }, 4711 ['@onselectstart']() { 4712 return false; 4713 } 4714 }, 4715 resizeEvent() { 4716 Array.from(this.$root.querySelectorAll('.snap')).forEach(sli
4716der => { 4717 slider.scrollLeft = 0; 4718 }) 4719 } 4720 } 4721 ); 4722 } 4723 4724 </script>
4724 4725 4726<div x-data="initConfigurableSwatchOptions_371457()" x-defer="intersect" x-init="findAllowedAttributeOptions(); initShowSwatchesIntersect();" @private-content-loaded.window="onGetCartData($event.detail.data)" @resize.window="resizeEvent()" class="mb-2 relative"> 4727 <div> 4728 4729 <div class="swatch-attribute border-t last:border-b border-container 4730 size"> 4731 <div class="w-full overflow-x-hidden swatch-attribute-options min-h-14"> 4732 <template x-if="showSwatches"> 4733 <div class="flex flex-nowrap w-full overflow-auto snap items-center py-1" id="attribute-label-371457-170" role="radiogroup" x-bind="scrollEvents" aria-label="Size"> 4734 <template x-for="(item, index) in optionConfig.attributes[170].options" :key="item.id"> 4735 4736<div x-id="['attribute-option-371457-'+item.id]"> 4737 <template x-if="optionIsEnabled(170, item.id) && optionIsActive(170, item.id)"> 4738 <label :for="$id('attribute-option-371457-'+item.id)" class="swatch-option relative shadow-sm cursor-pointer select-none bg-container-lighter product-option-value-label" :class="{ 4739 'border-container-lighter ring ring-primary/50': 4740 (selectedValues[170] === item.id), 4741 'border-container-darker': 4742 (selectedValues[170] !== item.id), 4743 'w-6 h-6' : !isTextSwatch(170, item.id), 4744 'border-container-lighter ring ring-primary/75' : focusedLabel === item.id 4745 }" :style="getSwatchBackgroundStyle('170',item.id)" @mouseenter.self="activeTooltipItem = { 4746 attribute: '170', 4747 item: item.id, 4748 index 4749 }; tooltipPositionElement = $event.target;
4749" @mouseleave.self="activeTooltipItem = false"> 4750 <input :id="$id('attribute-option-371457-'+item.id)" :value="item.id" name="super_attribute[170]" type="radio" class="inline-block absolute p-0 border-0 focus:border-0 focus:ring-0 product-option-value-input" style="z-index:-1" x-on:focus="focusLabel(item.id)" x-on:blur="blurLabel()" x-on:change="changeOption(170, $event.target.value)" x-on:click="clearOptionIfActive(170, item.id)" x-model="selectedValues[170]" :required="getAllowedAttributeOptions(170).filter( 4751 attributeOption => selectedValues[attributeOption] 4752 ).length === 0" :aria-label="getSwatchText(170, item.id)" aria-describedby="attribute-label-371457-170"> 4753 <template x-if="isTextSwatch(170, item.id)"> 4754 <div x-html="getSwatchText(170, item.id)" class="pointer-events-none select-none whitespace-nowrap" aria-hidden="true"></div> 4755 </template> 4756 </label> 4757 </template> 4758 4759 <template x-if="optionIsEnabled(170, item.id) && !optionIsActive(170, item.id)"> 4760 <div class="relative border-2 shadow-sm opacity-50 cursor-pointer select-none border-container-darker swatch-option bg-container-lighter" :class="{ 4761 'w-6 h-6' : !isTextSwatch(170, item.id), 4762 }" :style="getSwatchBackgroundStyle('170',item.id)"> 4763 <div x-html="getSwatchText(170, item.id)" class="whitespace-nowrap" :class="{ 'sr-only' : !isTextSwatch(170, item.id) }"></div> 4764 <svg class="absolute inset-0 w-full h-full text-gray-500 bg-white/25"> 4765 <line x1="0" y1="100%" x2="100%" y2="0" class="stroke-current stroke-1"></line> 4766 </svg> 4767 </div> 4768 </template> 4769</div> 4770 </template> 4771 </div> 4772 </template> 4773 </div> 4774 </div> 4775 </div> 4776 <template x-if="isTooltipVisible()"> 4777 <div class="absolute pointer-events-none select-none" :style="getTooltipPosition()"> 4778 <div class="shadow-lg"> 4779 <div class="absolute top-0 left-0 z-10 min-w-20 p-2 -mt-6 text-sm leading-tight text-black 4780 transform -translate-x-1/2 -translate-y-full bg-white rounded-lg shadow-lg text-center" :class="{'-translate-x-5' : isFirstItemCol()}"> 4781 <template x-if="isVisualSwatch(activeTooltipItem.attribute, activeTooltipItem.item)"> 4782 <div class="inline-block border shadow-sm border-container" :style="getTooltipImageStyle(activeTooltipItem.attribute, activeTooltipItem.item)"></div> 4783 </template> 4784 <span class="mx-2 text-lg font-semibold whitespace-nowrap subtitle" x-html="getTooltipLabel()"></span> 4785 </div> 4786 <svg class="absolute z-10 w-8 h-8 text-white transform -translate-x-1/5 4787 -translate-y-8 fill-current stroke-current" width="12" height="12"> 4788 <rect x="12" y="-12" width="12" height="12" transform="rotate(45)" class="shadow-xl"></rect> 4789 </svg> 4790 </div> 4791 </div> 4792</template> 4793 </div> 4794 4795 4796 4797 <div class="pt-1 text-gray-900" x-data="initPriceBox()" x-defer="intersect" @update-prices-371457.window="updatePrice($event.detail);"> 4798 <div class="price-box price-final_price" data-role="priceBox" data-product-id="371457" data-price-box="product-id-371457"><div x-data="{ priceIsZero: false }" @update-prices-371457.window="priceIsZero = ($event.detail?.finalPrice?.amount <= 0.0001)"> 4799 <div x-show="!priceIsZero"> 4800 4801 <span class="normal-price "> 4802 <span x-data x-id="['product\u002Dprice\u002D371457']" class="price-container price-final_price tax weee"> 4803 <span :id="$id('product\u002Dprice\u002D371457')" data-price-amount="999.99" data-price-type="finalPrice" class="price-wrapper "><span class="price">R Â 999.99</span></span> 4804 </span> 4805 </span> 4806 4807 4808 </div> 4809</div> 4810</div> </div> 4811 4812 <div class="mt-auto pt-3 flex flex-wrap items-center gap-y-4 gap-x-2 4813 "> 4814 <button class="w-auto btn btn-primary justify-center text-sm mr-auto" title="Add to Cart Puma Shuffle Downtown Low Sneaker Youth White/Royal" aria-label="Add to Cart Puma Shuffle Downtown Low Sneaker Youth White/Royal" data-addto="cart"> 4815 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 4816 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
48160 014 0z"></path> 4817</svg> 4818 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 4819 Add to Cart </span> 4820 </button> 4821 4822 <div class="flex flex-wrap gap-2"> 4823 <button x-data="initWishlist()" x-defer="intersect" @click.prevent="addToWishlist(371457)" aria-label="Add to Wish List Puma Shuffle Downtown Low Sneaker Youth White/Royal" type="button" class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex shrink-0 items-center justify-center text-gray-500 hover:text-red-600" data-addto="wishlist"> 4824 <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" fill="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 4825 <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"></path> 4826</svg> 4827 </button> 4828 </div> 4829 </div> 4830 </div> 4831</form> 4832 </div> 4833 <div class="js_slide flex flex-col shrink-0 w-1/2 mr-2 py-1 lg:w-1/3 xl:w-1/4" role="group" :aria-hidden="2 > active && 2 <= (active + pageSize) ? 'false' : 'true'" aria-label="Item 2" :aria-describedby="`slide-desc-371456-${$id('slider-id')}`"> 4834 4835<form method="post" action="https://www.studio-88.co.za/checkout/cart/add/uenc/%25uenc%25/product/371456/" class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full "> 4836 <input name="form_key" type="hidden" value="jQaPIgQq8BM99Mzs"> <input type="hidden" name="product" value="371456"> 4837 <a href="https://www.studio-88.co.za/nike-swim-breaker-essential-5-brief-lined-volley-shorts-mens-black" title="Nike Swim Breaker Essential 5'' Brief-Lined Volley Shorts Mens Black" class="product photo product-item-photo block mx-auto mb-3 " tabindex="-1"> 4838 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-371456.window="$root.src = $event.detail" src="https://www.studio-88.co.za/media/catalog/product/cache/bd5b7e5d15290d4083c2c8fbcfc68244/n/s/nss05b-nike-sht-volley-black-nessf560-v1_jpg.jpg" loading="lazy" width="360" height="360" alt="NSS05B-NIKE-SHT-VOLLEY-BLACK-NESSF560-V1" title="NSS05B-NIKE-SHT-VOLLEY-BLACK-NESSF560-V1"> 4839 </a> 4840 <div class="product-info equal flex flex-col grow"> 4841 <div class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 4842 <a class="product-item-link" href="https://www.studio-88.co.za/nike-swim-breaker-essential-5-brief-lined-volley-shorts-mens-black" :id="`slide-desc-371456-${$id('slider-id')}`"> 4843 Nike Swim Breaker Essential 5'' Brief-Lined Volley Shorts Mens Black </a> 4844 </div> 4845 4846 <div class="py-1 mx-auto "> 4847 </div> 4848 4849 4850
4850<script> 4851 function initConfigurableSwatchOptions_371456() { 4852 const configurableOptionsComponent = initConfigurableOptions( 4853 '371456', 4854 {"attributes":{"170":{"id":"170","code":"size","label":"Size","options":[{"id":"115","label":"Small","products":["371408"]},{"id":"117","label":"Large","products":["371407"]},{"id":"118","label":"Extra Large","products":["371409"]}],"position":"0"}},"template":"R \u00a0<%- data.price %>","currencyFormat":"R \u00a0%s","optionPrices":{"371408":{"baseOldPrice":{"amount":599.99},"oldPrice":{"amount":599.99},"basePrice":{"amount":599.99},"finalPrice":{"amount":599.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371407":{"baseOldPrice":{"amount":599.99},"oldPrice":{"amount":599.99},"basePrice":{"amount":599.99},"finalPrice":{"amount":599.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371409":{"baseOldPrice":{"amount":599.99},"oldPrice":{"amount":599.99},"basePrice":{"amount":599.99},"finalPrice":{"amount":599.99},"tierPrices":[],"msrpPrice":{"amount":0}}},"priceFormat":{"pattern":"R \u00a0%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"prices":{"baseOldPrice":{"amount":599.99},"oldPrice":{"amount":599.99},"basePrice":{"amount":599.99},"finalPrice":{"amount":599.99}},"productId":"371456","chooseText":"Choose an Option...","images":[],"index":{"371408":{"170":"115"},"371407":{"170":"117"},"371409":{"170":"118"}},"salable":[],"canDisplayShowOutOfStockStatus":false,"channel":"website","salesChannelCode":"base","sku":{"371408":"NSS05B__0S","371407":"NSS05B__0L","371409":"NSS05B__XL"},"label_reload":"https:\/\/www.studio-88.co.za\/amasty_label\/ajax\/label\/","label_category":".product-image-container, .product-item-photo, .cdz-product-top","label_product":".fotorama__stage, #amasty-main-container","original_product_id":"371456","amastyAdditionalConfig":{"isShowDefaultStatus":true,"outOfStockVisibility":1,"isProductPage":false}} ); 4855 const swatchOptionsComponent = initSwatchOptions({"170":{"115":{"type":"0","value":"Small","label":"Small"},"117":{"type":"0","value":"Large","label":"Large"},"118":{"type":"0","value":"Extra Large","label":"Extra Large"},"additional_data":"{\"swatch_input_type\":\"text\",\"update_product_preview_image\":\"0\",\"use_product_image_for_swatch\":0}"}}); 4856 4857 return Object.assign( 4858 configurableOptionsComponent, 4859 swatchOptionsComponent, 4860 { 4861 mediaCallback: "https\u003A\u002F\u002Fwww.studio\u002D88.co.za\u002Fswatches\u002Fajax\u002Fmedia\u002F", 4862 changeOption(optionId, value, skipUpdateGallery) { 4863 if (value === '') { 4864 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, optionId) 4865 } else { 4866 this.selectedValues[optionId] = value; 4867 } 4868 this.findSimpleIndex(); 4869 this.findAllowedAttributeOptions(); 4870 this.updatePrices(); 4871 !skipUpdateGallery && this.updateGallery(); 4872 window.dispatchEvent(new CustomEvent('listing-configurable-selection-changed', { 4873 detail: { 4874 productId: this.productId, 4875 optionId, 4876 value, 4877 productIndex: this.productIndex, 4878 selectedValues: this.selectedValues, 4879 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 4880 } 4881 })); 4882 }, 4883 updateGallery() { 4884 if (!this.productIndex) { 4885 return; 4886 } 4887 4888 fetch(`${this.mediaCallback}?product_id=${this.productIndex}&isAjax=true`, { 4889 method: 'GET', 4890 headers: { 4891 'Content-Type': 'application/json', 4892 'X-Requested-With': 'XMLHttpRequest' 4893 } 4894 } 4895 ).then(response => { 4896 return response.json() 4897 } 4898 ).then(data => { 4899 if (data.errors) { 4900 // non critical failure only console logged 4901 console.warn(data.errors); 4902 } else { 4903 const image = data && data.medium; 4904 4905 image && window.dispatchEvent( 4906 new CustomEvent( 4907 "update-gallery-371456", 4908 {detail: image} 4909 ) 4910 ); 4911 } 4912 }).catch(error => { 4913 console.warn(error) 4914 }); 4915 }, 4916 preselectQuerystringItems() { 4917 // pre-select option like ?size=167 4918 const urlQueryParams = new URLSearchParams(window.location.search.replace('?', '')); 4919 Object.values(this.optionConfig.attributes).map(attribute => { 4920 // Don't update images on load, since PLPs already set the main image to the selected options 4921 const skipUpdateGallery = true; 4922 urlQueryParams.get(attribute.code) && 4923 this.changeOption(attribute.id, urlQueryParams.get(attribute.code), skipUpdateGallery); 4924 }); 4925 }, 4926 mouseDown: false, 4927 startX: 0, 4928 maxScroll: 0, 4929 scrollLeft: null, 4930 slider: null, 4931 scrollEvents: { 4932 ['@mousedown'](e) { 4933 this.slider = e.target.closest('.snap'); 4934 if (!this.slider) { 4935 return; 4936 } 4937 this.maxScroll = this.slider.scrollWidth - this.slider.offsetWidth; 4938 this.startX = e.pageX - this.slider.offsetLeft; 4939 this.scrollLeft = this.slider.scrollLeft; 4940 this.mouseDown = true; 4941 }, 4942 ['@mouseout.self']() { 4943 this.mouseDown = false;
4944 }, 4945 ['@mouseup']() { 4946 this.mouseDown = false; 4947 }, 4948 ['@mousemove'](e) { 4949 e.preventDefault(); 4950 if (!this.mouseDown) { 4951 return; 4952 } 4953 const x = e.pageX - this.slider.offsetLeft; 4954 const scroll = x - this.startX; 4955 const scrollLeft = this.scrollLeft - scroll; 4956 4957 if (scrollLeft > this.maxScroll) { 4958 this.slider.scrollLeft = this.maxScroll; 4959 return 4960 } 4961 this.slider.scrollLeft = this.scrollLeft - scroll; 4962 }, 4963 ['@onselectstart']() { 4964 return false; 4965 } 4966 }, 4967 resizeEvent() { 4968 Array.from(this.$root.querySelectorAll('.snap')).forEach(sli
4968der => { 4969 slider.scrollLeft = 0; 4970 }) 4971 } 4972 } 4973 ); 4974 } 4975 4976 </script>
4976 4977 4978<div x-data="initConfigurableSwatchOptions_371456()" x-defer="intersect" x-init="findAllowedAttributeOptions(); initShowSwatchesIntersect();" @private-content-loaded.window="onGetCartData($event.detail.data)" @resize.window="resizeEvent()" class="mb-2 relative"> 4979 <div> 4980 4981 <div class="swatch-attribute border-t last:border-b border-container 4982 size"> 4983 <div class="w-full overflow-x-hidden swatch-attribute-options min-h-14"> 4984 <template x-if="showSwatches"> 4985 <div class="flex flex-nowrap w-full overflow-auto snap items-center py-1" id="attribute-label-371456-170" role="radiogroup" x-bind="scrollEvents" aria-label="Size"> 4986 <template x-for="(item, index) in optionConfig.attributes[170].options" :key="item.id"> 4987 4988<div x-id="['attribute-option-371456-'+item.id]"> 4989 <template x-if="optionIsEnabled(170, item.id) && optionIsActive(170, item.id)"> 4990 <label :for="$id('attribute-option-371456-'+item.id)" class="swatch-option relative shadow-sm cursor-pointer select-none bg-container-lighter product-option-value-label" :class="{ 4991 'border-container-lighter ring ring-primary/50': 4992 (selectedValues[170] === item.id), 4993 'border-container-darker': 4994 (selectedValues[170] !== item.id), 4995 'w-6 h-6' : !isTextSwatch(170, item.id), 4996 'border-container-lighter ring ring-primary/75' : focusedLabel === item.id 4997 }" :style="getSwatchBackgroundStyle('170',item.id)" @mouseenter.self="activeTooltipItem = { 4998 attribute: '170', 4999 item: item.id, 5000 index 5001 }; tooltipPositionElement = $event.target;
5001" @mouseleave.self="activeTooltipItem = false"> 5002 <input :id="$id('attribute-option-371456-'+item.id)" :value="item.id" name="super_attribute[170]" type="radio" class="inline-block absolute p-0 border-0 focus:border-0 focus:ring-0 product-option-value-input" style="z-index:-1" x-on:focus="focusLabel(item.id)" x-on:blur="blurLabel()" x-on:change="changeOption(170, $event.target.value)" x-on:click="clearOptionIfActive(170, item.id)" x-model="selectedValues[170]" :required="getAllowedAttributeOptions(170).filter( 5003 attributeOption => selectedValues[attributeOption] 5004 ).length === 0" :aria-label="getSwatchText(170, item.id)" aria-describedby="attribute-label-371456-170"> 5005 <template x-if="isTextSwatch(170, item.id)"> 5006 <div x-html="getSwatchText(170, item.id)" class="pointer-events-none select-none whitespace-nowrap" aria-hidden="true"></div> 5007 </template> 5008 </label> 5009 </template> 5010 5011 <template x-if="optionIsEnabled(170, item.id) && !optionIsActive(170, item.id)"> 5012 <div class="relative border-2 shadow-sm opacity-50 cursor-pointer select-none border-container-darker swatch-option bg-container-lighter" :class="{ 5013 'w-6 h-6' : !isTextSwatch(170, item.id), 5014 }" :style="getSwatchBackgroundStyle('170',item.id)"> 5015 <div x-html="getSwatchText(170, item.id)" class="whitespace-nowrap" :class="{ 'sr-only' : !isTextSwatch(170, item.id) }"></div> 5016 <svg class="absolute inset-0 w-full h-full text-gray-500 bg-white/25"> 5017 <line x1="0" y1="100%" x2="100%" y2="0" class="stroke-current stroke-1"></line> 5018 </svg> 5019 </div> 5020 </template> 5021</div> 5022 </template> 5023 </div> 5024 </template> 5025 </div> 5026 </div> 5027 </div> 5028 <template x-if="isTooltipVisible()"> 5029 <div class="absolute pointer-events-none select-none" :style="getTooltipPosition()"> 5030 <div class="shadow-lg"> 5031 <div class="absolute top-0 left-0 z-10 min-w-20 p-2 -mt-6 text-sm leading-tight text-black 5032 transform -translate-x-1/2 -translate-y-full bg-white rounded-lg shadow-lg text-center" :class="{'-translate-x-5' : isFirstItemCol()}"> 5033 <template x-if="isVisualSwatch(activeTooltipItem.attribute, activeTooltipItem.item)"> 5034 <div class="inline-block border shadow-sm border-container" :style="getTooltipImageStyle(activeTooltipItem.attribute, activeTooltipItem.item)"></div> 5035 </template> 5036 <span class="mx-2 text-lg font-semibold whitespace-nowrap subtitle" x-html="getTooltipLabel()"></span> 5037 </div> 5038 <svg class="absolute z-10 w-8 h-8 text-white transform -translate-x-1/5 5039 -translate-y-8 fill-current stroke-current" width="12" height="12"> 5040 <rect x="12" y="-12" width="12" height="12" transform="rotate(45)" class="shadow-xl"></rect> 5041 </svg> 5042 </div> 5043 </div> 5044</template> 5045 </div> 5046 5047 5048 5049 <div class="pt-1 text-gray-900" x-data="initPriceBox()" x-defer="intersect" @update-prices-371456.window="updatePrice($event.detail);"> 5050 <div class="price-box price-final_price" data-role="priceBox" data-product-id="371456" data-price-box="product-id-371456"><div x-data="{ priceIsZero: false }" @update-prices-371456.window="priceIsZero = ($event.detail?.finalPrice?.amount <= 0.0001)"> 5051 <div x-show="!priceIsZero"> 5052 5053 <span class="normal-price "> 5054 <span x-data x-id="['product\u002Dprice\u002D371456']" class="price-container price-final_price tax weee"> 5055 <span :id="$id('product\u002Dprice\u002D371456')" data-price-amount="599.99" data-price-type="finalPrice" class="price-wrapper "><span class="price">R Â 599.99</span></span> 5056 </span> 5057 </span> 5058 5059 5060 </div> 5061</div> 5062</div> </div> 5063 5064 <div class="mt-auto pt-3 flex flex-wrap items-center gap-y-4 gap-x-2 5065 "> 5066 <button class="w-auto btn btn-primary justify-center text-sm mr-auto" title="Add to Cart Nike Swim Breaker Essential 5'' Brief-Lined Volley Shorts Mens Black" aria-label="Add to Cart Nike Swim Breaker Essential 5'' Brief-Lined Volley Shorts Mens Black" data-addto="cart"> 5067 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 5068 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
50680 014 0z"></path> 5069</svg> 5070 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 5071 Add to Cart </span> 5072 </button> 5073 5074 <div class="flex flex-wrap gap-2"> 5075 <button x-data="initWishlist()" x-defer="intersect" @click.prevent="addToWishlist(371456)" aria-label="Add to Wish List Nike Swim Breaker Essential 5'' Brief-Lined Volley Shorts Mens Black" type="button" class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex shrink-0 items-center justify-center text-gray-500 hover:text-red-600" data-addto="wishlist"> 5076 <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" fill="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 5077 <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"></path> 5078</svg> 5079 </button> 5080 </div> 5081 </div> 5082 </div> 5083</form> 5084 </div> 5085 <div class="js_slide flex flex-col shrink-0 w-1/2 mr-2 py-1 lg:w-1/3 xl:w-1/4" role="group" :aria-hidden="3 > active && 3 <= (active + pageSize) ? 'false' : 'true'" aria-label="Item 3" :aria-describedby="`slide-desc-371436-${$id('slider-id')}`"> 5086 5087<form method="post" action="https://www.studio-88.co.za/checkout/cart/add/uenc/%25uenc%25/product/371436/" class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full "> 5088 <input name="form_key" type="hidden" value="jQaPIgQq8BM99Mzs"> <input type="hidden" name="product" value="371436"> 5089 <a href="https://www.studio-88.co.za/puma-shuffle-downtown-low-sneaker-kids-white-chamber-blue" title="Puma Shuffle Downtown Low Sneaker Kids White/Chamber Blue" class="product photo product-item-photo block mx-auto mb-3 " tabindex="-1"> 5090 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-371436.window="$root.src = $event.detail" src="https://www.studio-88.co.za/media/catalog/product/cache/bd5b7e5d15290d4083c2c8fbcfc68244/p/m/pma7657kcb-puma-shuffle-downtown-40651104-v1_jpg.jpg" loading="lazy" width="360" height="360" alt="PMA7657KCB-PUMA-SHUFFLE-DOWNTOWN-40651104-V1" title="PMA7657KCB-PUMA-SHUFFLE-DOWNTOWN-40651104-V1"> 5091 </a> 5092 <div class="product-info equal flex flex-col grow"> 5093 <div class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 5094 <a class="product-item-link" href="https://www.studio-88.co.za/puma-shuffle-downtown-low-sneaker-kids-white-chamber-blue" :id="`slide-desc-371436-${$id('slider-id')}`"> 5095 Puma Shuffle Downtown Low Sneaker Kids White/Chamber Blue </a> 5096 </div> 5097 5098 <div class="py-1 mx-auto "> 5099 </div> 5100 5101 5102
5102<script> 5103 function initConfigurableSwatchOptions_371436() { 5104 const configurableOptionsComponent = initConfigurableOptions( 5105 '371436', 5106 {"attributes":{"170":{"id":"170","code":"size","label":"Size","options":[{"id":"120","label":"Size 1","products":["371107"]},{"id":"121","label":"Size 2","products":["371108"]},{"id":"129","label":"Size 10","products":["371109"]},{"id":"250","label":"Size 11","products":["371110"]},{"id":"131","label":"Size 12","products":["371111"]},{"id":"132","label":"Size 13","products":["371112"]}],"position":"0"}},"template":"R \u00a0<%- data.price %>","currencyFormat":"R \u00a0%s","optionPrices":{"371107":{"baseOldPrice":{"amount":899.99},"oldPrice":{"amount":899.99},"basePrice":{"amount":899.99},"finalPrice":{"amount":899.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371108":{"baseOldPrice":{"amount":899.99},"oldPrice":{"amount":899.99},"basePrice":{"amount":899.99},"finalPrice":{"amount":899.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371109":{"baseOldPrice":{"amount":899.99},"oldPrice":{"amount":899.99},"basePrice":{"amount":899.99},"finalPrice":{"amount":899.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371110":{"baseOldPrice":{"amount":899.99},"oldPrice":{"amount":899.99},"basePrice":{"amount":899.99},"finalPrice":{"amount":899.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371111":{"baseOldPrice":{"amount":899.99},"oldPrice":{"amount":899.99},"basePrice":{"amount":899.99},"finalPrice":{"amount":899.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371112":{"baseOldPrice":{"amount":899.99},"oldPrice":{"amount":899.99},"basePrice":{"amount":899.99},"finalPrice":{"amount":899.99},"tierPrices":[],"msrpPrice":{"amount":0}}},"priceFormat":{"pattern":"R \u00a0%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"prices":{"baseOldPrice":{"amount":899.99},"oldPrice":{"amount":899.99},"basePrice":{"amount":899.99},"finalPrice":{"amount":899.99}},"productId":"371436","chooseText":"Choose an Option...","images":[],"index":{"371107":{"170":"120"},"371108":{"170":"121"},"371109":{"170":"129"},"371110":{"170":"250"},"371111":{"170":"131"},"371112":{"170":"132"}},"salable":[],"canDisplayShowOutOfStockStatus":false,"channel":"website","salesChannelCode":"base","sku":{"371107":"PMA7657KCB_010","371108":"PMA7657KCB_020","371109":"PMA7657KCB_100","371110":"PMA7657KCB_110","371111":"PMA7657KCB_120","371112":"PMA7657KCB_130"},"label_reload":"https:\/\/www.studio-88.co.za\/amasty_label\/ajax\/label\/","label_category":".product-image-container, .product-item-photo, .cdz-product-top","label_product":".fotorama__stage, #amasty-main-container","original_product_id":"371436","amastyAdditionalConfig":{"isShowDefaultStatus":true,"outOfStockVisibility":1,"isProductPage":false}} ); 5107 const swatchOptionsComponent = initSwatchOptions({"170":{"120":{"type":"0","value":"Size 1","label":"Size 1"},"121":{"type":"0","value":"Size 2","label":"Size 2"},"129":{"type":"0","value":"Size 10","label":"Size 10"},"250":{"type":"0","value":"Size 11","label":"Size 11"},"131":{"type":"0","value":"Size 12","label":"Size 12"},"132":{"type":"0","value":"Size 13","label":"Size 13"},"additional_data":"{\"swatch_input_type\":\"text\",\"update_product_preview_image\":\"0\",\"use_product_image_for_swatch\":0}"}}); 5108 5109 return Object.assign( 5110 configurableOptionsComponent, 5111 swatchOptionsComponent, 5112 { 5113 mediaCallback: "https\u003A\u002F\u002Fwww.studio\u002D88.co.za\u002Fswatches\u002Fajax\u002Fmedia\u002F", 5114 changeOption(optionId, value, skipUpdateGallery) { 5115 if (value === '') { 5116 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, optionId) 5117 } else { 5118 this.selectedValues[optionId] = value; 5119 } 5120 this.findSimpleIndex(); 5121 this.findAllowedAttributeOptions(); 5122 this.updatePrices(); 5123 !skipUpdateGallery && this.updateGallery(); 5124 window.dispatchEvent(new CustomEvent('listing-configurable-selection-changed', { 5125 detail: { 5126 productId: this.productId, 5127 optionId, 5128 value, 5129 productIndex: this.productIndex, 5130 selectedValues: this.selectedValues, 5131 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 5132 } 5133 })); 5134 }, 5135 updateGallery() { 5136 if (!this.productIndex) { 5137 return; 5138 } 5139 5140 fetch(`${this.mediaCallback}?product_id=${this.productIndex}&isAjax=true`, { 5141 method: 'GET', 5142 headers: { 5143 'Content-Type': 'application/json', 5144 'X-Requested-With': 'XMLHttpRequest' 5145 } 5146 } 5147 ).then(response => { 5148 return response.json() 5149 } 5150 ).then(data => { 5151 if (data.errors) { 5152 // non critical failure only console logged 5153 console.warn(data.errors); 5154 } else { 5155 const image = data && data.medium; 5156 5157 image && window.dispatchEvent( 5158 new CustomEvent( 5159 "update-gallery-371436", 5160 {detail: image} 5161 ) 5162 ); 5163 } 5164 }).catch(error => { 5165 console.warn(error) 5166 }); 5167 }, 5168 preselectQuerystringItems() { 5169 // pre-select option like ?size=167 5170 const urlQueryParams = new URLSearchParams(window.location.search.replace('?', '')); 5171 Object.values(this.optionConfig.attributes).map(attribute => { 5172 // Don't update images on load, since PLPs already set the main image to the selected options 5173 const skipUpdateGallery = true; 5174 urlQueryParams.get(attribute.code) && 5175 this.changeOption(attribute.id, urlQueryParams.get(attribute.code), skipUpdateGallery); 5176 }); 5177 }, 5178 mouseDown: false, 5179 startX: 0, 5180 maxScroll: 0, 5181 scrollLeft: null, 5182 slider: null, 5183 scrollEvents: { 5184 ['@mousedown'](e) { 5185 this.slider = e.target.closest('.snap'); 5186 if (!this.slider) { 5187 return; 5188 } 5189 this.maxScroll = this.slider.scrollWidth - this.slider.offsetWidth; 5190 this.startX = e.pageX - this.slider.offsetLeft; 5191 this.scrollLeft = this.slider.scrollLeft; 5192 this.mouseDown = true; 5193 }, 5194 ['@mouseout.self']() { 5195 this.mouseDown = false;
5196 }, 5197 ['@mouseup']() { 5198 this.mouseDown = false; 5199 }, 5200 ['@mousemove'](e) { 5201 e.preventDefault(); 5202 if (!this.mouseDown) { 5203 return; 5204 } 5205 const x = e.pageX - this.slider.offsetLeft; 5206 const scroll = x - this.startX; 5207 const scrollLeft = this.scrollLeft - scroll; 5208 5209 if (scrollLeft > this.maxScroll) { 5210 this.slider.scrollLeft = this.maxScroll; 5211 return 5212 } 5213 this.slider.scrollLeft = this.scrollLeft - scroll; 5214 }, 5215 ['@onselectstart']() { 5216 return false; 5217 } 5218 }, 5219 resizeEvent() { 5220 Array.from(this.$root.querySelectorAll('.snap')).forEach(sli
5220der => { 5221 slider.scrollLeft = 0; 5222 }) 5223 } 5224 } 5225 ); 5226 } 5227 5228 </script>
5228 5229 5230<div x-data="initConfigurableSwatchOptions_371436()" x-defer="intersect" x-init="findAllowedAttributeOptions(); initShowSwatchesIntersect();" @private-content-loaded.window="onGetCartData($event.detail.data)" @resize.window="resizeEvent()" class="mb-2 relative"> 5231 <div> 5232 5233 <div class="swatch-attribute border-t last:border-b border-container 5234 size"> 5235 <div class="w-full overflow-x-hidden swatch-attribute-options min-h-14"> 5236 <template x-if="showSwatches"> 5237 <div class="flex flex-nowrap w-full overflow-auto snap items-center py-1" id="attribute-label-371436-170" role="radiogroup" x-bind="scrollEvents" aria-label="Size"> 5238 <template x-for="(item, index) in optionConfig.attributes[170].options" :key="item.id"> 5239 5240<div x-id="['attribute-option-371436-'+item.id]"> 5241 <template x-if="optionIsEnabled(170, item.id) && optionIsActive(170, item.id)"> 5242 <label :for="$id('attribute-option-371436-'+item.id)" class="swatch-option relative shadow-sm cursor-pointer select-none bg-container-lighter product-option-value-label" :class="{ 5243 'border-container-lighter ring ring-primary/50': 5244 (selectedValues[170] === item.id), 5245 'border-container-darker': 5246 (selectedValues[170] !== item.id), 5247 'w-6 h-6' : !isTextSwatch(170, item.id), 5248 'border-container-lighter ring ring-primary/75' : focusedLabel === item.id 5249 }" :style="getSwatchBackgroundStyle('170',item.id)" @mouseenter.self="activeTooltipItem = { 5250 attribute: '170', 5251 item: item.id, 5252 index 5253 }; tooltipPositionElement = $event.target;
5253" @mouseleave.self="activeTooltipItem = false"> 5254 <input :id="$id('attribute-option-371436-'+item.id)" :value="item.id" name="super_attribute[170]" type="radio" class="inline-block absolute p-0 border-0 focus:border-0 focus:ring-0 product-option-value-input" style="z-index:-1" x-on:focus="focusLabel(item.id)" x-on:blur="blurLabel()" x-on:change="changeOption(170, $event.target.value)" x-on:click="clearOptionIfActive(170, item.id)" x-model="selectedValues[170]" :required="getAllowedAttributeOptions(170).filter( 5255 attributeOption => selectedValues[attributeOption] 5256 ).length === 0" :aria-label="getSwatchText(170, item.id)" aria-describedby="attribute-label-371436-170"> 5257 <template x-if="isTextSwatch(170, item.id)"> 5258 <div x-html="getSwatchText(170, item.id)" class="pointer-events-none select-none whitespace-nowrap" aria-hidden="true"></div> 5259 </template> 5260 </label> 5261 </template> 5262 5263 <template x-if="optionIsEnabled(170, item.id) && !optionIsActive(170, item.id)"> 5264 <div class="relative border-2 shadow-sm opacity-50 cursor-pointer select-none border-container-darker swatch-option bg-container-lighter" :class="{ 5265 'w-6 h-6' : !isTextSwatch(170, item.id), 5266 }" :style="getSwatchBackgroundStyle('170',item.id)"> 5267 <div x-html="getSwatchText(170, item.id)" class="whitespace-nowrap" :class="{ 'sr-only' : !isTextSwatch(170, item.id) }"></div> 5268 <svg class="absolute inset-0 w-full h-full text-gray-500 bg-white/25"> 5269 <line x1="0" y1="100%" x2="100%" y2="0" class="stroke-current stroke-1"></line> 5270 </svg> 5271 </div> 5272 </template> 5273</div> 5274 </template> 5275 </div> 5276 </template> 5277 </div> 5278 </div> 5279 </div> 5280 <template x-if="isTooltipVisible()"> 5281 <div class="absolute pointer-events-none select-none" :style="getTooltipPosition()"> 5282 <div class="shadow-lg"> 5283 <div class="absolute top-0 left-0 z-10 min-w-20 p-2 -mt-6 text-sm leading-tight text-black 5284 transform -translate-x-1/2 -translate-y-full bg-white rounded-lg shadow-lg text-center" :class="{'-translate-x-5' : isFirstItemCol()}"> 5285 <template x-if="isVisualSwatch(activeTooltipItem.attribute, activeTooltipItem.item)"> 5286 <div class="inline-block border shadow-sm border-container" :style="getTooltipImageStyle(activeTooltipItem.attribute, activeTooltipItem.item)"></div> 5287 </template> 5288 <span class="mx-2 text-lg font-semibold whitespace-nowrap subtitle" x-html="getTooltipLabel()"></span> 5289 </div> 5290 <svg class="absolute z-10 w-8 h-8 text-white transform -translate-x-1/5 5291 -translate-y-8 fill-current stroke-current" width="12" height="12"> 5292 <rect x="12" y="-12" width="12" height="12" transform="rotate(45)" class="shadow-xl"></rect> 5293 </svg> 5294 </div> 5295 </div> 5296</template> 5297 </div> 5298 5299 5300 5301 <div class="pt-1 text-gray-900" x-data="initPriceBox()" x-defer="intersect" @update-prices-371436.window="updatePrice($event.detail);"> 5302 <div class="price-box price-final_price" data-role="priceBox" data-product-id="371436" data-price-box="product-id-371436"><div x-data="{ priceIsZero: false }" @update-prices-371436.window="priceIsZero = ($event.detail?.finalPrice?.amount <= 0.0001)"> 5303 <div x-show="!priceIsZero"> 5304 5305 <span class="normal-price "> 5306 <span x-data x-id="['product\u002Dprice\u002D371436']" class="price-container price-final_price tax weee"> 5307 <span :id="$id('product\u002Dprice\u002D371436')" data-price-amount="899.99" data-price-type="finalPrice" class="price-wrapper "><span class="price">R Â 899.99</span></span> 5308 </span> 5309 </span> 5310 5311 5312 </div> 5313</div> 5314</div> </div> 5315 5316 <div class="mt-auto pt-3 flex flex-wrap items-center gap-y-4 gap-x-2 5317 "> 5318 <button class="w-auto btn btn-primary justify-center text-sm mr-auto" title="Add to Cart Puma Shuffle Downtown Low Sneaker Kids White/Chamber Blue" aria-label="Add to Cart Puma Shuffle Downtown Low Sneaker Kids White/Chamber Blue" data-addto="cart"> 5319 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 5320 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
53200 014 0z"></path> 5321</svg> 5322 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 5323 Add to Cart </span> 5324 </button> 5325 5326 <div class="flex flex-wrap gap-2"> 5327 <button x-data="initWishlist()" x-defer="intersect" @click.prevent="addToWishlist(371436)" aria-label="Add to Wish List Puma Shuffle Downtown Low Sneaker Kids White/Chamber Blue" type="button" class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex shrink-0 items-center justify-center text-gray-500 hover:text-red-600" data-addto="wishlist"> 5328 <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" fill="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 5329 <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"></path> 5330</svg> 5331 </button> 5332 </div> 5333 </div> 5334 </div> 5335</form> 5336 </div> 5337 <div class="js_slide flex flex-col shrink-0 w-1/2 mr-2 py-1 lg:w-1/3 xl:w-1/4" role="group" :aria-hidden="4 > active && 4 <= (active + pageSize) ? 'false' : 'true'" aria-label="Item 4" :aria-describedby="`slide-desc-371434-${$id('slider-id')}`"> 5338 5339<form method="post" action="https://www.studio-88.co.za/checkout/cart/add/uenc/%25uenc%25/product/371434/" class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full "> 5340 <input name="form_key" type="hidden" value="jQaPIgQq8BM99Mzs"> <input type="hidden" name="product" value="371434"> 5341 <a href="https://www.studio-88.co.za/new-balance-603-sneaker-womens-seal-salt" title="New Balance 603 Sneaker Womens Seal Salt" class="product photo product-item-photo block mx-auto mb-3 " tabindex="-1"> 5342 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-371434.window="$root.src = $event.detail" src="https://www.studio-88.co.za/media/catalog/product/cache/bd5b7e5d15290d4083c2c8fbcfc68244/n/e/new269sp-new-balance-603-w6035pg-v1_jpg.jpg" loading="lazy" width="360" height="360" alt="NEW269SP-NEW-BALANCE-603-W6035PG-V1" title="NEW269SP-NEW-BALANCE-603-W6035PG-V1"> 5343 </a> 5344 <div class="product-info equal flex flex-col grow"> 5345 <div class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 5346 <a class="product-item-link" href="https://www.studio-88.co.za/new-balance-603-sneaker-womens-seal-salt" :id="`slide-desc-371434-${$id('slider-id')}`"> 5347 New Balance 603 Sneaker Womens Seal Salt </a> 5348 </div> 5349 5350 <div class="py-1 mx-auto "> 5351 </div> 5352 5353 5354
5354<script> 5355 function initConfigurableSwatchOptions_371434() { 5356 const configurableOptionsComponent = initConfigurableOptions( 5357 '371434', 5358 {"attributes":{"170":{"id":"170","code":"size","label":"Size","options":[{"id":"123","label":"Size 4","products":["370911"]},{"id":"124","label":"Size 5","products":["370912"]},{"id":"125","label":"Size 6","products":["370913"]},{"id":"126","label":"Size 7","products":["370914"]}],"position":"0"}},"template":"R \u00a0<%- data.price %>","currencyFormat":"R \u00a0%s","optionPrices":{"370911":{"baseOldPrice":{"amount":2099.99},"oldPrice":{"amount":2099.99},"basePrice":{"amount":2099.99},"finalPrice":{"amount":2099.99},"tierPrices":[],"msrpPrice":{"amount":0}},"370912":{"baseOldPrice":{"amount":2099.99},"oldPrice":{"amount":2099.99},"basePrice":{"amount":2099.99},"finalPrice":{"amount":2099.99},"tierPrices":[],"msrpPrice":{"amount":0}},"370913":{"baseOldPrice":{"amount":2099.99},"oldPrice":{"amount":2099.99},"basePrice":{"amount":2099.99},"finalPrice":{"amount":2099.99},"tierPrices":[],"msrpPrice":{"amount":0}},"370914":{"baseOldPrice":{"amount":2099.99},"oldPrice":{"amount":2099.99},"basePrice":{"amount":2099.99},"finalPrice":{"amount":2099.99},"tierPrices":[],"msrpPrice":{"amount":0}}},"priceFormat":{"pattern":"R \u00a0%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"prices":{"baseOldPrice":{"amount":2099.99},"oldPrice":{"amount":2099.99},"basePrice":{"amount":2099.99},"finalPrice":{"amount":2099.99}},"productId":"371434","chooseText":"Choose an Option...","images":[],"index":{"370911":{"170":"123"},"370912":{"170":"124"},"370913":{"170":"125"},"370914":{"170":"126"}},"salable":[],"canDisplayShowOutOfStockStatus":false,"channel":"website","salesChannelCode":"base","sku":{"370911":"NEW269SP040","370912":"NEW269SP050","370913":"NEW269SP060","370914":"NEW269SP070"},"label_reload":"https:\/\/www.studio-88.co.za\/amasty_label\/ajax\/label\/","label_category":".product-image-container, .product-item-photo, .cdz-product-top","label_product":".fotorama__stage, #amasty-main-container","original_product_id":"371434","amastyAdditionalConfig":{"isShowDefaultStatus":true,"outOfStockVisibility":1,"isProductPage":false}} ); 5359 const swatchOptionsComponent = initSwatchOptions({"170":{"123":{"type":"0","value":"Size 4","label":"Size 4"},"124":{"type":"0","value":"Size 5","label":"Size 5"},"125":{"type":"0","value":"Size 6","label":"Size 6"},"126":{"type":"0","value":"Size 7","label":"Size 7"},"additional_data":"{\"swatch_input_type\":\"text\",\"update_product_preview_image\":\"0\",\"use_product_image_for_swatch\":0}"}}); 5360 5361 return Object.assign( 5362 configurableOptionsComponent, 5363 swatchOptionsComponent, 5364 { 5365 mediaCallback: "https\u003A\u002F\u002Fwww.studio\u002D88.co.za\u002Fswatches\u002Fajax\u002Fmedia\u002F", 5366 changeOption(optionId, value, skipUpdateGallery) { 5367 if (value === '') { 5368 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, optionId) 5369 } else { 5370 this.selectedValues[optionId] = value; 5371 } 5372 this.findSimpleIndex(); 5373 this.findAllowedAttributeOptions(); 5374 this.updatePrices(); 5375 !skipUpdateGallery && this.updateGallery(); 5376 window.dispatchEvent(new CustomEvent('listing-configurable-selection-changed', { 5377 detail: { 5378 productId: this.productId, 5379 optionId, 5380 value, 5381 productIndex: this.productIndex, 5382 selectedValues: this.selectedValues, 5383 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 5384 } 5385 })); 5386 }, 5387 updateGallery() { 5388 if (!this.productIndex) { 5389 return; 5390 } 5391 5392 fetch(`${this.mediaCallback}?product_id=${this.productIndex}&isAjax=true`, { 5393 method: 'GET', 5394 headers: { 5395 'Content-Type': 'application/json', 5396 'X-Requested-With': 'XMLHttpRequest' 5397 } 5398 } 5399 ).then(response => { 5400 return response.json() 5401 } 5402 ).then(data => { 5403 if (data.errors) { 5404 // non critical failure only console logged 5405 console.warn(data.errors); 5406 } else { 5407 const image = data && data.medium; 5408 5409 image && window.dispatchEvent( 5410 new CustomEvent( 5411 "update-gallery-371434", 5412 {detail: image} 5413 ) 5414 ); 5415 } 5416 }).catch(error => { 5417 console.warn(error) 5418 }); 5419 }, 5420 preselectQuerystringItems() { 5421 // pre-select option like ?size=167 5422 const urlQueryParams = new URLSearchParams(window.location.search.replace('?', '')); 5423 Object.values(this.optionConfig.attributes).map(attribute => { 5424 // Don't update images on load, since PLPs already set the main image to the selected options 5425 const skipUpdateGallery = true; 5426 urlQueryParams.get(attribute.code) && 5427 this.changeOption(attribute.id, urlQueryParams.get(attribute.code), skipUpdateGallery); 5428 }); 5429 }, 5430 mouseDown: false, 5431 startX: 0, 5432 maxScroll: 0, 5433 scrollLeft: null, 5434 slider: null, 5435 scrollEvents: { 5436 ['@mousedown'](e) { 5437 this.slider = e.target.closest('.snap'); 5438 if (!this.slider) { 5439 return; 5440 } 5441 this.maxScroll = this.slider.scrollWidth - this.slider.offsetWidth; 5442 this.startX = e.pageX - this.slider.offsetLeft; 5443 this.scrollLeft = this.slider.scrollLeft; 5444 this.mouseDown = true; 5445 }, 5446 ['@mouseout.self']() { 5447 this.mouseDown = false;
5448 }, 5449 ['@mouseup']() { 5450 this.mouseDown = false; 5451 }, 5452 ['@mousemove'](e) { 5453 e.preventDefault(); 5454 if (!this.mouseDown) { 5455 return; 5456 } 5457 const x = e.pageX - this.slider.offsetLeft; 5458 const scroll = x - this.startX; 5459 const scrollLeft = this.scrollLeft - scroll; 5460 5461 if (scrollLeft > this.maxScroll) { 5462 this.slider.scrollLeft = this.maxScroll; 5463 return 5464 } 5465 this.slider.scrollLeft = this.scrollLeft - scroll; 5466 }, 5467 ['@onselectstart']() { 5468 return false; 5469 } 5470 }, 5471 resizeEvent() { 5472 Array.from(this.$root.querySelectorAll('.snap')).forEach(sli
5472der => { 5473 slider.scrollLeft = 0; 5474 }) 5475 } 5476 } 5477 ); 5478 } 5479 5480 </script>
5480 5481 5482<div x-data="initConfigurableSwatchOptions_371434()" x-defer="intersect" x-init="findAllowedAttributeOptions(); initShowSwatchesIntersect();" @private-content-loaded.window="onGetCartData($event.detail.data)" @resize.window="resizeEvent()" class="mb-2 relative"> 5483 <div> 5484 5485 <div class="swatch-attribute border-t last:border-b border-container 5486 size"> 5487 <div class="w-full overflow-x-hidden swatch-attribute-options min-h-14"> 5488 <template x-if="showSwatches"> 5489 <div class="flex flex-nowrap w-full overflow-auto snap items-center py-1" id="attribute-label-371434-170" role="radiogroup" x-bind="scrollEvents" aria-label="Size"> 5490 <template x-for="(item, index) in optionConfig.attributes[170].options" :key="item.id"> 5491 5492<div x-id="['attribute-option-371434-'+item.id]"> 5493 <template x-if="optionIsEnabled(170, item.id) && optionIsActive(170, item.id)"> 5494 <label :for="$id('attribute-option-371434-'+item.id)" class="swatch-option relative shadow-sm cursor-pointer select-none bg-container-lighter product-option-value-label" :class="{ 5495 'border-container-lighter ring ring-primary/50': 5496 (selectedValues[170] === item.id), 5497 'border-container-darker': 5498 (selectedValues[170] !== item.id), 5499 'w-6 h-6' : !isTextSwatch(170, item.id), 5500 'border-container-lighter ring ring-primary/75' : focusedLabel === item.id 5501 }" :style="getSwatchBackgroundStyle('170',item.id)" @mouseenter.self="activeTooltipItem = { 5502 attribute: '170', 5503 item: item.id, 5504 index 5505 }; tooltipPositionElement = $event.target;
5505" @mouseleave.self="activeTooltipItem = false"> 5506 <input :id="$id('attribute-option-371434-'+item.id)" :value="item.id" name="super_attribute[170]" type="radio" class="inline-block absolute p-0 border-0 focus:border-0 focus:ring-0 product-option-value-input" style="z-index:-1" x-on:focus="focusLabel(item.id)" x-on:blur="blurLabel()" x-on:change="changeOption(170, $event.target.value)" x-on:click="clearOptionIfActive(170, item.id)" x-model="selectedValues[170]" :required="getAllowedAttributeOptions(170).filter( 5507 attributeOption => selectedValues[attributeOption] 5508 ).length === 0" :aria-label="getSwatchText(170, item.id)" aria-describedby="attribute-label-371434-170"> 5509 <template x-if="isTextSwatch(170, item.id)"> 5510 <div x-html="getSwatchText(170, item.id)" class="pointer-events-none select-none whitespace-nowrap" aria-hidden="true"></div> 5511 </template> 5512 </label> 5513 </template> 5514 5515 <template x-if="optionIsEnabled(170, item.id) && !optionIsActive(170, item.id)"> 5516 <div class="relative border-2 shadow-sm opacity-50 cursor-pointer select-none border-container-darker swatch-option bg-container-lighter" :class="{ 5517 'w-6 h-6' : !isTextSwatch(170, item.id), 5518 }" :style="getSwatchBackgroundStyle('170',item.id)"> 5519 <div x-html="getSwatchText(170, item.id)" class="whitespace-nowrap" :class="{ 'sr-only' : !isTextSwatch(170, item.id) }"></div> 5520 <svg class="absolute inset-0 w-full h-full text-gray-500 bg-white/25"> 5521 <line x1="0" y1="100%" x2="100%" y2="0" class="stroke-current stroke-1"></line> 5522 </svg> 5523 </div> 5524 </template> 5525</div> 5526 </template> 5527 </div> 5528 </template> 5529 </div> 5530 </div> 5531 </div> 5532 <template x-if="isTooltipVisible()"> 5533 <div class="absolute pointer-events-none select-none" :style="getTooltipPosition()"> 5534 <div class="shadow-lg"> 5535 <div class="absolute top-0 left-0 z-10 min-w-20 p-2 -mt-6 text-sm leading-tight text-black 5536 transform -translate-x-1/2 -translate-y-full bg-white rounded-lg shadow-lg text-center" :class="{'-translate-x-5' : isFirstItemCol()}"> 5537 <template x-if="isVisualSwatch(activeTooltipItem.attribute, activeTooltipItem.item)"> 5538 <div class="inline-block border shadow-sm border-container" :style="getTooltipImageStyle(activeTooltipItem.attribute, activeTooltipItem.item)"></div> 5539 </template> 5540 <span class="mx-2 text-lg font-semibold whitespace-nowrap subtitle" x-html="getTooltipLabel()"></span> 5541 </div> 5542 <svg class="absolute z-10 w-8 h-8 text-white transform -translate-x-1/5 5543 -translate-y-8 fill-current stroke-current" width="12" height="12"> 5544 <rect x="12" y="-12" width="12" height="12" transform="rotate(45)" class="shadow-xl"></rect> 5545 </svg> 5546 </div> 5547 </div> 5548</template> 5549 </div> 5550 5551 5552 5553 <div class="pt-1 text-gray-900" x-data="initPriceBox()" x-defer="intersect" @update-prices-371434.window="updatePrice($event.detail);"> 5554 <div class="price-box price-final_price" data-role="priceBox" data-product-id="371434" data-price-box="product-id-371434"><div x-data="{ priceIsZero: false }" @update-prices-371434.window="priceIsZero = ($event.detail?.finalPrice?.amount <= 0.0001)"> 5555 <div x-show="!priceIsZero"> 5556 5557 <span class="normal-price "> 5558 <span x-data x-id="['product\u002Dprice\u002D371434']" class="price-container price-final_price tax weee"> 5559 <span :id="$id('product\u002Dprice\u002D371434')" data-price-amount="2099.99" data-price-type="finalPrice" class="price-wrapper "><span class="price">R Â 2,099.99</span></span> 5560 </span> 5561 </span> 5562 5563 5564 </div> 5565</div> 5566</div> </div> 5567 5568 <div class="mt-auto pt-3 flex flex-wrap items-center gap-y-4 gap-x-2 5569 "> 5570 <button class="w-auto btn btn-primary justify-center text-sm mr-auto" title="Add to Cart New Balance 603 Sneaker Womens Seal Salt" aria-label="Add to Cart New Balance 603 Sneaker Womens Seal Salt" data-addto="cart"> 5571 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 5572 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
55720 014 0z"></path> 5573</svg> 5574 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 5575 Add to Cart </span> 5576 </button> 5577 5578 <div class="flex flex-wrap gap-2"> 5579 <button x-data="initWishlist()" x-defer="intersect" @click.prevent="addToWishlist(371434)" aria-label="Add to Wish List New Balance 603 Sneaker Womens Seal Salt" type="button" class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex shrink-0 items-center justify-center text-gray-500 hover:text-red-600" data-addto="wishlist"> 5580 <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" fill="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 5581 <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"></path> 5582</svg> 5583 </button> 5584 </div> 5585 </div> 5586 </div> 5587</form> 5588 </div> 5589 <div class="js_slide flex flex-col shrink-0 w-1/2 mr-2 py-1 lg:w-1/3 xl:w-1/4" role="group" :aria-hidden="5 > active && 5 <= (active + pageSize) ? 'false' : 'true'" aria-label="Item 5" :aria-describedby="`slide-desc-371432-${$id('slider-id')}`"> 5590 5591<form method="post" action="https://www.studio-88.co.za/checkout/cart/add/uenc/%25uenc%25/product/371432/" class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full "> 5592 <input name="form_key" type="hidden" value="jQaPIgQq8BM99Mzs"> <input type="hidden" name="product" value="371432"> 5593 <a href="https://www.studio-88.co.za/puma-suede-xl-sneaker-kids-archive-green" title="Puma Suede XL Sneaker Kids Archive Green" class="product photo product-item-photo block mx-auto mb-3 " tabindex="-1"> 5594 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-371432.window="$root.src = $event.detail" src="https://www.studio-88.co.za/media/catalog/product/cache/bd5b7e5d15290d4083c2c8fbcfc68244/p/m/pma5674kag-puma-suede-xl-39657812-v1_jpg.jpg" loading="lazy" width="360" height="360" alt="PMA5674KAG-PUMA-SUEDE-XL-39657812-V1" title="PMA5674KAG-PUMA-SUEDE-XL-39657812-V1"> 5595 </a> 5596 <div class="product-info equal flex flex-col grow"> 5597 <div class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 5598 <a class="product-item-link" href="https://www.studio-88.co.za/puma-suede-xl-sneaker-kids-archive-green" :id="`slide-desc-371432-${$id('slider-id')}`"> 5599 Puma Suede XL Sneaker Kids Archive Green </a> 5600 </div> 5601 5602 <div class="py-1 mx-auto "> 5603 </div> 5604 5605 5606
5606<script> 5607 function initConfigurableSwatchOptions_371432() { 5608 const configurableOptionsComponent = initConfigurableOptions( 5609 '371432', 5610 {"attributes":{"170":{"id":"170","code":"size","label":"Size","options":[{"id":"120","label":"Size 1","products":["371147"]},{"id":"121","label":"Size 2","products":["371148"]},{"id":"129","label":"Size 10","products":["371149"]},{"id":"250","label":"Size 11","products":["371150"]},{"id":"131","label":"Size 12","products":["371151"]},{"id":"132","label":"Size 13","products":["371152"]}],"position":"0"}},"template":"R \u00a0<%- data.price %>","currencyFormat":"R \u00a0%s","optionPrices":{"371147":{"baseOldPrice":{"amount":1299.99},"oldPrice":{"amount":1299.99},"basePrice":{"amount":1299.99},"finalPrice":{"amount":1299.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371148":{"baseOldPrice":{"amount":1299.99},"oldPrice":{"amount":1299.99},"basePrice":{"amount":1299.99},"finalPrice":{"amount":1299.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371149":{"baseOldPrice":{"amount":1299.99},"oldPrice":{"amount":1299.99},"basePrice":{"amount":1299.99},"finalPrice":{"amount":1299.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371150":{"baseOldPrice":{"amount":1299.99},"oldPrice":{"amount":1299.99},"basePrice":{"amount":1299.99},"finalPrice":{"amount":1299.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371151":{"baseOldPrice":{"amount":1299.99},"oldPrice":{"amount":1299.99},"basePrice":{"amount":1299.99},"finalPrice":{"amount":1299.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371152":{"baseOldPrice":{"amount":1299.99},"oldPrice":{"amount":1299.99},"basePrice":{"amount":1299.99},"finalPrice":{"amount":1299.99},"tierPrices":[],"msrpPrice":{"amount":0}}},"priceFormat":{"pattern":"R \u00a0%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"prices":{"baseOldPrice":{"amount":1299.99},"oldPrice":{"amount":1299.99},"basePrice":{"amount":1299.99},"finalPrice":{"amount":1299.99}},"productId":"371432","chooseText":"Choose an Option...","images":[],"index":{"371147":{"170":"120"},"371148":{"170":"121"},"371149":{"170":"129"},"371150":{"170":"250"},"371151":{"170":"131"},"371152":{"170":"132"}},"salable":[],"canDisplayShowOutOfStockStatus":false,"channel":"website","salesChannelCode":"base","sku":{"371147":"PMA5674KAG_010","371148":"PMA5674KAG_020","371149":"PMA5674KAG_100","371150":"PMA5674KAG_110","371151":"PMA5674KAG_120","371152":"PMA5674KAG_130"},"label_reload":"https:\/\/www.studio-88.co.za\/amasty_label\/ajax\/label\/","label_category":".product-image-container, .product-item-photo, .cdz-product-top","label_product":".fotorama__stage, #amasty-main-container","original_product_id":"371432","amastyAdditionalConfig":{"isShowDefaultStatus":true,"outOfStockVisibility":1,"isProductPage":false}} ); 5611 const swatchOptionsComponent = initSwatchOptions({"170":{"120":{"type":"0","value":"Size 1","label":"Size 1"},"121":{"type":"0","value":"Size 2","label":"Size 2"},"129":{"type":"0","value":"Size 10","label":"Size 10"},"250":{"type":"0","value":"Size 11","label":"Size 11"},"131":{"type":"0","value":"Size 12","label":"Size 12"},"132":{"type":"0","value":"Size 13","label":"Size 13"},"additional_data":"{\"swatch_input_type\":\"text\",\"update_product_preview_image\":\"0\",\"use_product_image_for_swatch\":0}"}}); 5612 5613 return Object.assign( 5614 configurableOptionsComponent, 5615 swatchOptionsComponent, 5616 { 5617 mediaCallback: "https\u003A\u002F\u002Fwww.studio\u002D88.co.za\u002Fswatches\u002Fajax\u002Fmedia\u002F", 5618 changeOption(optionId, value, skipUpdateGallery) { 5619 if (value === '') { 5620 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, optionId) 5621 } else { 5622 this.selectedValues[optionId] = value; 5623 } 5624 this.findSimpleIndex(); 5625 this.findAllowedAttributeOptions(); 5626 this.updatePrices(); 5627 !skipUpdateGallery && this.updateGallery(); 5628 window.dispatchEvent(new CustomEvent('listing-configurable-selection-changed', { 5629 detail: { 5630 productId: this.productId, 5631 optionId, 5632 value, 5633 productIndex: this.productIndex, 5634 selectedValues: this.selectedValues, 5635 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 5636 } 5637 })); 5638 }, 5639 updateGallery() { 5640 if (!this.productIndex) { 5641 return; 5642 } 5643 5644 fetch(`${this.mediaCallback}?product_id=${this.productIndex}&isAjax=true`, { 5645 method: 'GET', 5646 headers: { 5647 'Content-Type': 'application/json', 5648 'X-Requested-With': 'XMLHttpRequest' 5649 } 5650 } 5651 ).then(response => { 5652 return response.json() 5653 } 5654 ).then(data => { 5655 if (data.errors) { 5656 // non critical failure only console logged 5657 console.warn(data.errors); 5658 } else { 5659 const image = data && data.medium; 5660 5661 image && window.dispatchEvent( 5662 new CustomEvent( 5663 "update-gallery-371432", 5664 {detail: image} 5665 ) 5666 ); 5667 } 5668 }).catch(error => { 5669 console.warn(error) 5670 }); 5671 }, 5672 preselectQuerystringItems() { 5673 // pre-select option like ?size=167 5674 const urlQueryParams = new URLSearchParams(window.location.search.replace('?', '')); 5675 Object.values(this.optionConfig.attributes).map(attribute => { 5676 // Don't update images on load, since PLPs already set the main image to the selected options 5677 const skipUpdateGallery = true; 5678 urlQueryParams.get(attribute.code) && 5679 this.changeOption(attribute.id, urlQueryParams.get(attribute.code), skipUpdateGallery); 5680 }); 5681 }, 5682 mouseDown: false, 5683 startX: 0, 5684 maxScroll: 0, 5685 scrollLeft: null, 5686 slider: null, 5687 scrollEvents: { 5688 ['@mousedown'](e) { 5689 this.slider = e.target.closest('.snap'); 5690 if (!this.slider) { 5691 return; 5692 } 5693 this.maxScroll = this.slider.scrollWidth - this.slider.offsetWidth; 5694 this.startX = e.pageX - this.slider.offsetLeft; 5695 this.scrollLeft = this.slider.scrollLeft; 5696 this.mouseDown = true; 5697 }, 5698 ['@mouseout.self']() { 5699 this.mouseDown = false;
5700 }, 5701 ['@mouseup']() { 5702 this.mouseDown = false; 5703 }, 5704 ['@mousemove'](e) { 5705 e.preventDefault(); 5706 if (!this.mouseDown) { 5707 return; 5708 } 5709 const x = e.pageX - this.slider.offsetLeft; 5710 const scroll = x - this.startX; 5711 const scrollLeft = this.scrollLeft - scroll; 5712 5713 if (scrollLeft > this.maxScroll) { 5714 this.slider.scrollLeft = this.maxScroll; 5715 return 5716 } 5717 this.slider.scrollLeft = this.scrollLeft - scroll; 5718 }, 5719 ['@onselectstart']() { 5720 return false; 5721 } 5722 }, 5723 resizeEvent() { 5724 Array.from(this.$root.querySelectorAll('.snap')).forEach(sli
5724der => { 5725 slider.scrollLeft = 0; 5726 }) 5727 } 5728 } 5729 ); 5730 } 5731 5732 </script>
5732 5733 5734<div x-data="initConfigurableSwatchOptions_371432()" x-defer="intersect" x-init="findAllowedAttributeOptions(); initShowSwatchesIntersect();" @private-content-loaded.window="onGetCartData($event.detail.data)" @resize.window="resizeEvent()" class="mb-2 relative"> 5735 <div> 5736 5737 <div class="swatch-attribute border-t last:border-b border-container 5738 size"> 5739 <div class="w-full overflow-x-hidden swatch-attribute-options min-h-14"> 5740 <template x-if="showSwatches"> 5741 <div class="flex flex-nowrap w-full overflow-auto snap items-center py-1" id="attribute-label-371432-170" role="radiogroup" x-bind="scrollEvents" aria-label="Size"> 5742 <template x-for="(item, index) in optionConfig.attributes[170].options" :key="item.id"> 5743 5744<div x-id="['attribute-option-371432-'+item.id]"> 5745 <template x-if="optionIsEnabled(170, item.id) && optionIsActive(170, item.id)"> 5746 <label :for="$id('attribute-option-371432-'+item.id)" class="swatch-option relative shadow-sm cursor-pointer select-none bg-container-lighter product-option-value-label" :class="{ 5747 'border-container-lighter ring ring-primary/50': 5748 (selectedValues[170] === item.id), 5749 'border-container-darker': 5750 (selectedValues[170] !== item.id), 5751 'w-6 h-6' : !isTextSwatch(170, item.id), 5752 'border-container-lighter ring ring-primary/75' : focusedLabel === item.id 5753 }" :style="getSwatchBackgroundStyle('170',item.id)" @mouseenter.self="activeTooltipItem = { 5754 attribute: '170', 5755 item: item.id, 5756 index 5757 }; tooltipPositionElement = $event.target;
5757" @mouseleave.self="activeTooltipItem = false"> 5758 <input :id="$id('attribute-option-371432-'+item.id)" :value="item.id" name="super_attribute[170]" type="radio" class="inline-block absolute p-0 border-0 focus:border-0 focus:ring-0 product-option-value-input" style="z-index:-1" x-on:focus="focusLabel(item.id)" x-on:blur="blurLabel()" x-on:change="changeOption(170, $event.target.value)" x-on:click="clearOptionIfActive(170, item.id)" x-model="selectedValues[170]" :required="getAllowedAttributeOptions(170).filter( 5759 attributeOption => selectedValues[attributeOption] 5760 ).length === 0" :aria-label="getSwatchText(170, item.id)" aria-describedby="attribute-label-371432-170"> 5761 <template x-if="isTextSwatch(170, item.id)"> 5762 <div x-html="getSwatchText(170, item.id)" class="pointer-events-none select-none whitespace-nowrap" aria-hidden="true"></div> 5763 </template> 5764 </label> 5765 </template> 5766 5767 <template x-if="optionIsEnabled(170, item.id) && !optionIsActive(170, item.id)"> 5768 <div class="relative border-2 shadow-sm opacity-50 cursor-pointer select-none border-container-darker swatch-option bg-container-lighter" :class="{ 5769 'w-6 h-6' : !isTextSwatch(170, item.id), 5770 }" :style="getSwatchBackgroundStyle('170',item.id)"> 5771 <div x-html="getSwatchText(170, item.id)" class="whitespace-nowrap" :class="{ 'sr-only' : !isTextSwatch(170, item.id) }"></div> 5772 <svg class="absolute inset-0 w-full h-full text-gray-500 bg-white/25"> 5773 <line x1="0" y1="100%" x2="100%" y2="0" class="stroke-current stroke-1"></line> 5774 </svg> 5775 </div> 5776 </template> 5777</div> 5778 </template> 5779 </div> 5780 </template> 5781 </div> 5782 </div> 5783 </div> 5784 <template x-if="isTooltipVisible()"> 5785 <div class="absolute pointer-events-none select-none" :style="getTooltipPosition()"> 5786 <div class="shadow-lg"> 5787 <div class="absolute top-0 left-0 z-10 min-w-20 p-2 -mt-6 text-sm leading-tight text-black 5788 transform -translate-x-1/2 -translate-y-full bg-white rounded-lg shadow-lg text-center" :class="{'-translate-x-5' : isFirstItemCol()}"> 5789 <template x-if="isVisualSwatch(activeTooltipItem.attribute, activeTooltipItem.item)"> 5790 <div class="inline-block border shadow-sm border-container" :style="getTooltipImageStyle(activeTooltipItem.attribute, activeTooltipItem.item)"></div> 5791 </template> 5792 <span class="mx-2 text-lg font-semibold whitespace-nowrap subtitle" x-html="getTooltipLabel()"></span> 5793 </div> 5794 <svg class="absolute z-10 w-8 h-8 text-white transform -translate-x-1/5 5795 -translate-y-8 fill-current stroke-current" width="12" height="12"> 5796 <rect x="12" y="-12" width="12" height="12" transform="rotate(45)" class="shadow-xl"></rect> 5797 </svg> 5798 </div> 5799 </div> 5800</template> 5801 </div> 5802 5803 5804 5805 <div class="pt-1 text-gray-900" x-data="initPriceBox()" x-defer="intersect" @update-prices-371432.window="updatePrice($event.detail);"> 5806 <div class="price-box price-final_price" data-role="priceBox" data-product-id="371432" data-price-box="product-id-371432"><div x-data="{ priceIsZero: false }" @update-prices-371432.window="priceIsZero = ($event.detail?.finalPrice?.amount <= 0.0001)"> 5807 <div x-show="!priceIsZero"> 5808 5809 <span class="normal-price "> 5810 <span x-data x-id="['product\u002Dprice\u002D371432']" class="price-container price-final_price tax weee"> 5811 <span :id="$id('product\u002Dprice\u002D371432')" data-price-amount="1299.99" data-price-type="finalPrice" class="price-wrapper "><span class="price">R Â 1,299.99</span></span> 5812 </span> 5813 </span> 5814 5815 5816 </div> 5817</div> 5818</div> </div> 5819 5820 <div class="mt-auto pt-3 flex flex-wrap items-center gap-y-4 gap-x-2 5821 "> 5822 <button class="w-auto btn btn-primary justify-center text-sm mr-auto" title="Add to Cart Puma Suede XL Sneaker Kids Archive Green" aria-label="Add to Cart Puma Suede XL Sneaker Kids Archive Green" data-addto="cart"> 5823 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 5824 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
58240 014 0z"></path> 5825</svg> 5826 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 5827 Add to Cart </span> 5828 </button> 5829 5830 <div class="flex flex-wrap gap-2"> 5831 <button x-data="initWishlist()" x-defer="intersect" @click.prevent="addToWishlist(371432)" aria-label="Add to Wish List Puma Suede XL Sneaker Kids Archive Green" type="button" class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex shrink-0 items-center justify-center text-gray-500 hover:text-red-600" data-addto="wishlist"> 5832 <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" fill="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 5833 <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"></path> 5834</svg> 5835 </button> 5836 </div> 5837 </div> 5838 </div> 5839</form> 5840 </div> 5841 <div class="js_slide flex flex-col shrink-0 w-1/2 mr-2 py-1 lg:w-1/3 xl:w-1/4" role="group" :aria-hidden="6 > active && 6 <= (active + pageSize) ? 'false' : 'true'" aria-label="Item 6" :aria-describedby="`slide-desc-371430-${$id('slider-id')}`"> 5842 5843<form method="post" action="https://www.studio-88.co.za/checkout/cart/add/uenc/%25uenc%25/product/371430/" class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full "> 5844 <input name="form_key" type="hidden" value="966e30g3PGxPX7Kq"> <input type="hidden" name="product" value="371430"> 5845 <a href="https://www.studio-88.co.za/vans-authentic-denim-sneaker-mens-blue-white" title="Vans Authentic Denim Sneaker Mens Blue/White" class="product photo product-item-photo block mx-auto mb-3 " tabindex="-1"> 5846 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-371430.window="$root.src = $event.detail" src="https://www.studio-88.co.za/media/catalog/product/cache/bd5b7e5d15290d4083c2c8fbcfc68244/v/a/van8dm-vans-authdenim-bluwht-vn000d7yy6z-v1_jpg.jpg" loading="lazy" width="360" height="360" alt="VAN8DM-VANS-AUTHDENIM-BLUWHT-VN000D7YY6Z-V1" title="VAN8DM-VANS-AUTHDENIM-BLUWHT-VN000D7YY6Z-V1"> 5847 </a> 5848 <div class="product-info equal flex flex-col grow"> 5849 <div class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 5850 <a class="product-item-link" href="https://www.studio-88.co.za/vans-authentic-denim-sneaker-mens-blue-white" :id="`slide-desc-371430-${$id('slider-id')}`"> 5851 Vans Authentic Denim Sneaker Mens Blue/White </a> 5852 </div> 5853 5854 <div class="py-1 mx-auto "> 5855 </div> 5856 5857 5858
5858<script> 5859 function initConfigurableSwatchOptions_371430() { 5860 const configurableOptionsComponent = initConfigurableOptions( 5861 '371430', 5862 {"attributes":{"170":{"id":"170","code":"size","label":"Size","options":[{"id":"126","label":"Size 7","products":["371425"]},{"id":"127","label":"Size 8","products":["371426"]}],"position":"0"}},"template":"R \u00a0<%- data.price %>","currencyFormat":"R \u00a0%s","optionPrices":{"371425":{"baseOldPrice":{"amount":1099.99},"oldPrice":{"amount":1099.99},"basePrice":{"amount":1099.99},"finalPrice":{"amount":1099.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371426":{"baseOldPrice":{"amount":1099.99},"oldPrice":{"amount":1099.99},"basePrice":{"amount":1099.99},"finalPrice":{"amount":1099.99},"tierPrices":[],"msrpPrice":{"amount":0}}},"priceFormat":{"pattern":"R \u00a0%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"prices":{"baseOldPrice":{"amount":1099.99},"oldPrice":{"amount":1099.99},"basePrice":{"amount":1099.99},"finalPrice":{"amount":1099.99}},"productId":"371430","chooseText":"Choose an Option...","images":[],"index":{"371425":{"170":"126"},"371426":{"170":"127"}},"salable":[],"canDisplayShowOutOfStockStatus":false,"channel":"website","salesChannelCode":"base","sku":{"371425":"VAN8DM__070","371426":"VAN8DM__080"},"label_reload":"https:\/\/www.studio-88.co.za\/amasty_label\/ajax\/label\/","label_category":".product-image-container, .product-item-photo, .cdz-product-top","label_product":".fotorama__stage, #amasty-main-container","original_product_id":"371430","amastyAdditionalConfig":{"isShowDefaultStatus":true,"outOfStockVisibility":1,"isProductPage":false}} ); 5863 const swatchOptionsComponent = initSwatchOptions({"170":{"126":{"type":"0","value":"Size 7","label":"Size 7"},"127":{"type":"0","value":"Size 8","label":"Size 8"},"additional_data":"{\"swatch_input_type\":\"text\",\"update_product_preview_image\":\"0\",\"use_product_image_for_swatch\":0}"}}); 5864 5865 return Object.assign( 5866 configurableOptionsComponent, 5867 swatchOptionsComponent, 5868 { 5869 mediaCallback: "https\u003A\u002F\u002Fwww.studio\u002D88.co.za\u002Fswatches\u002Fajax\u002Fmedia\u002F", 5870 changeOption(optionId, value, skipUpdateGallery) { 5871 if (value === '') { 5872 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, optionId) 5873 } else { 5874 this.selectedValues[optionId] = value; 5875 } 5876 this.findSimpleIndex(); 5877 this.findAllowedAttributeOptions(); 5878 this.updatePrices(); 5879 !skipUpdateGallery && this.updateGallery(); 5880 window.dispatchEvent(new CustomEvent('listing-configurable-selection-changed', { 5881 detail: { 5882 productId: this.productId, 5883 optionId, 5884 value, 5885 productIndex: this.productIndex, 5886 selectedValues: this.selectedValues, 5887 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 5888 } 5889 })); 5890 }, 5891 updateGallery() { 5892 if (!this.productIndex) { 5893 return; 5894 } 5895 5896 fetch(`${this.mediaCallback}?product_id=${this.productIndex}&isAjax=true`, { 5897 method: 'GET', 5898 headers: { 5899 'Content-Type': 'application/json', 5900 'X-Requested-With': 'XMLHttpRequest' 5901 } 5902 } 5903 ).then(response => { 5904 return response.json() 5905 } 5906 ).then(data => { 5907 if (data.errors) { 5908 // non critical failure only console logged 5909 console.warn(data.errors); 5910 } else { 5911 const image = data && data.medium; 5912 5913 image && window.dispatchEvent( 5914 new CustomEvent( 5915 "update-gallery-371430", 5916 {detail: image} 5917 ) 5918 ); 5919 } 5920 }).catch(error => { 5921 console.warn(error) 5922 }); 5923 }, 5924 preselectQuerystringItems() { 5925 // pre-select option like ?size=167 5926 const urlQueryParams = new URLSearchParams(window.location.search.replace('?', '')); 5927 Object.values(this.optionConfig.attributes).map(attribute => { 5928 // Don't update images on load, since PLPs already set the main image to the selected options 5929 const skipUpdateGallery = true; 5930 urlQueryParams.get(attribute.code) && 5931 this.changeOption(attribute.id, urlQueryParams.get(attribute.code), skipUpdateGallery); 5932 }); 5933 }, 5934 mouseDown: false, 5935 startX: 0, 5936 maxScroll: 0, 5937 scrollLeft: null, 5938 slider: null, 5939 scrollEvents: { 5940 ['@mousedown'](e) { 5941 this.slider = e.target.closest('.snap'); 5942 if (!this.slider) { 5943 return; 5944 } 5945 this.maxScroll = this.slider.scrollWidth - this.slider.offsetWidth; 5946 this.startX = e.pageX - this.slider.offsetLeft; 5947 this.scrollLeft = this.slider.scrollLeft; 5948 this.mouseDown = true; 5949 }, 5950 ['@mouseout.self']() { 5951 this.mouseDown = false;
5952 }, 5953 ['@mouseup']() { 5954 this.mouseDown = false; 5955 }, 5956 ['@mousemove'](e) { 5957 e.preventDefault(); 5958 if (!this.mouseDown) { 5959 return; 5960 } 5961 const x = e.pageX - this.slider.offsetLeft; 5962 const scroll = x - this.startX; 5963 const scrollLeft = this.scrollLeft - scroll; 5964 5965 if (scrollLeft > this.maxScroll) { 5966 this.slider.scrollLeft = this.maxScroll; 5967 return 5968 } 5969 this.slider.scrollLeft = this.scrollLeft - scroll; 5970 }, 5971 ['@onselectstart']() { 5972 return false; 5973 } 5974 }, 5975 resizeEvent() { 5976 Array.from(this.$root.querySelectorAll('.snap')).forEach(sli
5976der => { 5977 slider.scrollLeft = 0; 5978 }) 5979 } 5980 } 5981 ); 5982 } 5983 5984 </script>
5984 5985 5986<div x-data="initConfigurableSwatchOptions_371430()" x-defer="intersect" x-init="findAllowedAttributeOptions(); initShowSwatchesIntersect();" @private-content-loaded.window="onGetCartData($event.detail.data)" @resize.window="resizeEvent()" class="mb-2 relative"> 5987 <div> 5988 5989 <div class="swatch-attribute border-t last:border-b border-container 5990 size"> 5991 <div class="w-full overflow-x-hidden swatch-attribute-options min-h-14"> 5992 <template x-if="showSwatches"> 5993 <div class="flex flex-nowrap w-full overflow-auto snap items-center py-1" id="attribute-label-371430-170" role="radiogroup" x-bind="scrollEvents" aria-label="Size"> 5994 <template x-for="(item, index) in optionConfig.attributes[170].options" :key="item.id"> 5995 5996<div x-id="['attribute-option-371430-'+item.id]"> 5997 <template x-if="optionIsEnabled(170, item.id) && optionIsActive(170, item.id)"> 5998 <label :for="$id('attribute-option-371430-'+item.id)" class="swatch-option relative shadow-sm cursor-pointer select-none bg-container-lighter product-option-value-label" :class="{ 5999 'border-container-lighter ring ring-primary/50': 6000 (selectedValues[170] === item.id), 6001 'border-container-darker': 6002 (selectedValues[170] !== item.id), 6003 'w-6 h-6' : !isTextSwatch(170, item.id), 6004 'border-container-lighter ring ring-primary/75' : focusedLabel === item.id 6005 }" :style="getSwatchBackgroundStyle('170',item.id)" @mouseenter.self="activeTooltipItem = { 6006 attribute: '170', 6007 item: item.id, 6008 index 6009 }; tooltipPositionElement = $event.target;
6009" @mouseleave.self="activeTooltipItem = false"> 6010 <input :id="$id('attribute-option-371430-'+item.id)" :value="item.id" name="super_attribute[170]" type="radio" class="inline-block absolute p-0 border-0 focus:border-0 focus:ring-0 product-option-value-input" style="z-index:-1" x-on:focus="focusLabel(item.id)" x-on:blur="blurLabel()" x-on:change="changeOption(170, $event.target.value)" x-on:click="clearOptionIfActive(170, item.id)" x-model="selectedValues[170]" :required="getAllowedAttributeOptions(170).filter( 6011 attributeOption => selectedValues[attributeOption] 6012 ).length === 0" :aria-label="getSwatchText(170, item.id)" aria-describedby="attribute-label-371430-170"> 6013 <template x-if="isTextSwatch(170, item.id)"> 6014 <div x-html="getSwatchText(170, item.id)" class="pointer-events-none select-none whitespace-nowrap" aria-hidden="true"></div> 6015 </template> 6016 </label> 6017 </template> 6018 6019 <template x-if="optionIsEnabled(170, item.id) && !optionIsActive(170, item.id)"> 6020 <div class="relative border-2 shadow-sm opacity-50 cursor-pointer select-none border-container-darker swatch-option bg-container-lighter" :class="{ 6021 'w-6 h-6' : !isTextSwatch(170, item.id), 6022 }" :style="getSwatchBackgroundStyle('170',item.id)"> 6023 <div x-html="getSwatchText(170, item.id)" class="whitespace-nowrap" :class="{ 'sr-only' : !isTextSwatch(170, item.id) }"></div> 6024 <svg class="absolute inset-0 w-full h-full text-gray-500 bg-white/25"> 6025 <line x1="0" y1="100%" x2="100%" y2="0" class="stroke-current stroke-1"></line> 6026 </svg> 6027 </div> 6028 </template> 6029</div> 6030 </template> 6031 </div> 6032 </template> 6033 </div> 6034 </div> 6035 </div> 6036 <template x-if="isTooltipVisible()"> 6037 <div class="absolute pointer-events-none select-none" :style="getTooltipPosition()"> 6038 <div class="shadow-lg"> 6039 <div class="absolute top-0 left-0 z-10 min-w-20 p-2 -mt-6 text-sm leading-tight text-black 6040 transform -translate-x-1/2 -translate-y-full bg-white rounded-lg shadow-lg text-center" :class="{'-translate-x-5' : isFirstItemCol()}"> 6041 <template x-if="isVisualSwatch(activeTooltipItem.attribute, activeTooltipItem.item)"> 6042 <div class="inline-block border shadow-sm border-container" :style="getTooltipImageStyle(activeTooltipItem.attribute, activeTooltipItem.item)"></div> 6043 </template> 6044 <span class="mx-2 text-lg font-semibold whitespace-nowrap subtitle" x-html="getTooltipLabel()"></span> 6045 </div> 6046 <svg class="absolute z-10 w-8 h-8 text-white transform -translate-x-1/5 6047 -translate-y-8 fill-current stroke-current" width="12" height="12"> 6048 <rect x="12" y="-12" width="12" height="12" transform="rotate(45)" class="shadow-xl"></rect> 6049 </svg> 6050 </div> 6051 </div> 6052</template> 6053 </div> 6054 6055 6056 6057 <div class="pt-1 text-gray-900" x-data="initPriceBox()" x-defer="intersect" @update-prices-371430.window="updatePrice($event.detail);"> 6058 <div class="price-box price-final_price" data-role="priceBox" data-product-id="371430" data-price-box="product-id-371430"><div x-data="{ priceIsZero: false }" @update-prices-371430.window="priceIsZero = ($event.detail?.finalPrice?.amount <= 0.0001)"> 6059 <div x-show="!priceIsZero"> 6060 6061 <span class="normal-price "> 6062 <span x-data x-id="['product\u002Dprice\u002D371430']" class="price-container price-final_price tax weee"> 6063 <span :id="$id('product\u002Dprice\u002D371430')" data-price-amount="1099.99" data-price-type="finalPrice" class="price-wrapper "><span class="price">R Â 1,099.99</span></span> 6064 </span> 6065 </span> 6066 6067 6068 </div> 6069</div> 6070</div> </div> 6071 6072 <div class="mt-auto pt-3 flex flex-wrap items-center gap-y-4 gap-x-2 6073 "> 6074 <button class="w-auto btn btn-primary justify-center text-sm mr-auto" title="Add to Cart Vans Authentic Denim Sneaker Mens Blue/White" aria-label="Add to Cart Vans Authentic Denim Sneaker Mens Blue/White" data-addto="cart"> 6075 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 6076 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
60760 014 0z"></path> 6077</svg> 6078 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 6079 Add to Cart </span> 6080 </button> 6081 6082 <div class="flex flex-wrap gap-2"> 6083 <button x-data="initWishlist()" x-defer="intersect" @click.prevent="addToWishlist(371430)" aria-label="Add to Wish List Vans Authentic Denim Sneaker Mens Blue/White" type="button" class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex shrink-0 items-center justify-center text-gray-500 hover:text-red-600" data-addto="wishlist"> 6084 <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" fill="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 6085 <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"></path> 6086</svg> 6087 </button> 6088 </div> 6089 </div> 6090 </div> 6091</form> 6092 </div> 6093 <div class="js_slide flex flex-col shrink-0 w-1/2 mr-2 py-1 lg:w-1/3 xl:w-1/4" role="group" :aria-hidden="7 > active && 7 <= (active + pageSize) ? 'false' : 'true'" aria-label="Item 7" :aria-describedby="`slide-desc-371429-${$id('slider-id')}`"> 6094 6095<form method="post" action="https://www.studio-88.co.za/checkout/cart/add/uenc/%25uenc%25/product/371429/" class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full "> 6096 <input name="form_key" type="hidden" value="jQaPIgQq8BM99Mzs"> <input type="hidden" name="product" value="371429"> 6097 <a href="https://www.studio-88.co.za/puma-rebound-slam-lo-sneaker-youth-archive-green" title="Puma Rebound Slam Lo Sneaker Youth Archive Green" class="product photo product-item-photo block mx-auto mb-3 " tabindex="-1"> 6098 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-371429.window="$root.src = $event.detail" src="https://www.studio-88.co.za/media/catalog/product/cache/bd5b7e5d15290d4083c2c8fbcfc68244/p/m/pma8317yag-puma-rebound-slam-lo-jr-grn-40829205-v1_jpg.jpg" loading="lazy" width="360" height="360" alt="PMA8317YAG-PUMA-REBOUND-SLAM-LO-JR-GRN-40829205-V1" title="PMA8317YAG-PUMA-REBOUND-SLAM-LO-JR-GRN-40829205-V1"> 6099 </a> 6100 <div class="product-info equal flex flex-col grow"> 6101 <div class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 6102 <a class="product-item-link" href="https://www.studio-88.co.za/puma-rebound-slam-lo-sneaker-youth-archive-green" :id="`slide-desc-371429-${$id('slider-id')}`"> 6103 Puma Rebound Slam Lo Sneaker Youth Archive Green </a> 6104 </div> 6105 6106 <div class="py-1 mx-auto "> 6107 </div> 6108 6109 6110
6110<script> 6111 function initConfigurableSwatchOptions_371429() { 6112 const configurableOptionsComponent = initConfigurableOptions( 6113 '371429', 6114 {"attributes":{"170":{"id":"170","code":"size","label":"Size","options":[{"id":"122","label":"Size 3","products":["371410"]},{"id":"123","label":"Size 4","products":["371411"]},{"id":"124","label":"Size 5","products":["371412"]}],"position":"0"}},"template":"R \u00a0<%- data.price %>","currencyFormat":"R \u00a0%s","optionPrices":{"371410":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371411":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99},"tierPrices":[],"msrpPrice":{"amount":0}},"371412":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99},"tierPrices":[],"msrpPrice":{"amount":0}}},"priceFormat":{"pattern":"R \u00a0%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"prices":{"baseOldPrice":{"amount":999.99},"oldPrice":{"amount":999.99},"basePrice":{"amount":999.99},"finalPrice":{"amount":999.99}},"productId":"371429","chooseText":"Choose an Option...","images":[],"index":{"371410":{"170":"122"},"371411":{"170":"123"},"371412":{"170":"124"}},"salable":[],"canDisplayShowOutOfStockStatus":false,"channel":"website","salesChannelCode":"base","sku":{"371410":"PMA8317YAG_030","371411":"PMA8317YAG_040","371412":"PMA8317YAG_050"},"label_reload":"https:\/\/www.studio-88.co.za\/amasty_label\/ajax\/label\/","label_category":".product-image-container, .product-item-photo, .cdz-product-top","label_product":".fotorama__stage, #amasty-main-container","original_product_id":"371429","amastyAdditionalConfig":{"isShowDefaultStatus":true,"outOfStockVisibility":1,"isProductPage":false}} ); 6115 const swatchOptionsComponent = initSwatchOptions({"170":{"122":{"type":"0","value":"Size 3","label":"Size 3"},"123":{"type":"0","value":"Size 4","label":"Size 4"},"124":{"type":"0","value":"Size 5","label":"Size 5"},"additional_data":"{\"swatch_input_type\":\"text\",\"update_product_preview_image\":\"0\",\"use_product_image_for_swatch\":0}"}}); 6116 6117 return Object.assign( 6118 configurableOptionsComponent, 6119 swatchOptionsComponent, 6120 { 6121 mediaCallback: "https\u003A\u002F\u002Fwww.studio\u002D88.co.za\u002Fswatches\u002Fajax\u002Fmedia\u002F", 6122 changeOption(optionId, value, skipUpdateGallery) { 6123 if (value === '') { 6124 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, optionId) 6125 } else { 6126 this.selectedValues[optionId] = value; 6127 } 6128 this.findSimpleIndex(); 6129 this.findAllowedAttributeOptions(); 6130 this.updatePrices(); 6131 !skipUpdateGallery && this.updateGallery(); 6132 window.dispatchEvent(new CustomEvent('listing-configurable-selection-changed', { 6133 detail: { 6134 productId: this.productId, 6135 optionId, 6136 value, 6137 productIndex: this.productIndex, 6138 selectedValues: this.selectedValues, 6139 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 6140 } 6141 })); 6142 }, 6143 updateGallery() { 6144 if (!this.productIndex) { 6145 return; 6146 } 6147 6148 fetch(`${this.mediaCallback}?product_id=${this.productIndex}&isAjax=true`, { 6149 method: 'GET', 6150 headers: { 6151 'Content-Type': 'application/json', 6152 'X-Requested-With': 'XMLHttpRequest' 6153 } 6154 } 6155 ).then(response => { 6156 return response.json() 6157 } 6158 ).then(data => { 6159 if (data.errors) { 6160 // non critical failure only console logged 6161 console.warn(data.errors); 6162 } else { 6163 const image = data && data.medium; 6164 6165 image && window.dispatchEvent( 6166 new CustomEvent( 6167 "update-gallery-371429", 6168 {detail: image} 6169 ) 6170 ); 6171 } 6172 }).catch(error => { 6173 console.warn(error) 6174 }); 6175 }, 6176 preselectQuerystringItems() { 6177 // pre-select option like ?size=167 6178 const urlQueryParams = new URLSearchParams(window.location.search.replace('?', '')); 6179 Object.values(this.optionConfig.attributes).map(attribute => { 6180 // Don't update images on load, since PLPs already set the main image to the selected options 6181 const skipUpdateGallery = true; 6182 urlQueryParams.get(attribute.code) && 6183 this.changeOption(attribute.id, urlQueryParams.get(attribute.code), skipUpdateGallery); 6184 }); 6185 }, 6186 mouseDown: false, 6187 startX: 0, 6188 maxScroll: 0, 6189 scrollLeft: null, 6190 slider: null, 6191 scrollEvents: { 6192 ['@mousedown'](e) { 6193 this.slider = e.target.closest('.snap'); 6194 if (!this.slider) { 6195 return; 6196 } 6197 this.maxScroll = this.slider.scrollWidth - this.slider.offsetWidth; 6198 this.startX = e.pageX - this.slider.offsetLeft; 6199 this.scrollLeft = this.slider.scrollLeft; 6200 this.mouseDown = true; 6201 }, 6202 ['@mouseout.self']() { 6203 this.mouseDown = false;
6204 }, 6205 ['@mouseup']() { 6206 this.mouseDown = false; 6207 }, 6208 ['@mousemove'](e) { 6209 e.preventDefault(); 6210 if (!this.mouseDown) { 6211 return; 6212 } 6213 const x = e.pageX - this.slider.offsetLeft; 6214 const scroll = x - this.startX; 6215 const scrollLeft = this.scrollLeft - scroll; 6216 6217 if (scrollLeft > this.maxScroll) { 6218 this.slider.scrollLeft = this.maxScroll; 6219 return 6220 } 6221 this.slider.scrollLeft = this.scrollLeft - scroll; 6222 }, 6223 ['@onselectstart']() { 6224 return false; 6225 } 6226 }, 6227 resizeEvent() { 6228 Array.from(this.$root.querySelectorAll('.snap')).forEach(sli
6228der => { 6229 slider.scrollLeft = 0; 6230 }) 6231 } 6232 } 6233 ); 6234 } 6235 6236 </script>
6236 6237 6238<div x-data="initConfigurableSwatchOptions_371429()" x-defer="intersect" x-init="findAllowedAttributeOptions(); initShowSwatchesIntersect();" @private-content-loaded.window="onGetCartData($event.detail.data)" @resize.window="resizeEvent()" class="mb-2 relative"> 6239 <div> 6240 6241 <div class="swatch-attribute border-t last:border-b border-container 6242 size"> 6243 <div class="w-full overflow-x-hidden swatch-attribute-options min-h-14"> 6244 <template x-if="showSwatches"> 6245 <div class="flex flex-nowrap w-full overflow-auto snap items-center py-1" id="attribute-label-371429-170" role="radiogroup" x-bind="scrollEvents" aria-label="Size"> 6246 <template x-for="(item, index) in optionConfig.attributes[170].options" :key="item.id"> 6247 6248<div x-id="['attribute-option-371429-'+item.id]"> 6249 <template x-if="optionIsEnabled(170, item.id) && optionIsActive(170, item.id)"> 6250 <label :for="$id('attribute-option-371429-'+item.id)" class="swatch-option relative shadow-sm cursor-pointer select-none bg-container-lighter product-option-value-label" :class="{ 6251 'border-container-lighter ring ring-primary/50': 6252 (selectedValues[170] === item.id), 6253 'border-container-darker': 6254 (selectedValues[170] !== item.id), 6255 'w-6 h-6' : !isTextSwatch(170, item.id), 6256 'border-container-lighter ring ring-primary/75' : focusedLabel === item.id 6257 }" :style="getSwatchBackgroundStyle('170',item.id)" @mouseenter.self="activeTooltipItem = { 6258 attribute: '170', 6259 item: item.id, 6260 index 6261 }; tooltipPositionElement = $event.target;
6261" @mouseleave.self="activeTooltipItem = false"> 6262 <input :id="$id('attribute-option-371429-'+item.id)" :value="item.id" name="super_attribute[170]" type="radio" class="inline-block absolute p-0 border-0 focus:border-0 focus:ring-0 product-option-value-input" style="z-index:-1" x-on:focus="focusLabel(item.id)" x-on:blur="blurLabel()" x-on:change="changeOption(170, $event.target.value)" x-on:click="clearOptionIfActive(170, item.id)" x-model="selectedValues[170]" :required="getAllowedAttributeOptions(170).filter( 6263 attributeOption => selectedValues[attributeOption] 6264 ).length === 0" :aria-label="getSwatchText(170, item.id)" aria-describedby="attribute-label-371429-170"> 6265 <template x-if="isTextSwatch(170, item.id)"> 6266 <div x-html="getSwatchText(170, item.id)" class="pointer-events-none select-none whitespace-nowrap" aria-hidden="true"></div> 6267 </template> 6268 </label> 6269 </template> 6270 6271 <template x-if="optionIsEnabled(170, item.id) && !optionIsActive(170, item.id)"> 6272 <div class="relative border-2 shadow-sm opacity-50 cursor-pointer select-none border-container-darker swatch-option bg-container-lighter" :class="{ 6273 'w-6 h-6' : !isTextSwatch(170, item.id), 6274 }" :style="getSwatchBackgroundStyle('170',item.id)"> 6275 <div x-html="getSwatchText(170, item.id)" class="whitespace-nowrap" :class="{ 'sr-only' : !isTextSwatch(170, item.id) }"></div> 6276 <svg class="absolute inset-0 w-full h-full text-gray-500 bg-white/25"> 6277 <line x1="0" y1="100%" x2="100%" y2="0" class="stroke-current stroke-1"></line> 6278 </svg> 6279 </div> 6280 </template> 6281</div> 6282 </template> 6283 </div> 6284 </template> 6285 </div> 6286 </div> 6287 </div> 6288 <template x-if="isTooltipVisible()"> 6289 <div class="absolute pointer-events-none select-none" :style="getTooltipPosition()"> 6290 <div class="shadow-lg"> 6291 <div class="absolute top-0 left-0 z-10 min-w-20 p-2 -mt-6 text-sm leading-tight text-black 6292 transform -translate-x-1/2 -translate-y-full bg-white rounded-lg shadow-lg text-center" :class="{'-translate-x-5' : isFirstItemCol()}"> 6293 <template x-if="isVisualSwatch(activeTooltipItem.attribute, activeTooltipItem.item)"> 6294 <div class="inline-block border shadow-sm border-container" :style="getTooltipImageStyle(activeTooltipItem.attribute, activeTooltipItem.item)"></div> 6295 </template> 6296 <span class="mx-2 text-lg font-semibold whitespace-nowrap subtitle" x-html="getTooltipLabel()"></span> 6297 </div> 6298 <svg class="absolute z-10 w-8 h-8 text-white transform -translate-x-1/5 6299 -translate-y-8 fill-current stroke-current" width="12" height="12"> 6300 <rect x="12" y="-12" width="12" height="12" transform="rotate(45)" class="shadow-xl"></rect> 6301 </svg> 6302 </div> 6303 </div> 6304</template> 6305 </div> 6306 6307 6308 6309 <div class="pt-1 text-gray-900" x-data="initPriceBox()" x-defer="intersect" @update-prices-371429.window="updatePrice($event.detail);"> 6310 <div class="price-box price-final_price" data-role="priceBox" data-product-id="371429" data-price-box="product-id-371429"><div x-data="{ priceIsZero: false }" @update-prices-371429.window="priceIsZero = ($event.detail?.finalPrice?.amount <= 0.0001)"> 6311 <div x-show="!priceIsZero"> 6312 6313 <span class="normal-price "> 6314 <span x-data x-id="['product\u002Dprice\u002D371429']" class="price-container price-final_price tax weee"> 6315 <span :id="$id('product\u002Dprice\u002D371429')" data-price-amount="999.99" data-price-type="finalPrice" class="price-wrapper "><span class="price">R Â 999.99</span></span> 6316 </span> 6317 </span> 6318 6319 6320 </div> 6321</div> 6322</div> </div> 6323 6324 <div class="mt-auto pt-3 flex flex-wrap items-center gap-y-4 gap-x-2 6325 "> 6326 <button class="w-auto btn btn-primary justify-center text-sm mr-auto" title="Add to Cart Puma Rebound Slam Lo Sneaker Youth Archive Green" aria-label="Add to Cart Puma Rebound Slam Lo Sneaker Youth Archive Green" data-addto="cart"> 6327 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 6328 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
63280 014 0z"></path> 6329</svg> 6330 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 6331 Add to Cart </span> 6332 </button> 6333 6334 <div class="flex flex-wrap gap-2"> 6335 <button x-data="initWishlist()" x-defer="intersect" @click.prevent="addToWishlist(371429)" aria-label="Add to Wish List Puma Rebound Slam Lo Sneaker Youth Archive Green" type="button" class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex shrink-0 items-center justify-center text-gray-500 hover:text-red-600" data-addto="wishlist"> 6336 <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" fill="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 6337 <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"></path> 6338</svg> 6339 </button> 6340 </div> 6341 </div> 6342 </div> 6343</form> 6344 </div> 6345 <div class="js_slide flex flex-col shrink-0 w-1/2 mr-2 py-1 lg:w-1/3 xl:w-1/4" role="group" :aria-hidden="8 > active && 8 <= (active + pageSize) ? 'false' : 'true'" aria-label="Item 8" :aria-describedby="`slide-desc-371428-${$id('slider-id')}`"> 6346 6347<form method="post" action="https://www.studio-88.co.za/checkout/cart/add/uenc/%25uenc%25/product/371428/" class="item product product-item product_addtocart_form card card-interactive flex flex-col w-full "> 6348 <input name="form_key" type="hidden" value="jQaPIgQq8BM99Mzs"> <input type="hidden" name="product" value="371428"> 6349 <a href="https://www.studio-88.co.za/nike-air-max-portal-sneaker-womens-sail-light-brown" title="Nike Air Max Portal Sneaker Womens Sail/Light Brown" class="product photo product-item-photo block mx-auto mb-3 " tabindex="-1"> 6350 <img class="hover:shadow-sm object-contain product-image-photo" x-data="" @update-gallery-371428.window="$root.src = $event.detail" src="https://www.studio-88.co.za/media/catalog/product/cache/bd5b7e5d15290d4083c2c8fbcfc68244/n/k/nkk3253lb-nike-air-max-portal-sail-tbrn-iv5360-100-v1_jpg.jpg" loading="lazy" width="360" height="360" alt="NKK3253LB-NIKE-AIR-MAX-PORTAL-SAIL-TBRN-IV5360-100-V1" title="NKK3253LB-NIKE-AIR-MAX-PORTAL-SAIL-TBRN-IV5360-100-V1"> 6351 </a> 6352 <div class="product-info equal flex flex-col grow"> 6353 <div class="mt-2 mb-1 items-center justify-center text-primary font-semibold text-lg text-center "> 6354 <a class="product-item-link" href="https://www.studio-88.co.za/nike-air-max-portal-sneaker-womens-sail-light-brown" :id="`slide-desc-371428-${$id('slider-id')}`"> 6355 Nike Air Max Portal Sneaker Womens Sail/Light Brown </a> 6356 </div> 6357 6358 <div class="py-1 mx-auto "> 6359 </div> 6360 6361 6362
6362<script> 6363 function initConfigurableSwatchOptions_371428() { 6364 const configurableOptionsComponent = initConfigurableOptions( 6365 '371428', 6366 {"attributes":{"170":{"id":"170","code":"size","label":"Size","options":[{"id":"124","label":"Size 5","products":["371404"]}],"position":"0"}},"template":"R \u00a0<%- data.price %>","currencyFormat":"R \u00a0%s","optionPrices":{"371404":{"baseOldPrice":{"amount":2199.99},"oldPrice":{"amount":2199.99},"basePrice":{"amount":2199.99},"finalPrice":{"amount":2199.99},"tierPrices":[],"msrpPrice":{"amount":0}}},"priceFormat":{"pattern":"R \u00a0%s","precision":2,"requiredPrecision":2,"decimalSymbol":".","groupSymbol":",","groupLength":3,"integerRequired":false},"prices":{"baseOldPrice":{"amount":2199.99},"oldPrice":{"amount":2199.99},"basePrice":{"amount":2199.99},"finalPrice":{"amount":2199.99}},"productId":"371428","chooseText":"Choose an Option...","images":[],"index":{"371404":{"170":"124"}},"salable":[],"canDisplayShowOutOfStockStatus":false,"channel":"website","salesChannelCode":"base","sku":{"371404":"NKK3253LB__050"},"label_reload":"https:\/\/www.studio-88.co.za\/amasty_label\/ajax\/label\/","label_category":".product-image-container, .product-item-photo, .cdz-product-top","label_product":".fotorama__stage, #amasty-main-container","original_product_id":"371428","amastyAdditionalConfig":{"isShowDefaultStatus":true,"outOfStockVisibility":1,"isProductPage":false}} ); 6367 const swatchOptionsComponent = initSwatchOptions({"170":{"124":{"type":"0","value":"Size 5","label":"Size 5"},"additional_data":"{\"swatch_input_type\":\"text\",\"update_product_preview_image\":\"0\",\"use_product_image_for_swatch\":0}"}}); 6368 6369 return Object.assign( 6370 configurableOptionsComponent, 6371 swatchOptionsComponent, 6372 { 6373 mediaCallback: "https\u003A\u002F\u002Fwww.studio\u002D88.co.za\u002Fswatches\u002Fajax\u002Fmedia\u002F", 6374 changeOption(optionId, value, skipUpdateGallery) { 6375 if (value === '') { 6376 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, optionId) 6377 } else { 6378 this.selectedValues[optionId] = value; 6379 } 6380 this.findSimpleIndex(); 6381 this.findAllowedAttributeOptions(); 6382 this.updatePrices(); 6383 !skipUpdateGallery && this.updateGallery(); 6384 window.dispatchEvent(new CustomEvent('listing-configurable-selection-changed', { 6385 detail: { 6386 productId: this.productId, 6387 optionId, 6388 value, 6389 productIndex: this.productIndex, 6390 selectedValues: this.selectedValues, 6391 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 6392 } 6393 })); 6394 }, 6395 updateGallery() { 6396 if (!this.productIndex) { 6397 return; 6398 } 6399 6400 fetch(`${this.mediaCallback}?product_id=${this.productIndex}&isAjax=true`, { 6401 method: 'GET', 6402 headers: { 6403 'Content-Type': 'application/json', 6404 'X-Requested-With': 'XMLHttpRequest' 6405 } 6406 } 6407 ).then(response => { 6408 return response.json() 6409 } 6410 ).then(data => { 6411 if (data.errors) { 6412 // non critical failure only console logged 6413 console.warn(data.errors); 6414 } else { 6415 const image = data && data.medium; 6416 6417 image && window.dispatchEvent( 6418 new CustomEvent( 6419 "update-gallery-371428", 6420 {detail: image} 6421 ) 6422 ); 6423 } 6424 }).catch(error => { 6425 console.warn(error) 6426 }); 6427 }, 6428 preselectQuerystringItems() { 6429 // pre-select option like ?size=167 6430 const urlQueryParams = new URLSearchParams(window.location.search.replace('?', '')); 6431 Object.values(this.optionConfig.attributes).map(attribute => { 6432 // Don't update images on load, since PLPs already set the main image to the selected options 6433 const skipUpdateGallery = true; 6434 urlQueryParams.get(attribute.code) && 6435 this.changeOption(attribute.id, urlQueryParams.get(attribute.code), skipUpdateGallery); 6436 }); 6437 }, 6438 mouseDown: false, 6439 startX: 0, 6440 maxScroll: 0, 6441 scrollLeft: null, 6442 slider: null, 6443 scrollEvents: { 6444 ['@mousedown'](e) { 6445 this.slider = e.target.closest('.snap'); 6446 if (!this.slider) { 6447 return; 6448 } 6449 this.maxScroll = this.slider.scrollWidth - this.slider.offsetWidth; 6450 this.startX = e.pageX - this.slider.offsetLeft; 6451 this.scrollLeft = this.slider.scrollLeft; 6452 this.mouseDown = true; 6453 }, 6454 ['@mouseout.self']() { 6455 this.mouseDown = false;
6456 }, 6457 ['@mouseup']() { 6458 this.mouseDown = false; 6459 }, 6460 ['@mousemove'](e) { 6461 e.preventDefault(); 6462 if (!this.mouseDown) { 6463 return; 6464 } 6465 const x = e.pageX - this.slider.offsetLeft; 6466 const scroll = x - this.startX; 6467 const scrollLeft = this.scrollLeft - scroll; 6468 6469 if (scrollLeft > this.maxScroll) { 6470 this.slider.scrollLeft = this.maxScroll; 6471 return 6472 } 6473 this.slider.scrollLeft = this.scrollLeft - scroll; 6474 }, 6475 ['@onselectstart']() { 6476 return false; 6477 } 6478 }, 6479 resizeEvent() { 6480 Array.from(this.$root.querySelectorAll('.snap')).forEach(sli
6480der => { 6481 slider.scrollLeft = 0; 6482 }) 6483 } 6484 } 6485 ); 6486 } 6487 6488 </script>
6488 6489 6490<div x-data="initConfigurableSwatchOptions_371428()" x-defer="intersect" x-init="findAllowedAttributeOptions(); initShowSwatchesIntersect();" @private-content-loaded.window="onGetCartData($event.detail.data)" @resize.window="resizeEvent()" class="mb-2 relative"> 6491 <div> 6492 6493 <div class="swatch-attribute border-t last:border-b border-container 6494 size"> 6495 <div class="w-full overflow-x-hidden swatch-attribute-options min-h-14"> 6496 <template x-if="showSwatches"> 6497 <div class="flex flex-nowrap w-full overflow-auto snap items-center py-1" id="attribute-label-371428-170" role="radiogroup" x-bind="scrollEvents" aria-label="Size"> 6498 <template x-for="(item, index) in optionConfig.attributes[170].options" :key="item.id"> 6499 6500<div x-id="['attribute-option-371428-'+item.id]"> 6501 <template x-if="optionIsEnabled(170, item.id) && optionIsActive(170, item.id)"> 6502 <label :for="$id('attribute-option-371428-'+item.id)" class="swatch-option relative shadow-sm cursor-pointer select-none bg-container-lighter product-option-value-label" :class="{ 6503 'border-container-lighter ring ring-primary/50': 6504 (selectedValues[170] === item.id), 6505 'border-container-darker': 6506 (selectedValues[170] !== item.id), 6507 'w-6 h-6' : !isTextSwatch(170, item.id), 6508 'border-container-lighter ring ring-primary/75' : focusedLabel === item.id 6509 }" :style="getSwatchBackgroundStyle('170',item.id)" @mouseenter.self="activeTooltipItem = { 6510 attribute: '170', 6511 item: item.id, 6512 index 6513 }; tooltipPositionElement = $event.target;
6513" @mouseleave.self="activeTooltipItem = false"> 6514 <input :id="$id('attribute-option-371428-'+item.id)" :value="item.id" name="super_attribute[170]" type="radio" class="inline-block absolute p-0 border-0 focus:border-0 focus:ring-0 product-option-value-input" style="z-index:-1" x-on:focus="focusLabel(item.id)" x-on:blur="blurLabel()" x-on:change="changeOption(170, $event.target.value)" x-on:click="clearOptionIfActive(170, item.id)" x-model="selectedValues[170]" :required="getAllowedAttributeOptions(170).filter( 6515 attributeOption => selectedValues[attributeOption] 6516 ).length === 0" :aria-label="getSwatchText(170, item.id)" aria-describedby="attribute-label-371428-170"> 6517 <template x-if="isTextSwatch(170, item.id)"> 6518 <div x-html="getSwatchText(170, item.id)" class="pointer-events-none select-none whitespace-nowrap" aria-hidden="true"></div> 6519 </template> 6520 </label> 6521 </template> 6522 6523 <template x-if="optionIsEnabled(170, item.id) && !optionIsActive(170, item.id)"> 6524 <div class="relative border-2 shadow-sm opacity-50 cursor-pointer select-none border-container-darker swatch-option bg-container-lighter" :class="{ 6525 'w-6 h-6' : !isTextSwatch(170, item.id), 6526 }" :style="getSwatchBackgroundStyle('170',item.id)"> 6527 <div x-html="getSwatchText(170, item.id)" class="whitespace-nowrap" :class="{ 'sr-only' : !isTextSwatch(170, item.id) }"></div> 6528 <svg class="absolute inset-0 w-full h-full text-gray-500 bg-white/25"> 6529 <line x1="0" y1="100%" x2="100%" y2="0" class="stroke-current stroke-1"></line> 6530 </svg> 6531 </div> 6532 </template> 6533</div> 6534 </template> 6535 </div> 6536 </template> 6537 </div> 6538 </div> 6539 </div> 6540 <template x-if="isTooltipVisible()"> 6541 <div class="absolute pointer-events-none select-none" :style="getTooltipPosition()"> 6542 <div class="shadow-lg"> 6543 <div class="absolute top-0 left-0 z-10 min-w-20 p-2 -mt-6 text-sm leading-tight text-black 6544 transform -translate-x-1/2 -translate-y-full bg-white rounded-lg shadow-lg text-center" :class="{'-translate-x-5' : isFirstItemCol()}"> 6545 <template x-if="isVisualSwatch(activeTooltipItem.attribute, activeTooltipItem.item)"> 6546 <div class="inline-block border shadow-sm border-container" :style="getTooltipImageStyle(activeTooltipItem.attribute, activeTooltipItem.item)"></div> 6547 </template> 6548 <span class="mx-2 text-lg font-semibold whitespace-nowrap subtitle" x-html="getTooltipLabel()"></span> 6549 </div> 6550 <svg class="absolute z-10 w-8 h-8 text-white transform -translate-x-1/5 6551 -translate-y-8 fill-current stroke-current" width="12" height="12"> 6552 <rect x="12" y="-12" width="12" height="12" transform="rotate(45)" class="shadow-xl"></rect> 6553 </svg> 6554 </div> 6555 </div> 6556</template> 6557 </div> 6558 6559 6560 6561 <div class="pt-1 text-gray-900" x-data="initPriceBox()" x-defer="intersect" @update-prices-371428.window="updatePrice($event.detail);"> 6562 <div class="price-box price-final_price" data-role="priceBox" data-product-id="371428" data-price-box="product-id-371428"><div x-data="{ priceIsZero: false }" @update-prices-371428.window="priceIsZero = ($event.detail?.finalPrice?.amount <= 0.0001)"> 6563 <div x-show="!priceIsZero"> 6564 6565 <span class="normal-price "> 6566 <span x-data x-id="['product\u002Dprice\u002D371428']" class="price-container price-final_price tax weee"> 6567 <span :id="$id('product\u002Dprice\u002D371428')" data-price-amount="2199.99" data-price-type="finalPrice" class="price-wrapper "><span class="price">R Â 2,199.99</span></span> 6568 </span> 6569 </span> 6570 6571 6572 </div> 6573</div> 6574</div> </div> 6575 6576 <div class="mt-auto pt-3 flex flex-wrap items-center gap-y-4 gap-x-2 6577 "> 6578 <button class="w-auto btn btn-primary justify-center text-sm mr-auto" title="Add to Cart Nike Air Max Portal Sneaker Womens Sail/Light Brown" aria-label="Add to Cart Nike Air Max Portal Sneaker Womens Sail/Light Brown" data-addto="cart"> 6579 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true"> 6580 <path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2
65800 014 0z"></path> 6581</svg> 6582 <span class="ml-2 inline md:ml-0 md:hidden lg:ml-2 lg:inline"> 6583 Add to Cart </span> 6584 </button> 6585 6586 <div class="flex flex-wrap gap-2"> 6587 <button x-data="initWishlist()" x-defer="intersect" @click.prevent="addToWishlist(371428)" aria-label="Add to Wish List Nike Air Max Portal Sneaker Womens Sail/Light Brown" type="button" class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex shrink-0 items-center justify-center text-gray-500 hover:text-red-600" data-addto="wishlist"> 6588 <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 20 20" fill="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 6589 <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"></path> 6590</svg> 6591 </button> 6592 </div> 6593 </div> 6594 </div> 6595</form> 6596 </div> 6597 <!---<//?php for ($i = 0; $i < $maxVisibleSlides; $i++): /* Add empty filler slides in case the number of items is not dividable by the pagesize */ ?> 6598 <div :class="js_dummy_slide w-full flex-none <//?= $escaper->escapeJs($slideItemClasses) ?>' : pageFillers > <//?= (int) $i ?//>"> 6599 </div> 6600 <//?php endfor; ?//>--> 6601 </div> 6602 </div> 6603 <div class="flex-none relative w-full mb-5"> 6604 <template x-if="itemCount > pageSize"> 6605 <div class="flex justify-center py-2" :id="$id('slider-nav')"> 6606 <button type="button" aria-label="Previous slide" :disabled="active === 0" class="text-black flex-none p-3" :class="{ 'opacity-25 pointer-events-none' : active === 0 }" @click="scrollPrevious"> 6607 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 6608 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"></path> 6609</svg> 6610 </button> 6611 <div class="hidden sm:flex flex-wrap w-full md:w-auto justify-center"> 6612 <button type="button" :class="{ 6613 'hidden': (pageSize !== 1 && !!(0 % pageSize)) 6614 }" :aria-current="active === 0" :aria-label="hyva.str('Display\u0020slide\u0020\u00251', (0 / pageSize) + 1)" @click="scrollTo(0)"> 6615 <span class="shrink-0 block w-3 h-3 m-4 bg-black bg-opacity-25 rounded-full shadow cursor-pointer" :class="{ 6616 'bg-opacity-100': active === 0, 6617 'bg-opacity-25': active !== 0, 6618 'hidden': (pageSize !== 1 && !!(0 % pageSize)) 6619 }" @click="scrollTo(0)"></span> 6620 </button> 6621 <button type="button" :class="{ 6622 'hidden': (pageSize !== 1 && !!(1 % pageSize)) 6623 }" :aria-current="active === 1" :aria-label="hyva.str('Display\u0020slide\u0020\u00251', (1 / pageSize) + 1)" @click="scrollTo(1)"> 6624 <span class="shrink-0 block w-3 h-3 m-4 bg-black bg-opacity-25 rounded-full shadow cursor-pointer" :class="{ 6625 'bg-opacity-100': active === 1, 6626 'bg-opacity-25': active !== 1, 6627 'hidden': (pageSize !== 1 && !!(1 % pageSize)) 6628 }" @click="scrollTo(1)"></span> 6629 </button> 6630 <button type="button" :class="{ 6631 'hidden': (pageSize !== 1 && !!(2 % pageSize)) 6632 }" :aria-current="active === 2" :aria-label="hyva.str('Display\u0020slide\u0020\u00251', (2 / pageSize) + 1)" @click="scrollTo(2)">
6633 <span class="shrink-0 block w-3 h-3 m-4 bg-black bg-opacity-25 rounded-full shadow cursor-pointer" :class="{ 6634 'bg-opacity-100': active === 2, 6635 'bg-opacity-25': active !== 2, 6636 'hidden': (pageSize !== 1 && !!(2 % pageSize)) 6637 }" @click="scrollTo(2)"></span> 6638 </button> 6639 <button type="button" :class="{ 6640 'hidden': (pageSize !== 1 && !!(3 % pageSize)) 6641 }" :aria-current="active === 3" :aria-label="hyva.str('Display\u0020slide\u0020\u00251', (3 / pageSize) + 1)" @click="scrollTo(3)"> 6642 <span class="shrink-0 block w-3 h-3 m-4 bg-black bg-opacity-25 rounded-full shadow cursor-pointer" :class="{ 6643 'bg-opacity-100': active === 3, 6644 'bg-opacity-25': active !== 3, 6645 'hidden': (pageSize !== 1 && !!(3 % pageSize)) 6646 }" @click="scrollTo(3)"></span> 6647 </button> 6648 <button type="button" :class="{ 6649 'hidden': (pageSize !== 1 && !!(4 % pageSize)) 6650 }" :aria-current="active === 4" :aria-label="hyva.str('Display\u0020slide\u0020\u00251', (4 / pageSize) + 1)" @click="scrollTo(4)"> 6651 <span class="shrink-0 block w-3 h-3 m-4 bg-black bg-opacity-25 rounded-full shadow cursor-pointer" :class="{ 6652 'bg-opacity-100': active === 4, 6653 'bg-opacity-25': active !== 4, 6654 'hidden': (pageSize !== 1 && !!(4 % pageSize)) 6655 }" @click="scrollTo(4)"></span> 6656 </button> 6657 <button type="button" :class="{ 6658 'hidden': (pageSize !== 1 && !!(5 % pageSize)) 6659 }" :aria-current="active === 5" :aria-label="hyva.str('Display\u0020slide\u0020\u00251', (5 / pageSize) + 1)" @click="scrollTo(5)"> 6660 <span class="shrink-0 block w-3 h-3 m-4 bg-black bg-opacity-25 rounded-full shadow cursor-pointer" :class="{ 6661 'bg-opacity-100': active === 5, 6662 'bg-opacity-25': active !== 5, 6663 'hidden': (pageSize !== 1 && !!(5 % pageSize)) 6664 }" @click="scrollTo(5)"></span> 6665 </button> 6666 <button type="button" :class="{ 6667 'hidden': (pageSize !== 1 && !!(6 % pageSize)) 6668 }" :aria-current="active === 6" :aria-label="hyva.str('Display\u0020slide\u0020\u00251', (6 / pageSize) + 1)" @click="scrollTo(6)"> 6669 <span class="shrink-0 block w-3 h-3 m-4 bg-black bg-opacity-25 rounded-full shadow cursor-pointer" :class="{ 6670 'bg-opacity-100': active === 6, 6671 'bg-opacity-25': active !== 6, 6672 'hidden': (pageSize !== 1 && !!(6 % pageSize)) 6673 }" @click="scrollTo(6)"></span> 6674 </button> 6675 <button type="button" :class="{ 6676 'hidden': (pageSize !== 1 && !!(7 % pageSize)) 6677 }" :aria-current="active === 7" :aria-label="hyva.str('Display\u0020slide\u0020\u00251', (7 / pageSize) + 1)" @click="scrollTo(7)"> 6678 <span class="shrink-0 block w-3 h-3 m-4 bg-black bg-opacity-25 rounded-full shadow cursor-pointer" :class="{ 6679 'bg-opacity-100': active === 7, 6680 'bg-opacity-25': active !== 7, 6681 'hidden': (pageSize !== 1 && !!(7 % pageSize)) 6682 }" @click="scrollTo(7)"></span> 6683 </button> 6684 </div> 6685 <button aria-label="Next slide" :disabled="active >= itemCount-pageSize" class="text-black flex-none p-3" :class="{ 'opacity-25 pointer-events-none' : active >= itemCount-pageSize }" @click="scrollNext"> 6686 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5" width="25" height="25" aria-hidden="true"> 6687 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"></path> 6688</svg> 6689 </button> 6690 </div> 6691 </template> 6692 </div>
6693 <span id="magento\framework\view\element\template_1-slider-end" tabindex="-1"></span> 6694 </div> 6695 </div> 6696
6696<script> 6697 'use strict'; 6698 window.addEventListener('DOMContentLoaded', function() { 6699 if (! window.productSliderEventHandlerInitialized) { 6700 window.productSliderEventHandlerInitialized = true; 6701 6702 window.addEventListener('product-add-to-wishlist', (event) => { 6703 const formKey = hyva.getFormKey(); 6704 const postUrl = BASE_URL + 'wishlist/index/add/'; 6705 const productId = event.detail.productId; 6706 6707 fetch(postUrl, { 6708 "headers": { 6709 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 6710 }, 6711 "body": "form_key=" + formKey + "&product=" + productId + "&uenc=" + hyva.getUenc(), 6712 "method": "POST", 6713 "mode": "cors", 6714 "credentials": "include" 6715 }).then(function (response) { 6716 if (response.redirected) { 6717 window.location.href = response.url; 6718 } else if (response.ok) { 6719 return response.json(); 6720 } else { 6721 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 6722 [{ 6723 type: "warning", 6724 text: "Could not add item to wishlist." 6725 }], 5000 6726 ); 6727 } 6728 }).then(function (result) { 6729 if (!result) { 6730 return 6731 } 6732 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 6733 [{ 6734 type: (result.success) ? "success" : "error", 6735 text: (result.success) 6736 ? "Product has been added to your Wish List." : result.error_message 6737 }], 5000 6738 ); 6739 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 6740 }).catch(function (error) { 6741 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 6742 [{ 6743 type: "error", 6744 text: error 6745 }], 5000 6746 ); 6747 }); 6748 }) 6749 6750 } 6751 }); 6752 </script>
6752 6753</section> 6754 6755 </div> 6756 6757 <div class="cmsp363-w-full h-225 cmsp363-overflow-hidden cmsp363-rounded-lg cmsp363-cursor-pointer cmsp363-relative"> 6758 <a href="https://www.studio-88.co.za/soweto-giants" class="cmsp363-w-full cmsp363-flex-shrink-0"> 6759 <picture class="cmsp363-w-full cmsp363-flex-shrink-0"> 6760 <source media="(min-width: 768px)" srcset="https://www.studio-88.co.za/media/slider/OP_KC_Soweto_Giants_Desktop.webp" 6761 type="image/webp"> 6762 <source media="(max-width: 767px)" 6763 srcset="https://www.studio-88.co.za/media/slider/OP_KC_Soweto_Giants_Mobile.webp" type="image/webp"> 6764 <img loading="lazy" 6765 class="cmsp363-w-full cmsp363-h-full cmsp363-object-cover cmsp363-transition-transform cmsp363-duration-500 cmsp363-hover:scale-110 cmsp363-max-w-full" 6766 src="https://www.studio-88.co.za/media/slider/OP_KC_Soweto_Giants_Mobil.webp" alt="Soweto Giants" 6767 decoding="async"> 6768 </picture> 6769 </a> 6770 </div> 6771 6772 <h2 class="cmsp363-text-2xl cmsp363-font-bold cmsp363-text-center cmsp363-my-8">SHOP OUR CATEGORIES</h2> 6773 <div class="cmsp363-grid cmsp363-grid-cols-1 cmsp363-md:grid-cols-4 cmsp363-gap-4"> 6774 <a href="men"><img loading="lazy" src="https://www.studio-88.co.za/media/wysiwyg/shop-cats/MEN_CAT.webp" 6775 alt="Men Category" class="cmsp363-w-full"></a> 6776 <a href="women"><img loading="lazy" 6777 src="https://www.studio-88.co.za/media/wysiwyg/shop-cats/WOMEN_CAT.webp" alt="Women Category" 6778 class="cmsp363-w-full"></a> 6779 <a href="kids"><img loading="lazy" src="https://www.studio-88.co.za/media/wysiwyg/shop-cats/KIDS_CAT.webp" 6780 alt="Kids Category" class="cmsp363-w-full"></a> 6781 <a href="men/accessories"><img loading="lazy" 6782 src="https://www.studio-88.co.za/media/wysiwyg/shop-cats/ACCESSORIES_CAT.webp" 6783 alt="Accessories Category" class="cmsp363-w-full"></a> 6784 </div> 6785 6786 <div class="cmsp363-text-center cmsp363-my-12"> 6787 <h2 class="cmsp363-text-2xl cmsp363-font-bold">FEATURE 88</h2> 6788 <p class="cmsp363-text-gray-600">Stay updated with our <strong>#FEATURE88</strong> brand releases, promotions, and more! 6789 </p> 6790 <a href="feature88" 6791 class="cmsp363-inline-block cmsp363-mt-4 cmsp363-px-6 cmsp363-py-3 cmsp363-bg-red-600 cmsp363-text-white cmsp363-rounded-lg cmsp363-hover:bg-red-700 cmsp363-transition">VISIT OUR 6792 BLOG</a> 6793 </div> 6794 6795 <!-- Blog --> 6796 <div class="cmsp363-mt-6"> 6797 6798 <div class="widget amblog-post-list amblog-element-block custom-feature"> 6799 <!--<div class="amblog-title"> 6800 <//?php if ($headerText): ?> 6801 <//?= $escaper->escapeHtml(__($headerText)) ?> 6802 <//?php else: ?> 6803 <//?= $escaper->escapeHtml(__('Featured posts')) ?> 6804 <//?php endif; ?> 6805 </div>--> 6806 <div class="amblog-list post-slider feature-list"> 6807 <div class="post-items amblog-slick-slider grid grid-cols-1 lg:grid-cols-3 gap-2 lg:gap-4" data-amblog-js="post-slider" data-mage-init='{"amBlogSlider":{ 6808 "rows": 0, 6809 "responsive": [ 6810 { 6811 "breakpoint": 480, 6812 "settings": { 6813 "arrows": false, 6814 "dots": true, 6815 "slidesToShow": 1 6816 } 6817 } 6818 ] 6819 }}'> 6820 <div class="post-item "> 6821 <div class="amasty-contain post-item-details equal-two"> 6822 <a href="https://www.studio-88.co.za/feature88/sharks-unbox-the-pride-everyday-style-big-rewards" title="SHARKS UNBOX THE PRIDE: EVERYDAY STYLE, BIG REWARDS" aria-label="SHARKS UNBOX THE PRIDE: EVERYDAY STYLE, BIG REWARDS" class="post-image"> 6823 <img class="object-fill" src="https://www.studio-88.co.za/media/amasty/blog/cache/S/h/1200/700/Sharks_Competition_Blog_fixed_1.jpg" alt="Sharks Rugby" width="450" height="300"> 6824 </a> 6825 <h4><a class="post-title amblog-headline" href="https://www.studio-88.co.za/feature88/sharks-unbox-the-pride-everyday-style-big-rewards" title="SHARKS UNBOX THE PRIDE: EVERYDAY STYLE, BIG REWARDS" aria-label="SHARKS UNBOX THE PRIDE: EVERYDAY STYLE, BIG REWARDS"> 6826 SHARKS UNBOX THE PRIDE: EVERYDAY STYLE, BIG REWARDS </a></h4> 6827 <div class="amblog-date"> 6828 August 17, 2026 </div> 6829 <div class="short-description"> 6830 Past. Present. Future. Inspired by the iconic 1996 Currie Cup jersey, the 2026 edition brings heritage into a
6830new generation. And now, Studio 88 is giving you the chance to celebrate your Sharks pride in everyday life - and win big while doing it. </div> 6831 <a class="ampost-link pt-5" href="https://www.studio-88.co.za/feature88/sharks-unbox-the-pride-everyday-style-big-rewards" title="Read more" aria-label="Read more"> 6832 Read more </a> 6833 </div> 6834 </div> 6835 <div class="post-item "> 6836 <div class="amasty-contain post-item-details equal-two"> 6837 <a href="https://www.studio-88.co.za/feature88/soccer-central-brings-the-hottest-football-jerseys-together-at-studio-88" title="Soccer Central Brings the Hottest Football Jerseys Together at Studio 88" aria-label="Soccer Central Brings the Hottest Football Jerseys Together at Studio 88" class="post-image"> 6838 <img class="object-fill" src="https://www.studio-88.co.za/media/amasty/blog/cache/P/S/1200/700/PSL_Blog_1.jpg" alt="PSL Blog" width="450" height="300"> 6839 </a> 6840 <h4><a class="post-title amblog-headline" href="https://www.studio-88.co.za/feature88/soccer-central-brings-the-hottest-football-jerseys-together-at-studio-88" title="Soccer Central Brings the Hottest Football Jerseys Together at Studio 88" aria-label="Soccer Central Brings the Hottest Football Jerseys Together at Studio 88"> 6841 Soccer Central Brings the Hottest Football Jerseys Together at Studio 88 </a></h4> 6842 <div class="amblog-date"> 6843 August 28, 2026 </div> 6844 <div class="short-description"> 6845 Football is more than what happens on the pitch. Itâs the jersey you wear, the club you back and the match-day debates that carry on all week. If youâre looking for football jerseys South Africa fans can wear with pride, Studio 88âs Soccer Central range brings local and international football culture together in one place. </div> 6846 <a class="ampost-link pt-5" href="https://www.studio-88.co.za/feature88/soccer-central-brings-the-hottest-football-jerseys-together-at-studio-88" title="Read more" aria-label="Read more"> 6847 Read more </a> 6848 </div> 6849 </div> 6850 <div class="post-item "> 6851 <div class="amasty-contain post-item-details equal-two"> 6852 <a href="https://www.studio-88.co.za/feature88/designed-by-one-of-us-puma-and-manchester-city-unveil-the-2627-third-kit" title="DESIGNED BY ONE OF US: PUMA AND MANCHESTER CITY UNVEIL THE 26/27 THIRD KIT" aria-label="DESIGNED BY ONE OF US: PUMA AND MANCHESTER CITY UNVEIL THE 26/27 THIRD KIT" class="post-image"> 6853 <img class="object-fill" src="https://www.studio-88.co.za/media/amasty/blog/cache/M/a/1200/700/Man_City_Third_Blog_1.jpg" alt="Man City Third Jersey" width="450" height="300"> 6854 </a> 6855 <h4><a class="post-title amblog-headline" href="https://www.studio-88.co.za/feature88/designed-by-one-of-us-puma-and-manchester-city-unveil-the-2627-third-kit" title="DESIGNED BY ONE OF US: PUMA AND MANCHESTER CITY UNVEIL THE 26/27 THIRD KIT" aria-label="DESIGNED BY ONE OF US: PUMA AND MANCHEST
6855ER CITY UNVEIL THE 26/27 THIRD KIT"> 6856 DESIGNED BY ONE OF US: PUMA AND MANCHESTER CITY UNVEIL THE 26/27 THIRD KIT </a></h4> 6857 <div class="amblog-date"> 6858 September 1, 2026 </div> 6859 <div class="short-description"> 6860 Global sports brand <a href="https://www.studio-88.co.za/puma" title="Puma" target="_self">PUMA</a> and Manchester City have today revealed the Club's 26/27 Third Kit and â for the first time in professional football â fans were given the power to design a kit using AI, with the winning fan-created design now revealed as the official Third kit. </div> 6861 <a class="ampost-link pt-5" href="https://www.studio-88.co.za/feature88/designed-by-one-of-us-puma-and-manchester-city-unveil-the-2627-third-kit" title="Read more" aria-label="Read more"> 6862 Read more </a> 6863 </div> 6864 </div> 6865 </div> 6866 </div> 6867 </div> 6868 6869 </div> 6870</div></div></div></div></main><footer class="page-footer"><div data-bind="scope: 'amasty-shipbar-bottom'"> 6871 <!-- ko template: getTemplate() --><!-- /ko --> 6872
6872<script type="text/x-magento-init"> 6873 { 6874 "*": { 6875 "Magento_Ui/js/core/app": {"components":{"amasty-shipbar-bottom":{"component":"Amasty_ShippingBar\/js\/bar","currencySymbol":"R "}}} } 6876 } 6877 </script>
6877 6878</div> 6879<div class="footer content"> 6880<style>.cmsb277-container { 6881width: 100% 6882} 6883@media (min-width: 640px) { 6884.cmsb277-container { 6885max-width: 640px 6886} 6887} 6888@media (min-width: 768px) { 6889.cmsb277-container { 6890max-width: 768px 6891} 6892} 6893@media (min-width: 1024px) { 6894.cmsb277-container { 6895max-width: 1024px 6896} 6897} 6898@media (min-width: 1280px) { 6899.cmsb277-container { 6900max-width: 1280px 6901} 6902} 6903@media (min-width: 1536px) { 6904.cmsb277-container { 6905max-width: 1536px 6906} 6907} 6908.cmsb277-mx-auto { 6909margin-left: auto; 6910margin-right: auto 6911} 6912.cmsb277-flex { 6913display: flex 6914} 6915.cmsb277-inline-flex { 6916display: inline-flex 6917} 6918.cmsb277-grid { 6919display: grid 6920} 6921.cmsb277-hidden { 6922display: none 6923} 6924.cmsb277-w-full { 6925width: 100% 6926} 6927.cmsb277-flex-row { 6928flex-direction: row 6929} 6930.cmsb277-flex-col { 6931flex-direction: column 6932} 6933.cmsb277-flex-wrap { 6934flex-wrap: wrap 6935} 6936.cmsb277-items-center { 6937align-items: center 6938} 6939.cmsb277-items-stretch { 6940align-items: stretch 6941} 6942.cmsb277-justify-center { 6943justify-content: center 6944} 6945.cmsb277-justify-self-end { 6946justify-self: end 6947} 6948.cmsb277-border { 6949border-width: 1px 6950} 6951.cmsb277-bg-black { 6952--tw-bg-opacity: 1; 6953background-color: rgb(0 0 0 / var(--tw-bg-opacity)) 6954} 6955.cmsb277-px-5 { 6956padding-left: 1.25rem; 6957padding-right: 1.25rem 6958} 6959.cmsb277-py-5 { 6960padding-top: 1.25rem; 6961padding-bottom: 1.25rem 6962} 6963.cmsb277-text-white { 6964--tw-text-opacity: 1; 6965color: rgb(255 255 255 / var(--tw-text-opacity)) 6966} 6967@media (min-width: 1024px) { 6968.cmsb277-lg\:w-5\/12 { 6969width: 41.666667% 6970} 6971.cmsb277-lg\:w-7\/12 { 6972width: 58.333333% 6973} 6974.cmsb277-lg\:justify-stretch { 6975justify-content: stretch 6976} 6977.cmsb277-lg\:px-3 { 6978padding-left: 0.75rem; 6979padding-right: 0.75rem 6980} 6981.cmsb277-lg\:py-8 { 6982padding-top: 2rem; 6983padding-bottom: 2rem 6984} 6985}</style> 6986<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><div class="newsletter-signup cmsb277-container cmsb277-bg-black cmsb277-flex cmsb277-flex-row cmsb277-flex-wrap cmsb277-items-center cmsb277-justify-center cmsb277-mx-auto cmsb277-px-5 cmsb277-lg:px-3 cmsb277-py-5 cmsb277-lg:py-8"> 6987 <div class="cmsb277-flex cmsb277-flex-col cmsb277-w-full cmsb277-lg:w-7/12"> 6988 <h3 class="cmsb277-text-white">SIGN UP TO OUR NEWSLETTER TO STAY UPDATED</h3> 6989 </div> 6990 <div class="cmsb277-flex cmsb277-flex-col cmsb277-w-full cmsb277-lg:w-5/12"> 6991 <form id="rm-signup-form" class="cmsb277-flex cmsb277-flex-row cmsb277-flex-wrap cmsb277-items-stretch" action="https://app.remarkety.com/public/subscribers/createcontact/" method="post"> 6992 <div class="news-inputs cmsb277-flex cmsb277-flex-col cmsb277-w-full cmsb277-lg:w-7/12"> 6993 <input type="hidden" name="store_id" value="ym6nvMm7"> 6994 <input type="hidden" name="form_id" value="Wx4AwXdr"> 6995 <input name="email" type="email" required="" id="newsletter-subscribe" class="form-input cmsb277-inline-flex cmsb277-w-full" placeholder="Enter your email address" aria-describedby="footer-newsletter-heading"> 6996 </div> 6997 <div class="btn-subs cmsb277-grid cmsb277-flex-col cmsb277-w-full cmsb277-lg:w-5/12 cmsb277-justify-center cmsb277-lg:justify-stretch"> 6998 <button class="g-recaptcha cmsb277-justify-self-end cmsb277-w-full" data-sitekey="6LdVcR8UAAAAACR_8DNwuaNOL_RojsmhLACSSeEU" data-callback='onRmSubmit'>SUBMIT</button> 6999 </div> 7000 </form> 7001 </div> 7002</div> 7003<style> 7004button.g-recaptcha { 7005 border: 1px solid #d1021c; 7006 color: #fff; 7007 background-color: #d1021c; 7008} 7009</style></div><div class="bg-black flex flex-wrap flex-row items-start justify-center px-6 pt-0 lg:pt-5"> 7010 <div class="flex flex-col flex-wrap text-white w-full lg:w-1/4 content-center lg:content-start mb-5 lg:mb-0"> 7011 <img src="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/images/studio88-footer-logo.png" alt="Studio88 Footer Logo" width="200" height="68"/> 7012 </div> 7013 <div class="flex flex-col flex-wrap text-white content-start w-full lg:w-3/4"> 7014 <div class="w-full flex flex-row flex-wrap lg:pr-4 pb-3 lg:pb-6"> 7015 <div class="w-full grid grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-
70155 lg:gap-8 xl:col-span-2"> 7016 <div class="pb-5 lg:pb-0"> 7017 <!--Need Help--> 7018 <h2 class="text-md leading-5 font-semibold tracking-wider uppercase"> 7019 Need Help? 7020 </h2> 7021 <ul class="my-4"> 7022 <style>.cmsb433-mt-3 { 7023margin-top: 0.75rem 7024} 7025.cmsb433-text-base { 7026font-size: 1rem; 7027line-height: 1.5rem 7028} 7029.cmsb433-leading-6 { 7030line-height: 1.5rem 7031} 7032@media (min-width: 768px) { 7033.cmsb433-md\:mt-4 { 7034margin-top: 1rem 7035} 7036}</style> 7037<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><li> 7038 <a href="https://www.studio-88.co.za/returns-policy" class="cmsb433-text-base cmsb433-leading-6">Returns Policy</a> 7039</li> 7040<li class="cmsb433-mt-3 cmsb433-md:mt-4"> 7041 <a href="https://www.studio-88.co.za/privacy-policy" class="cmsb433-text-base cmsb433-leading-6">Privacy Policy</a> 7042</li> 7043<li class="cmsb433-mt-3 cmsb433-md:mt-4"> 7044 <a href="https://www.studio-88.co.za/faq/#shipping" class="cmsb433-text-base cmsb433-leading-6">Shipping & Deliveries</a> 7045</li> 7046<li class="cmsb433-mt-3 cmsb433-md:mt-4"> 7047 <a href="https://www.studio-88.co.za/how-to-shop-online" class="cmsb433-text-base cmsb433-leading-6">How to Shop Online</a> 7048</li> 7049<li class="cmsb433-mt-3 cmsb433-md:mt-4"> 7050 <a href="https://www.studio-88.co.za/varsity-vibe-student-discount" class="cmsb433-text-base cmsb433-leading-6">Varsity Vibe Discount</a> 7051</li> 7052<li class="cmsb433-mt-3 cmsb433-md:mt-4"> 7053 <a href="https://www.studio-88.co.za/tracking" class="cmsb433-text-base cmsb433-leading-6">Track Your Parcel</a> 7054</li> 7055<li class="cmsb433-mt-3 cmsb433-md:mt-4"> 7056 <a href="https://www.studio-88.co.za/feature88/our-style-on-the-move-size-guide-for-you" class="cmsb433-text-base cmsb433-leading-6">Size Guide</a> 7057</li> 7058<li class="cmsb433-mt-3 cmsb433-md:mt-4"> 7059 <a href="https://www.studio-88.co.za/faq" class="cmsb433-text-base cmsb433-leading-6">Frequently Asked Questions</a> 7060</li></div> </ul> 7061 </div> 7062 <div class="pb-5 lg:pb-0"> 7063 <!--About Us--> 7064 <h2 class="text-md leading-5 font-semibold tracking-wider uppercase"> 7065 About Us 7066 </h2> 7067 <ul class="my-4"> 7068 <style>.cmsb434-mt-3 { 7069margin-top: 0.75rem 7070} 7071.cmsb434-text-base { 7072font-size: 1rem; 7073line-height: 1.5rem 7074} 7075.cmsb434-leading-6 { 7076line-height: 1.5rem 7077} 7078@media (min-width: 768px) { 7079.cmsb434-md\:mt-4 { 7080margin-top: 1rem 7081} 7082}</style> 7083<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><li> 7084 <a href="https://www.studio-88.co.za/about-us" class="cmsb434-text-base cmsb434-leading-6">About Studio 88</a> 7085</li> 7086<li class="cmsb434-mt-3 cmsb434-md:mt-4"> 7087 <a href="https://www.studio-88.co.za/store-locator" class="cmsb434-text-base cmsb434-leading-6">Store Locator</a> 7088</li> 7089<li class="cmsb434-mt-3 cmsb434-md:mt-4"> 7090 <a href="https://www.studio-88.co.za/feature88" class="cmsb434-text-base cmsb434-leading-6">FEATURE 88</a> 7091</li> 7092<li class="cmsb434-mt-3 cmsb434-md:mt-4"> 7093 <a href="https://www.studio-88.co.za/soccer-central" class="cmsb434-text-base cmsb434-leading-6">Soccer Central</a> 7094</li></div> 7095 </ul> 7096 </div> 7097 <div> 7098 <!--Contact Us--> 7099 <h2 class="text-md leading-5 font-semibold tracking-wider uppercase"> 7100 Contact Us 7101 </h2> 7102 <ul class="my-4"> 7103 <style>.cmsb435-mt-3 { 7104margin-top: 0.75rem 7105} 7106.cmsb435-text-base { 7107font-size: 1rem; 7108line-height: 1.5rem 7109} 7110.cmsb435-leading-6 { 7111line-height: 1.5rem 7112} 7113@media (min-width: 768px) { 7114.cmsb435-md\:mt-4 { 7115margin-top: 1rem 7116} 7117}</style> 7118<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><li class="cmsb435-mt-3 cmsb435-md:mt-4"> 7119 <a href="mailto:[email protected]" target="_blank" class="cmsb435-text-base cmsb435-leading-6">Email Support</a> 7120</li> 7121<li class="cmsb435-mt-3 cmsb435-md:mt-4"> 7122 <a href="Tel:+2710.006.0888" target="_blank" class="cmsb435-text-base cmsb435-leading-6">Call: +27 10 006 0888</a> 7123</li> 7124<li class="cmsb435-mt-3 cmsb435-md:mt-4"> 7125 <a href="https://careers88.co.za/" target="_blank" class="cmsb435-text-base cmsb435-leading-6">Careers</a> 7126</li> 7127</div> 7128 </ul> 7129 </div> 7130 <div> 7131 <!--Social Media Icons--> 7132 <style>.cmsb11-mt-5 { 7133margin-top: 1.25rem 7134}
7135.cmsb11-flex { 7136display: flex 7137} 7138.cmsb11-h-5 { 7139height: 1.25rem 7140} 7141.cmsb11-h-\[5px\] { 7142height: 5px 7143} 7144.cmsb11-w-5 { 7145width: 1.25rem 7146} 7147.cmsb11-w-\[5px\] { 7148width: 5px 7149} 7150.cmsb11-gap-2 { 7151gap: 0.5rem 7152} 7153.cmsb11-space-x-1 > :not([hidden]) ~ :not([hidden]) { 7154--tw-space-x-reverse: 0; 7155margin-right: calc(0.25rem * var(--tw-space-x-reverse)); 7156margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse))) 7157} 7158@media (min-width: 768px) { 7159.cmsb11-md\:gap-4 { 7160gap: 1rem 7161} 7162}</style> 7163<div data-content-type="html" data-appearance="default" data-element="main" data-decoded="true"><div class="cmsb11-flex cmsb11-space-x-1 cmsb11-md:gap-4 cmsb11-gap-2"> 7164 <a href="https://web.facebook.com/studio88clothing/?_rdc=1&_rdr" title="Join us on Facebook" target="_blank"> 7165 <span class="text-white-500 hover:text-white-600 cmsb11-w-[5px] cmsb11-h-[5px]"> 7166 <svg 7167 xmlns="http://www.w3.org/2000/svg" 7168 fill="currentColor" 7169 viewBox="0 0 320 512" 7170 class="cmsb11-h-5 cmsb11-w-5"> 7171 <path 7172 d="M80 299.3V512H196V299.3h86.5l18-97.8H196V166.9c0-51.7 20.3-71.5 72.7-71.5c16.3 0 29.4 .4 37 1.2V7.9C291.4 4 256.4 0 236.2 0C129.3 0 80 50.5 80 159.4v42.1H14v97.8H80z" /> 7173 </svg> 7174 </span> 7175 </a> 7176 <a class="first" href="https://x.com/Studio_88" title="Follow Side-Step on Twitter" target="_blank"> 7177 <span class="text-white-500 hover:text-white-600 cmsb11-w-[5px] cmsb11-h-[5px]"> 7178 <svg 7179 xmlns="http://www.w3.org/2000/svg" 7180 fill="currentColor"
7181 viewBox="0 0 512 512" 7182 class="cmsb11-h-5 cmsb11-w-5"> 7183 <path 7184 d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" /> 7185 </svg> 7186 </span> 7187 </a> 7188 <a href="https://za.linkedin.com/company/studio-88" title="Linked in" target="_blank"> 7189 <span class="text-white-500 hover:text-white-600 cmsb11-w-[5px] cmsb11-h-[5px]"> 7190 <svg 7191 xmlns="http://www.w3.org/2000/svg" 7192 fill="currentColor" 7193 viewBox="0 0 448 512" 7194 class="cmsb11-h-5 cmsb11-w-5"> 7195 <path 7196 d="M100.3 448H7.4V148.9h92.9zM53.8 108.1C24.1 108.1 0 83.5 0 53.8a53.8 53.8 0 0 1 107.6 0c0 29.7-24.1 54.3-53.8 54.3zM447.9 448h-92.7V302.4c0-34.7-.7-79.2-48.3-79.2-48.3 0-55.7 37.7-55.7 76.7V448h-92.8V148.9h89.1v40.8h1.3c12.4-23.5 42.7-48.3 87.9-48.3 94 0 111.3 61.9 111.3 142.3V448z" /> 7197 </svg> 7198 </span> 7199 </a> 7200 <a href="https://www.instagram.com/studio88_branded/" title="Instagram" target="_blank"> 7201 <span class="text-white-500 hover:text-white-600 cmsb11-w-[5px] cmsb11-h-[5px]"> 7202 <svg 7203 xmlns="http://www.w3.org/2000/svg" 7204 fill="currentColor"
7205 viewBox="0 0 448 512" 7206 class="cmsb11-h-5 cmsb11-w-5"> 7207 <path 7208 d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z" /> 7209 </svg> 7210 </span> 7211 </a> 7212 <a href="https://www.youtube.com/@Studio88Group" title="Youtube" target="_blank"> 7213 <span class="text-white-500 hover:text-white-600 cmsb11-w-[5px] cmsb11-h-[5px]"> 7214 <svg 7215 xmlns="http://www.w3.org/2000/svg" 7216 fill="currentColor"
7217 viewBox="0 0 576 512" 7218 class="cmsb11-h-5 cmsb11-w-5"> 7219 <path 7220 d="M549.7 124.1c-6.3-23.7-24.8-42.3-48.3-48.6C458.8 64 288 64 288 64S117.2 64 74.6 75.5c-23.5 6.3-42 24.9-48.3 48.6-11.4 42.9-11.4 132.3-11.4 132.3s0 89.4 11.4 132.3c6.3 23.7 24.8 41.5 48.3 47.8C117.2 448 288 448 288 448s170.8 0 213.4-11.5c23.5-6.3 42-24.2 48.3-47.8 11.4-42.9 11.4-132.3 11.4-132.3s0-89.4-11.4-132.3zm-317.5 213.5V175.2l142.7 81.2-142.7 81.2z" /> 7221 </svg> 7222 </span> 7223 </a> 7224</div> 7225</br> 7226<div> 7227 <a href="https://www.mrp.com/" target="_blank">Mr Price</a> 7228</div> 7229<div class="cmsb11-mt-5"> 7230 <span> 7231 Address: 30 OâConnor Place, Aeroton, <br> 7232 Johannesburg, 2013, South Africa 7233 </span> 7234</div></div> </div> 7235 </div> 7236</div> 7237 </div> 7238</div> 7239<div class="bg-black border-white border-container flex flex-wrap flex-row items-center py-4 px-6"> 7240 7241<div class="flex flex-wrap flex-col content-center md:content-start w-full md:w-1/2 lg:w-1/3 px-5 md:px-0 mb-4 mt-3 md:mb-0"> 7242 <p class="text-sm text-gray-200"> 7243 <a 7244 href="https://www.studio-88.co.za/" 7245 target="_self" 7246 rel="noopener noreferrer" 7247 class="text-white underline" 7248 aria-label="Studio88 Homepage" 7249 >© 2026 Studio 88</a> â All Rights Reserved. 7250 </p> 7251</div> 7252<div class="flex flex-wrap flex-col content-center md:content-end w-full md:w-1/2 lg:w-1/3 content-center text-white mb-4 md:mb-0"> 7253 <img src="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/images/S88_Payment_Options.webp" alt="Website Payment Options" width="100%"/> 7254</div> 7255<div class="flex flex-wrap flex-col content-center w-full lg:w-1/3 px-5 md:px-0 mb-4 md:mb-0"> 7256 <ul id="footBar" class="p-0 text-white text-sm my-2"> 7257 <li class="pe-2"> 7258 <a href="https://www.studio-88.co.za/terms-and-conditions">T's and C's</a> 7259 </li> 7260 <li class="pe-2"> 7261 <a href="https://www.studio-88.co.za/privacy-policy">Privacy Policy</a> 7262 </li> 7263 <li class="pe-2"> 7264 <a href="https://www.studio-88.co.za/payment-security-policy">Payment Security Policy</a> 7265 </li> 7266 </ul> 7267</div> 7268 7269</div> 7270 7271 7272<div id="android-app-banner" style="display:none; background:#2E2E2E; color:#fff; padding:10px; position:fixed; top:0; left:0; right:0; z-index:10000;"> 7273 <div style="display:flex; align-items:center; justify-content:space-between;"> 7274 <div style="display:flex; align-items:center;"> 7275 <img src="/media/logo/stores/1/studio88-logo-simple.jpg" alt="App Icon" style="height:40px; margin-right:10px;"> 7276 <div> 7277 <div><strong>Shop Easier with Our App</strong></div> 7278 <div>Download for a faster checkout experience</div> 7279 </div> 7280 </div> 7281 <div style="display: flex;"> 7282 <a href="https://play.google.com/store/apps/details?id=com.studio88group.studio88" style="background:#D1021C; color:#fff; padding:8px 12px; border-radius:5px; text-decoration:none;">Install</a> 7283 <button onclick="document.getElementById('android-app-banner').style.display='none';" style="margin-left:10px; background:none; border:none; color:#fff;">â</button> 7284 </div> 7285 </div> 7286</div> 7287
7288<script> 7289 // Ensure this runs after the rest of the page might have loaded, 7290 // so the callAmGA4GTM function is definitely defined. 7291 // Using DOMContentLoaded is generally safer than a fixed timeout. 7292 document.addEventListener('DOMContentLoaded', function() { 7293 // Check if the function exists and hasn't been run by this specific script yet 7294 if (typeof window.callAmGA4GTM === 'function' && !window.amastyGa4GtmForceLoaded) { 7295 window.callAmGA4GTM(); 7296 window.amastyGa4GtmForceLoaded = true; // Flag to prevent multiple calls by this script 7297 } 7298 }); 7299 document.addEventListener('DOMContentLoaded', function () { 7300 if (/Android/i.test(navigator.userAgent)) { 7301 // Optional: Only show banner once using localStorage 7302 if (!localStorage.getItem('androidAppBannerDismissed')) { 7303 document.getElementById('android-app-banner').style.display = 'block'; 7304 } 7305 7306 // Optional: Store dismissal 7307 document.querySelector('#android-app-banner button').addEventListener('click', function () { 7308 localStorage.setItem('androidAppBannerDismissed', 'true'); 7309 }); 7310 } 7311 }); 7312</script>
7312 7313
7314<script> 7315 'use strict'; 7316 7317 function amBrandsPopup() { 7318 let cached = false; 7319 7320 const displayError = (error) => { 7321 console.error(error); 7322 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 7323 [{ 7324 type: "error", 7325 text: "Something\u0020went\u0020wrong.\u0020Please\u0020try\u0020again." 7326 }], 10000 7327 ); 7328 }; 7329 7330 let letters = [ 7331 "A","B","C","D", 7332 "E","F","G","H","I","J","K","L","M", 7333 "N","O","P","Q","R","S","T","U","V","W","X","Y","Z","#" 7334 ]; 7335 7336 return { 7337 hoverPanelActiveId: null, 7338 selectedLetter: null, 7339 items: [], 7340 letters: [], 7341 showCount: null, 7342 showFilter: null, 7343 showImages: true, 7344 showSearch: null, 7345 loading: false, 7346 label: 'Brands', 7347 isPopupEnabled: false, 7348 isTopMenuItemEnabled: false, 7349 cache: false, 7350 isMobile: false, 7351 7352 initErrorMessages(errors) { 7353 const messages = []; 7354 for (let error in Object.keys(errors)) { 7355 messages.push({type: 'error', text: errors[error].message}); 7356 } 7357 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(messages, 10000); 7358 }, 7359 7360 chooseLetter(letter) { 7361 this.selectedLetter = this.selectedLetter === letter ? null : letter; 7362 }, 7363 7364 getQuery() { 7365 return "query BrandsPopupQuery { ambrandlist(\n showCount: true\n displayZero: true\n ) {\n all_letters,\n items {\n alt\n brandId\n cnt\n image\n img\n label\n letter\n url\n }\n }\n storeConfig {\n amshopby_brand_general_brands_brands_popup,\n amshopby_brand_general_menu_item_label,\n amshopby_brand_general_brands_page,\n amshopby_brand_general_topmenu_enabled,\n amshopby_brand_general_brands_popup_config {\n show_images,\n display_zero,\n filter_display_all,\n image_width,\n image_height,\n show_filter,\n show_count\n }\n } }" }, 7366 7367 /** 7368 * Retrieve media url for given path 7369 * 7370 * @param path 7371 * @returns {string} 7372 */ 7373 getMediaUrl(path) { 7374 let stringToRemove = 'index.php/'; 7375 let baseUrl = BASE_URL.replace(stringToRemove, ''); 7376 return path.includes(baseUrl) ? path : baseUrl + path; 7377 }, 7378 7379 checkIsMobileResolution() { 7380 this.isMobile = window.matchMedia('(max-width: 1023px)').matches; 7381 }, 7382 7383 getBrandUrl(url){ 7384 return BASE_URL + url; 7385 }, 7386 7387 popupInit() { 7388 this.checkIsMobileResolution(); 7389 7390 if (this.isMobile) { 7391 return; 7392 } 7393 7394 this.loading = true; 7395 7396 if (cached) { 7397 this.loading = false; 7398 return; 7399 } 7400 7401 const req = this.sendRequest(); 7402 req.then(result => { 7403 if (result && result.errors) { 7404 this.initErrorMessages(result.errors); 7405 return; 7406 } 7407 7408 const data = result.data?.ambrandlist; 7409 const mapper = this.settingsMapper(result) 7410 for (let config in mapper) { 7411 this[config] = mapper[config]; 7412 } 7413 7414 if (!this.filterDisplayAll) { 7415 letters = data.all_letters.split(','); 7416 } 7417 7418 const items = data.items.map(brand => { 7419 if (brand.img) { 7420 brand.image = this.getMediaUrl(brand.img); 7421 return brand; 7422 } else if (brand.image) { 7423 brand.image = this.getMediaUrl(brand.image); 7424 return brand; 7425 } else { 7426 return brand; 7427 } 7428 }); 7429 7430 this.letters = letters.map(letter => ({ 7431 letter: letter, 7432 brands: this.getLetterItems(items, letter), 7433 })); 7434 7435 this.items = this.letters.filter(row => row.brands && row.brands.length > 0); 7436 7437 cached = true; 7438 }).catch(displayError).finally(() => { 7439 this.loading = false;
7440 }); 7441 }, 7442 7443 /** 7444 * Get brands by letter 7445 * 7446 * @param result 7447 * @returns {{imageWidth: *, isTopMenuItemEnabled: *, showCount: *, displayAll: *, showFilter: *, showImages: *, isPopupEnabled: *, label: *, imageHeight: *, filterDisplayAll: *, brandsPage: *}} 7448 */ 7449 settingsMapper(result) { 7450 const config = result.data.storeConfig?.amshopby_brand_general_brands_popup_config; 7451 return { 7452 showImages: config.show_images, 7453 showCount: config.show_count, 7454 showFilter: config.show_filter, 7455 imageWidth: config.image_width, 7456 imageHeight: config.image_height, 7457 filterDisplayAll: config.filter_display_all, 7458 displayAll: config.display_zero, 7459 isPopupEnabled: result.data.storeConfig?.amshopby_brand_general_brands_brands_popup, 7460 label: result.data.storeConfig?.amshopby_brand_general_menu_item_label, 7461 isTopMenuItemEnabled: result.data.storeConfig?.amshopby_brand_general_topmenu_enabled, 7462 brandsPage: result.data.storeConfig?.amshopby_brand_general_brands_page, 7463 } 7464 }, 7465 7466 /** 7467 * Send request to graphql 7468 * 7469 * @returns {Promise<any>} 7470 */ 7471 sendRequest() { 7472 return fetch('https://www.studio-88.co.za/graphql', { 7473 method: 'POST', 7474 headers: { 7475 'Content-Type': 'application/json', 7476 'Store': 'default' 7477 }, 7478 credentials: 'include', 7479 body: JSON.stringify({query: this.getQuery(), variables: {}}) 7480 }).then( 7481 response => response.json() 7482 ); 7483 }, 7484 7485 /** 7486 * Get brands by letter 7487 * 7488 * @param items 7489 * @param letter 7490 * @returns {*} 7491 */ 7492 getLetterItems(items, letter) { 7493 return items.filter(row => row.letter === letter); 7494 }, 7495 7496 convertHtml(text) { 7497 let textField = document.createElement("textarea"); 7498 textField.innerHTML = text; 7499 return textField.value; 7500 } 7501 } 7502 } 7503</script>
7503 7504</div></footer>
7505<script defer 7506 src="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/Klevu_FrontendJs/js/klevu-theme-breeze_cascade.js"></script>
7506 7507 7508<link rel="stylesheet" 7509 type="text/css" 7510 href="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/Klevu_FrontendJs/css/klevu-theme-breeze_cascade.css"><div style="display:none;" 7511 x-data="initKlevuCustomerTracking()" 7512 @private-content-loaded.window="getCustomerSectionData($event.detail.data)" 7513> 7514 <span id="klevu_sessionId" x-text="klevuSessionId"></span> 7515 <span id="klevu_loginCustomerGroup" x-text="klevuLoginCustomerGroup"></span> 7516 <span id="klevu_loginCustomerEmail" x-text="klevuIdCode"></span> 7517 <span id="klevu_shopperIP" x-text="klevuShopperIP"></span> 7518</div> 7519
7520<script> 7521 function initKlevuCustomerTracking() { 7522 return { 7523 klevuSessionId : '', 7524 klevuLoginCustomerGroup : '', 7525 klevuIdCode : '', 7526 klevuShopperIP : '', 7527 getCustomerSectionData(data) { 7528 this.klevuSessionId = data['personal-data'].klevuSessionId; 7529 this.klevuLoginCustomerGroup = data['personal-data'].klevuLoginCustomerGroup; 7530 this.klevuIdCode = data['personal-data'].klevuIdCode; 7531 this.klevuShopperIP = data['personal-data'].klevuShopperIP; 7532 } 7533 } 7534 } 7535</script>
7535 7536
7537<script> 7538 'use strict'; 7539 7540 (function () { 7541 window.AmFB = { 7542 fbSectionName: 'amfacebook-pixel', 7543 options: { 7544 eventCode: '', 7545 url: 'https://www.studio-88.co.za/amasty_fbpixel/pageView/processPageView/', 7546 eventsData: {}, 7547 isLogEnabled: '1', 7548 loggingUrl: 'https://www.studio-88.co.za/amasty_fbpixel/logEvent/log/', 7549 sectionLoadUrl: 'https://www.studio-88.co.za/customer/section/load/' 7550 }, 7551 sectionFetchInFlight: false, 7552 7553 /** 7554 * @private 7555 * @param {object} body 7556 * @param {string} param 7557 * @param {object} object 7558 * @returns {void} 7559 */ 7560 prepareObjectData: function (body, param, object) { 7561 for (const [key, value] of Object.entries(object)) { 7562 7563 if (typeof value === 'object') { 7564 this.prepareObjectData(body, param + `[${key}]`, value); 7565 } else { 7566 body.append(param + `[${key}]`, value); 7567 } 7568 7569 } 7570 }, 7571 7572 /** 7573 * @private 7574 * @returns {void} 7575 */ 7576 setDataToSection: function () { 7577 const body = new URLSearchParams({ 7578 form_key: hyva.getFormKey(), 7579 uenc: hyva.getUenc(), 7580 event_code: this.options.eventCode 7581 }); 7582 7583 this.prepareObjectData(body, 'event_data', this.options.eventsData); 7584 7585 return fetch(this.options.url, { 7586 method: 'POST', 7587 headers: { 7588 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 7589 }, 7590 body: body 7591 }).catch(error => { 7592 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 7593 { 7594 type: "error", 7595 text: error 7596 } 7597 ]); 7598 }); 7599 }, 7600 7601 /** 7602 * @private 7603 * @param {string} eventsData 7604 * @returns {void} 7605 */ 7606 logEventData: function (eventsData) { 7607 const body = new URLSearchParams({ 7608 form_key: hyva.getFormKey(), 7609 uenc: hyva.getUenc() 7610 }); 7611 7612 this.prepareObjectData(body, 'events', eventsData); 7613 7614 fetch(this.options.loggingUrl, { 7615 method: 'POST', 7616 headers: { 7617 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 7618 }, 7619 body: body 7620 }).catch(error => { 7621 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 7622 { 7623 type: "error", 7624 text: error 7625 } 7626 ]); 7627 }); 7628 }, 7629 7630 /** 7631 * @private 7632 * @param {Object} event 7633 * @returns {void} 7634 */ 7635 afterLoadSectionData: function (event) { 7636 const data = event.detail?.data || {}; 7637 const eventsData = data[this.fbSectionName]?.events; 7638 7639 if (eventsData) { 7640 this.setFbqData(eventsData); 7641 } else if (data['cart'] && !this.sectionFetchInFlight) { 7642 this.fetchPixelSection(); 7643 } 7644 }, 7645 7646 /** 7647 * @private 7648 * @returns {void} 7649 */ 7650 fetchPixelSection: function () { 7651 this.sectionFetchInFlight = true; 7652 7653 fetch(this.options.sectionLoadUrl 7654 + '?sections=' + this.fbSectionName 7655 + '&force_new_section_timestamp=true&_=' + Date.now(), { 7656 headers: {'X-Requested-With': 'XMLHttpRequest'} 7657 }) 7658 .then((response) => response.json()) 7659 .then((data) => { 7660 const eventsData = data?.[this.fbSectionName]?.events; 7661 !!eventsData && this.setFbqData(eventsData); 7662 }) 7663 .catch(() => {}) 7664 .finally(() => { 7665 this.sectionFetchInFlight = false;
7666 }); 7667 }, 7668 7669 /** 7670 * @private 7671 * @param {array} eventsData 7672 * @returns {void} 7673 */ 7674 setFbqData: function(eventsData) { 7675 eventsData.forEach((data) => { 7676 const id = Object.keys(data)[0]; 7677 7678 !!id && fbq(data[id].event_action, id, data[id].event_type, data[id].event_data, {eventID: data[id].event_id}); 7679 }); 7680 7681 if (this.options.loggingUrl && this.options.isLogEnabled) { 7682 this.logEventData(eventsData); 7683 } 7684 }, 7685 7686 /** 7687 * @public 7688 * @returns {void} 7689 */ 7690 initFB: function () { 7691 window.addEventListener("private-content-loaded", (event) => this.afterLoadSectionData(event)); 7692 7693 if (this.options.eventCode && this.options.url) { 7694 this.setDataToSection().finally(() => this.reloadCustomerData()); 7695 } else { 7696 this.reloadCustomerData(); 7697 } 7698 }, 7699 7700 /** 7701 * @private 7702 * @returns {void} 7703 */ 7704 reloadCustomerData() { 7705 this.resetSessionCookie(); 7706 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 7707 }, 7708 7709 /** 7710 * @private 7711 * @returns {void} 7712 */ 7713 resetSessionCookie() { 7714 hyva.setCookie('mage-cache-sessid', '', -1, true); 7715 }, 7716 }; 7717 7718 window.AmFB.initFB(); 7719 })(); 7720</script>
7720 7721 7722<div x-data="{...hyva.modal(), ...initAmGdprPolicyPopup()}" x-init="initGdprPopup()" 7723 x-spread="eventListeners" x-bind="eventListeners"> 7724 <div x-cloak x-spread="overlay('amgdpr-policy-popup')" x-bind="overlay('amgdpr-policy-popup')" 7725 class="fixed inset-0 flex items-center justify-center text-left bg-black bg-opacity-50 z-40"> 7726 <div x-ref="amgdpr-policy-popup" role="dialog" aria-labelledby="amgdpr-policy-content" 7727 class="flex flex-col bg-white shadow-xl rounded-lg p-10 max-h-[95vh] max-w-6xl relative"> 7728 <div tabindex="0" @focusin="focusAcceptButton()"></div> 7729 <button @click="$dispatch('amgdpr-close-policy')" 7730 x-ref="amgdpr-close-policy" 7731 aria-label="Close" 7732 class="absolute right-4 top-4 text-gray-300 transition 7733 hover:text-black focus:drop-shadow-md hover:drop-shadow-md"> 7734 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="h-6 w-6" width="24" height="24" role="img"> 7735 <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/> 7736<title>x</title></svg> 7737 </button> 7738 <div class="flex w-full"> 7739 <p class="w-full text-blue-500 text-3xl mb-5 ml-3" x-text="title"></p> 7740 </div> 7741 <div x-show="notificationText && policyData.versionChanged" class="flex w-full"> 7742 <p class="mb-4 p-4 bg-yellow-100 w-full" x-text="notificationText"></p> 7743 </div> 7744 <div id="amgdpr-policy-content" 7745 class="max-h-[500px] overflow-auto first:mx-0 child:mx-0" 7746 tabindex="0" 7747 x-html="policyText"></div> 7748 <div class="mt-8 flex w-full justify-center"> 7749 <button x-focus-first 7750 @click="$dispatch('amgdpr-accept-policy')" 7751 x-ref="amgdpr-accept-policy" 7752 type="button" 7753 class="btn btn-primary uppercase font-bold" 7754 aria-label="I have read and accept"> 7755 I have read and accept </button> 7756 </div> 7757 <div tabindex="0" @focusin="focusCloseButton()"></div> 7758 </div> 7759 7760 </div> 7761</div> 7762
7763<script> 7764 'use strict'; 7765 7766 function initAmGdprPolicyPopup() { 7767 return { 7768 /** 7769 * @typedef PolicyData 7770 * @property {string} policyVersion 7771 * @property {boolean} show 7772 * @property {string} title 7773 * @property{boolean} versionChanged 7774 * @property {boolean} hideClose 7775 * @property {boolean} action 7776 */ 7777 7778 eventListeners: { 7779 ['@amgdpr-accept-policy']() { 7780 if (this.consentId) { 7781 window.dispatchEvent(new CustomEvent('amgdpr-consent-accepted', {detail: this.consentId})); 7782 this.consentId = undefined; 7783 this.hide('amgdpr-policy-popup'); 7784 } else { 7785 this.acceptPolicy(); 7786 } 7787 }, 7788 ['@amgdpr-show-policy.window'](event) { 7789 this.consentId = event.detail; 7790 this.fetchPolicyContent().then(() => { 7791 this.$nextTick(() => { 7792 this.show('amgdpr-policy-popup'); 7793 }); 7794 }); 7795 }, 7796 ['@amgdpr-close-policy']() { 7797 this.hide('amgdpr-policy-popup'); 7798 } 7799 }, 7800 textUrl: 'https://www.studio-88.co.za/gdpr/policy/policytext/', 7801 acceptUrl: 'https://www.studio-88.co.za/gdpr/policy/accept/', 7802 popupDataUrl: 'https://www.studio-88.co.za/gdpr/policy/popupData/', 7803 notificationText: 'We would like to inform you that our Privacy Policy has been amended.Please, read and accept the new terms.', 7804 showOnPageLoad: '', 7805 /** 7806 * @type PolicyData 7807 */ 7808 policyData: { 7809 policyVersion: '', 7810 show: false, 7811 title: '', 7812 versionChanged: false, 7813 hideClose: false, 7814 action: true 7815 }, 7816 title: '', 7817 policyText: '', 7818 consentId: undefined, 7819 initGdprPopup: function () { 7820 this.fetchPolicyData().then(() => { 7821 if ((this.showOnPageLoad && this.policyData.show) || this.policyData.versionChanged) { 7822 this.fetchPolicyContent().then(() => { 7823 this.$nextTick(() => { 7824 this.show('amgdpr-policy-popup'); 7825 }); 7826 }); 7827 } 7828 }); 7829 }, 7830 7831 /** 7832 * @returns {Promise} 7833 */ 7834 fetchPolicyData: function () { 7835 return fetch(this.popupDataUrl) 7836 .then(response => response.json()) 7837 .then((data) => { 7838 this.policyData = {...data} 7839 }) 7840 .catch(error => this.dispatchErrorMessage(error)); 7841 }, 7842 7843 /** 7844 * @returns {Promise} 7845 */ 7846 fetchPolicyContent: function () { 7847 this.title = ''; 7848 this.policyText = ''; 7849 7850 const params = !!this.consentId 7851 ? '?' + new URLSearchParams({consent_id: this.consentId}) 7852 : '' 7853 7854 return fetch(this.textUrl + params) 7855 .then(response => response.json()) 7856 .then(policyContent => { 7857 this.title = policyContent.title; 7858 this.policyText = policyContent.content; 7859 }).catch(error => this.dispatchErrorMessage(error)); 7860 }, 7861 7862 /** 7863 * @returns {void} 7864 */ 7865 acceptPolicy: function () { 7866 window.dispatchEvent(new Event('amgdpr-loading-start')); 7867 7868 const body = new URLSearchParams({form_key: hyva.getFormKey(), ...this.policyData}); 7869 const contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 7870 7871 fetch(this.acceptUrl, {method: 'POST', body, headers: {'Content-Type': contentType}}) 7872 .then(response => response.json()) 7873 .then(data => { 7874 if (data.error) { 7875 this.dispatchErrorMessage(data.message) 7876 } else { 7877 this.notificationText = ''; 7878 this.hide('amgdpr-policy-popup'); 7879 } 7880 }) 7881 .catch(error => this.dispatchErrorMessage(error)) 7882 .finally(() => window.dispatchEvent(new Event('amgdpr-loading-stop'))); 7883 }, 7884 7885 /** 7886 * @param {string} message 7887 * @returns {void} 7888 */ 7889 dispatchErrorMessage: function (message) { 7890 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 7891 { 7892 type: "error", 7893 text: message 7894 } 7895 ]); 7896 }, 7897 7898 /** 7899 * @returns {void} 7900 */ 7901 focusAcceptButton: function () { 7902 this.$refs['amgdpr-accept-policy']?.focus(); 7903 }, 7904 7905 /** 7906 * @returns {void} 7907 */ 7908 focusCloseButton: function () { 7909 this.$refs['amgdpr-close-policy']?.focus(); 7910 } 7911 } 7912 } 7913</script>
7913 7914 7915
7916<script type="text/javascript">window.dataLayer = window.dataLayer || []; 7917 7918(function () { 7919 /** 7920 * @var {[Object]} 7921 */ 7922 const dataToPush = JSON.parse('\u005B\u007B\u0022ecommerce\u0022\u003Anull\u007D,\u007B\u0022event\u0022\u003A\u0022view_item_list\u0022,\u0022ecommerce\u0022\u003A\u007B\u0022items\u0022\u003A\u005B\u007B\u0022item_name\u0022\u003A\u0022Puma\u0020Shuffle\u0020Downtown\u0020Low\u0020Sneaker\u0020Youth\u0020White\u005C\u002FRoyal\u0022,\u0022item_id\u0022\u003A\u0022PMA7657YRY\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022PUMA\u0022,\u0022item_category\u0022\u003A\u0022Kids\u0022,\u0022price\u0022\u003A999.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_1\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00201\u0022,\u0022index\u0022\u003A1\u007D,\u007B\u0022item_name\u0022\u003A\u0022Nike\u0020Swim\u0020Breaker\u0020Essential\u00205\u0026\u0023039\u003B\u0026\u0023039\u003B\u0020Brief\u002DLined\u0020Volley\u0020Shorts\u0020Mens\u0020Black\u0022,\u0022item_id\u0022\u003A\u0022NSS05B\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022NIKE\u0022,\u0022item_category\u0022\u003A\u0022Men\u0022,\u0022price\u0022\u003A599.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_1\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00201\u0022,\u0022index\u0022\u003A2\u007D,\u007B\u0022item_name\u0022\u003A\u0022Puma\u0020Shuffle\u0020Downtown\u0020Low\u0020Sneaker\u0020Kids\u0020White\u005C\u002FChamber\u0020Blue\u0022,\u0022item_id\u0022\u003A\u0022PMA7657KCB\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022PUMA\u0022,\u0022item_category\u0022\u003A\u0022Kids\u0022,\u0022price\u0022\u003A899.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_1\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00201\u0022,\u0022index\u0022\u003A3\u007D,\u007B\u0022item_name\u0022\u003A\u0022New\u0020Balance\u0020603\u0020Sneaker\u0020Womens\u0020Seal\u0020Salt\u0022,\u0022item_id\u0022\u003A\u0022NEW269SP\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022NEW\u0020BALANCE\u0022,\u0022item_category\u0022\u003A\u0022Women\u0022,\u0022price\u0022\u003A2099.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_1\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00201\u0022,\u0022index\u0022\u003A4\u007D,\u007B\u0022item_name\u0022\u003A\u0022Puma\u0020Suede\u0020XL\u0020Sneaker\u0020Kids\u0020Archive\u0020Green\u0022,\u0022item_id\u0022\u003A\u0022PMA5674KAG\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022PUMA\u0022,\u0022item_category\u0022\u003A\u0022Kids\u0022,\u0022price\u0022\u003A1299.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_1\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00201\u0022,\u0022index\u0022\u003A5\u007D,\u007B\u0022item_name\u0022\u003A\u0022Vans\u0020Authentic\u0020Denim\u0020Sneaker\u0020Mens\u0020Blue\u005C\u002FWhite\u0022,\u0022item_id\u0022\u003A\u0022VAN8DM\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022VANS\u0022,\u0022item_category\u0022\u003A\u0022Men\u0022,\u0022price\u0022\u003A1099.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_1\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00201\u0022,\u0022index\u0022\u003A6\u007D,\u007B\u0022item_name\u0022\u003A\u0022Puma\u0020Rebound\u0020Slam\u0020Lo\u0020Sneaker\u0020Youth\u0020Archive\u0020Green\u0022,\u0022item_id\u0022\u003A\u0022PMA8317YAG\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022PUMA\u0022,\u0022item_category\u0022\u003A\u0022Kids\u0022,\u0022price\u0022\u003A999.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_1\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00201\u0022,\u0022index\u0022\u003A7\u007D,\u007B\u0022item_name\u0022\u003A\u0022Nike\u0020Air\u0020Max\u0020Portal\u0020Sneaker\u0020Womens\u0020Sail\u005C\u002FLight\u0020Brown\u0022,\u0022item_id\u0022\u003A\u0022NKK3253LB\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022NIKE\u0022,\u0022item_category\u0022\u003A\u0022Women\u0022,\u0022price\u0022\u003A2199.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_1\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00201\u0022,\u0022index\u0022\u003A8\u007D\u005D\u00
79227D\u007D,\u007B\u0022ecommerce\u0022\u003Anull\u007D,\u007B\u0022event\u0022\u003A\u0022view_item_list\u0022,\u0022ecommerce\u0022\u003A\u007B\u0022items\u0022\u003A\u005B\u007B\u0022item_name\u0022\u003A\u0022Puma\u0020Shuffle\u0020Downtown\u0020Low\u0020Sneaker\u0020Youth\u0020White\u005C\u002FRoyal\u0022,\u0022item_id\u0022\u003A\u0022PMA7657YRY\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022PUMA\u0022,\u0022item_category\u0022\u003A\u0022Kids\u0022,\u0022price\u0022\u003A999.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_2\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00202\u0022,\u0022index\u0022\u003A1\u007D,\u007B\u0022item_name\u0022\u003A\u0022Nike\u0020Swim\u0020Breaker\u0020Essential\u00205\u0026\u0023039\u003B\u0026\u0023039\u003B\u0020Brief\u002DLined\u0020Volley\u0020Shorts\u0020Mens\u0020Black\u0022,\u0022item_id\u0022\u003A\u0022NSS05B\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022NIKE\u0022,\u0022item_category\u0022\u003A\u0022Men\u0022,\u0022price\u0022\u003A599.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_2\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00202\u0022,\u0022index\u0022\u003A2\u007D,\u007B\u0022item_name\u0022\u003A\u0022Puma\u0020Shuffle\u0020Downtown\u0020Low\u0020Sneaker\u0020Kids\u0020White\u005C\u002FChamber\u0020Blue\u0022,\u0022item_id\u0022\u003A\u0022PMA7657KCB\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022PUMA\u0022,\u0022item_category\u0022\u003A\u0022Kids\u0022,\u0022price\u0022\u003A899.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_2\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00202\u0022,\u0022index\u0022\u003A3\u007D,\u007B\u0022item_name\u0022\u003A\u0022New\u0020Balance\u0020603\u0020Sneaker\u0020Womens\u0020Seal\u0020Salt\u0022,\u0022item_id\u0022\u003A\u0022NEW269SP\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022NEW\u0020BALANCE\u0022,\u0022item_category\u0022\u003A\u0022Women\u0022,\u0022price\u0022\u003A2099.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_2\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00202\u0022,\u0022index\u0022\u003A4\u007D,\u007B\u0022item_name\u0022\u003A\u0022Puma\u0020Suede\u0020XL\u0020Sneaker\u0020Kids\u0020Archive\u0020Green\u0022,\u0022item_id\u0022\u003A\u0022PMA5674KAG\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022PUMA\u0022,\u0022item_category\u0022\u003A\u0022Kids\u0022,\u0022price\u0022\u003A1299.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_2\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00202\u0022,\u0022index\u0022\u003A5\u007D,\u007B\u0022item_name\u0022\u003A\u0022Vans\u0020Authentic\u0020Denim\u0020Sneaker\u0020Mens\u0020Blue\u005C\u002FWhite\u0022,\u0022item_id\u0022\u003A\u0022VAN8DM\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022VANS\u0022,\u0022item_category\u0022\u003A\u0022Men\u0022,\u0022price\u0022\u003A1099.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_2\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00202\u0022,\u0022index\u0022\u003A6\u007D,\u007B\u0022item_name\u0022\u003A\u0022Puma\u0020Rebound\u0020Slam\u0020Lo\u0020Sneaker\u0020Youth\u0020Archive\u0020Green\u0022,\u0022item_id\u0022\u003A\u0022PMA8317YAG\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022PUMA\u0022,\u0022item_category\u0022\u003A\u0022Kids\u0022,\u0022price\u0022\u003A999.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_2\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00202\u0022,\u0022index\u0022\u003A7\u007D,\u007B\u0022item_name\u0022\u003A\u0022Nike\u0020Air\u0020Max\u0020Portal\u0020Sneaker\u0020Womens\u0020Sail\u005C\u002FLight\u0020Brown\u0022,\u0022item_id\u0022\u003A\u0022NKK3253LB\u0022,\u0022currency\u0022\u003A\u0022ZAR\u0022,\u0022item_brand\u0022\u003A\u0022NIKE\u0022,\u0022item_category\u0022\u003A\u0022Women\u0022,\u0022price\u0022\u003A2199.99,\u0022item_list_id\u0022\u003A\u0022slider_product_list_2\u0022,\u0022item_list_name\u0022\u003A\u0022Slider\u0020Product\u0020List\u00202\u0022,\u0022index\u0022\u003A8\u007D\u005D\u00
79227D\u007D\u005D'); 7923 7924 dataToPush.forEach((dataObject) => { 7925 window.dataLayer.push(dataObject); 7926 }); 7927})(); 7928</script>
7928 7929
7930<script> 7931 (() => { 7932 'use strict'; 7933 const clickTracking = { 7934 productEventData: {"slider_product_list":{"371457":{"event":"select_item","ecommerce":{"items":[{"item_name":"Puma Shuffle Downtown Low Sneaker Youth White\/Royal","item_id":"PMA7657YRY","currency":"ZAR","item_brand":"PUMA","item_category":"Kids","price":999.99,"item_list_id":"slider_product_list","item_list_name":"Slider Product List","index":9}]}},"371456":{"event":"select_item","ecommerce":{"items":[{"item_name":"Nike Swim Breaker Essential 5'' Brief-Lined Volley Shorts Mens Black","item_id":"NSS05B","currency":"ZAR","item_brand":"NIKE","item_category":"Men","price":599.99,"item_list_id":"slider_product_list","item_list_name":"Slider Product List","index":10}]}},"371436":{"event":"select_item","ecommerce":{"items":[{"item_name":"Puma Shuffle Downtown Low Sneaker Kids White\/Chamber Blue","item_id":"PMA7657KCB","currency":"ZAR","item_brand":"PUMA","item_category":"Kids","price":899.99,"item_list_id":"slider_product_list","item_list_name":"Slider Product List","index":11}]}},"371434":{"event":"select_item","ecommerce":{"items":[{"item_name":"New Balance 603 Sneaker Womens Seal Salt","item_id":"NEW269SP","currency":"ZAR","item_brand":"NEW BALANCE","item_category":"Women","price":2099.99,"item_list_id":"slider_product_list","item_list_name":"Slider Product List","index":12}]}},"371432":{"event":"select_item","ecommerce":{"items":[{"item_name":"Puma Suede XL Sneaker Kids Archive Green","item_id":"PMA5674KAG","currency":"ZAR","item_brand":"PUMA","item_category":"Kids","price":1299.99,"item_list_id":"slider_product_list","item_list_name":"Slider Product List","index":13}]}},"371430":{"event":"select_item","ecommerce":{"items":[{"item_name":"Vans Authentic Denim Sneaker Mens Blue\/White","item_id":"VAN8DM","currency":"ZAR","item_brand":"VANS","item_category":"Men","price":1099.99,"item_list_id":"slider_product_list","item_list_name":"Slider Product List","index":14}]}},"371429":{"event":"select_item","ecommerce":{"items":[{"item_name":"Puma Rebound Slam Lo Sneaker Youth Archive Green","item_id":"PMA8317YAG","currency":"ZAR","item_brand":"PUMA","item_category":"Kids","price":999.99,"item_list_id":"slider_product_list","item_list_name":"Slider Product List","index":15}]}},"371428":{"event":"select_item","ecommerce":{"items":[{"item_name":"Nike Air Max Portal Sneaker Womens Sail\/Light Brown","item_id":"NKK3253LB","currency":"ZAR","item_brand":"NIKE","item_category":"Women","price":2199.99,"item_list_id":"slider_product_list","item_list_name":"Slider Product List","index":16}]}}}}, 7935 typeSelectorMapping: [ 7936 { 7937 type: 'category', 7938 selector: ':is(.products-grid, .products-list)' 7939 }, 7940 { 7941 type: 'search_results', 7942 selector: ':is(.products-grid, .products-list)' 7943 }, 7944 { 7945 type: 'related', 7946 selector: '.catalog-product-view .product-slider :is(.js_slide, .swiper-slide)' 7947 }, 7948 { 7949 type: 'upsell', 7950 selector: '.catalog-product-view .product-slider :is(.js_slide, .swiper-slide)' 7951 }, 7952 { 7953 type: 'crosssell', 7954 selector: '.checkout-cart-index .product-slider :is(.js_slide, .swiper-slide)' 7955 }, 7956 { 7957 type: 'widget_product_list', 7958 selector: '.block.widget.block-products-list' 7959 }, 7960 { 7961 type: 'slider_product_list', 7962 selector: '.product-slider .js_slides :is(.js_slide, .swiper-slide)' 7963 } 7964 ], 7965 selectors: { 7966 productIdContainer: '[data-product-id="%productId%"]', 7967 containerToBind: 'a.product-item-photo, a.product-item-link', 7968 productItem: '.product-item', 7969 reviewForm: '.review-form' 7970 }, 7971 7972 /** 7973 * @private 7974 * @return {void} 7975 */ 7976 initClickTracking() { 7977 const reviewForm = document.querySelector(this.selectors.reviewForm); 7978 if (reviewForm) { 7979 reviewForm.addEventListener('click', () => { 7980 setTimeout(() => { 7981 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 7982 }, 1500); 7983 }); 7984 } 7985 7986 this.typeSelectorMapping.forEach((mapping) => { 7987 this.getProductIds(mapping.type).forEach((productId) => { 7988 const eventData = this.getEventData(mapping.type, productId); 7989 if (eventData) { 7990 this.setProductClickEvent.bind(this, mapping.selector, productId, eventData)(); 7991 } 7992 }); 7993 }); 7994 }, 7995 7996 /** 7997 * @param {string} listContainerSelector 7998 * @param {string} productId 7999 * @param {Object} eventData 8000 * @return {void} 8001 */ 8002 setProductClickEvent(listContainerSelector, productId, eventData) { 8003 const containerToBind = this.findProductContainer(listContainerSelector, productId); 8004 8005 if (!containerToBind.length) { 8006 return; 8007 } 8008
8009 containerToBind.forEach(elem => { 8010 elem.addEventListener('click', () => { 8011 this.triggerSelectItem(eventData); 8012 }) 8013 }) 8014 }, 8015 8016 /** 8017 * @param {Object} eventData 8018 * @return {void} 8019 */ 8020 triggerSelectItem(eventData) { 8021 window.dataLayer.push({ecommerce: null}); 8022 window.dataLayer.push(eventData); 8023 }, 8024 8025 /** 8026 * @param {string} listContainerSelector 8027 * @param {string} productId 8028 * @return {*[]} 8029 */ 8030 findProductContainer(listContainerSelector, productId) { 8031 let productIdContainerSelector, 8032 productIdContainer, 8033 itemElement, 8034 containerToBind; 8035 8036 productIdContainerSelector = listContainerSelector 8037 + ' ' + this.selectors.productIdContainer.replace('%productId%', productId); 8038 8039 productIdContainer = document.querySelector(productIdContainerSelector); 8040 8041 if (!productIdContainer) { 8042 return []; 8043 } 8044 8045 itemElement = productIdContainer.closest(this.selectors.productItem); 8046 containerToBind = itemElement?.querySelectorAll(this.selectors.containerToBind); 8047 8048 return containerToBind ? containerToBind : []; 8049 }, 8050 8051 /** 8052 * @return {string[]} 8053 */ 8054 getProductIds(type) { 8055 return Object.keys(this.productEventData?.[type] ?? {}); 8056 }, 8057 8058 /** 8059 * @param {string} type 8060 * @param {string} productId 8061 * @return {Object|null} 8062 */ 8063 getEventData(type, productId) { 8064 return this.productEventData?.[type]?.[productId] ?? null; 8065 } 8066 } 8067 8068 window.addEventListener('init-external-scripts', () => { 8069 clickTracking.initClickTracking(); 8070 }, { once: true, passive: true }); 8071 })() 8072</script>
8072 8073
8074<script> 8075 (_ => { 8076 'use strict'; 8077 8078 const SECTION_KEY = 'amga4_hyva_event_pool'; 8079 const MAGE_CACHE_SECTION_KEY = 'mage-cache-storage'; 8080 const storage = hyva.getBrowserStorage(); 8081 8082 window.addEventListener('private-content-loaded', ({ detail }) => { 8083 if (!detail?.data) { 8084 return; 8085 } 8086 8087 const data = detail?.data[SECTION_KEY]; 8088 if (!data) { 8089 return; 8090 } 8091 8092 let eventsPool = data?.events || []; 8093 if (eventsPool.length) { 8094 eventsPool.forEach(event => { 8095 window.dataLayer.push({ecommerce: null}); 8096 window.dataLayer.push(event); 8097 }); 8098 8099 setTimeout(() => { 8100 storage.setItem( 8101 MAGE_CACHE_SECTION_KEY, 8102 JSON.stringify({ 8103 ...detail.data, 8104 [SECTION_KEY]: {} 8105 }) 8106 ); 8107 }, 0); 8108 } 8109 }); 8110 })(); 8111</script>
8111 8112 8113
8113<script type="text/javascript"> 8114 function klevu_addtocart(id, url, qty) { 8115 let form = document.createElement("form"); 8116 form.setAttribute('method', "post"); 8117 form.setAttribute('action', "https://www.studio-88.co.za/checkout/cart/add/"); 8118 8119 let formKey = document.createElement("input"); //input element form_key, hidden 8120 formKey.setAttribute('type', "hidden"); 8121 formKey.setAttribute('name', "form_key"); 8122 formKey.setAttribute('value', hyva.getFormKey()); 8123 8124 let productId = document.createElement("input"); //input element product, hidden 8125 productId.setAttribute('type', "hidden"); 8126 productId.setAttribute('name', "product"); 8127 productId.setAttribute('value', id); 8128 8129 let quantity = document.createElement("input"); //input element qty, hidden 8130 quantity.setAttribute('type', "hidden"); 8131 quantity.setAttribute('name', "qty"); 8132 quantity.setAttribute('value', qty); 8133 8134 form.appendChild(formKey); 8135 form.appendChild(productId); 8136 form.appendChild(quantity); 8137 8138 document.body.appendChild(form); 8139 form.submit(); 8140 document.body.removeChild(form); 8141 } 8142</script>
8142 8143
8143<script type="text/javascript">var klevu_cms_module_enabled = true;</script>
vendor: 1 bytes, line 8143
8143
8144<script type="text/javascript"> 8145 var klevu_cmsSearchEnabled = false; 8146</script>
8146<!-- BLOCK remarkety.tracking.general -->
8146<script> 8147 var _rmData = _rmData || []; 8148 _rmData.push(['setStoreKey', 'ym6nvMm7']); 8149 </script>
8149 8150
8150<script>(function(d, t) { 8151 var g = d.createElement(t), 8152 s = d.getElementsByTagName(t)[0]; 8153 g.src = 'https://d3ryumxhbd2uw7.cloudfront.net/webtracking/track.js'; 8154 s.parentNode.insertBefore(g, s); 8155 }(document, 'script'));</script>
8155 8156<!-- /BLOCK remarkety.tracking.general -->
8157<script> 8158 (() => { 8159 function src_default(Alpine) { 8160 Alpine.directive("intersect", (el, { value, expression, modifiers }, { evaluateLater, cleanup }) => { 8161 let evaluate = evaluateLater(expression); 8162 let options = { 8163 rootMargin: getRootMargin(modifiers), 8164 threshold: getThreshhold(modifiers) 8165 }; 8166 let observer = new IntersectionObserver((entries) => { 8167 entries.forEach((entry) => { 8168 if (entry.isIntersecting === (value === "leave")) 8169 return; 8170 evaluate(); 8171 modifiers.includes("once") && observer.disconnect(); 8172 }); 8173 }, options); 8174 observer.observe(el); 8175 cleanup(() => { 8176 observer.disconnect(); 8177 }); 8178 }); 8179 } 8180 function getThreshhold(modifiers) { 8181 if (modifiers.includes("full")) 8182 return 0.99; 8183 if (modifiers.includes("half")) 8184 return 0.5; 8185 if (!modifiers.includes("threshold")) 8186 return 0; 8187 let threshold = modifiers[modifiers.indexOf("threshold") + 1]; 8188 if (threshold === "100") 8189 return 1; 8190 if (threshold === "0") 8191 return 0; 8192 return Number(`.${threshold}`); 8193 } 8194 function getLengthValue(rawValue) { 8195 let match = rawValue.match(/^(-?[0-9]+)(px|%)?$/); 8196 return match ? match[1] + (match[2] || "px") : void 0; 8197 } 8198 function getRootMargin(modifiers) { 8199 const key = "margin"; 8200 const fallback = "0px 0px 0px 0px"; 8201 const index = modifiers.indexOf(key); 8202 if (index === -1) 8203 return fallback; 8204 let values = []; 8205 for (let i = 1; i < 5; i++) { 8206 values.push(getLengthValue(modifiers[index + i] || "")); 8207 } 8208 values = values.filter((v) => v !== void 0); 8209 return values.length ? values.join(" ").trim() : fallback; 8210 } 8211 8212 document.addEventListener("alpine:init", () => { 8213 window.Alpine.plugin(src_default); 8214 }); 8215 })(); 8216</script>
vendor: 1 bytes, line 8216
8216
8217<script> 8218 for (const [selector, deferUntil] of Object.entries({".product-slider section[x-data]":"intersect",".product-info [x-data]":"intersect","#filters-content [x-data]":"intersect","#review_form":"intersect","section[x-data^=initRecentlyViewedProductsComponent]":"intersect","div[x-data^=initBundleOptions]":"intersect","#product_addtocart_form [x-data]":"intersect","#notice-cookie-block":"intersect"})) { 8219 document.querySelectorAll(selector).forEach(el => el.setAttribute('x-defer', `${deferUntil}`)); 8220 } 8221</script>
vendor: 1 bytes, line 8221
8221
8222<script> 8223 (function () { 8224 "use strict"; 8225 8226 const hasAlpine = new Promise(resolve => { 8227 window.addEventListener('alpine:initialized', resolve, {once: true, passive: true}); 8228 }); 8229 8230 const hasInteract = new Promise(resolve => { 8231 (events => { 8232 const onInteract = () => { 8233 resolve(); 8234 events.forEach(type => window.removeEventListener(type, onInteract)); 8235 } 8236 events.forEach(type => window.addEventListener(type, onInteract, {once: true, passive: true})) 8237 })(['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown']) 8238 }); 8239 8240 const onIntersect = (el) => { 8241 return new Promise(resolve => { 8242 const observer = new IntersectionObserver(entries => { 8243 for (const entry of entries) { 8244 if (entry.isIntersecting) { 8245 observer.disconnect() 8246 resolve(); 8247 } 8248 } 8249 }, {}); 8250 observer.observe(el); 8251 }); 8252 } 8253 8254 function runComponent(el) { 8255 hasAlpine.then(() => { 8256 el.removeAttribute('x-ignore'); 8257 queueMicrotask(() => Alpine.initTree(el)); 8258 }); 8259 } 8260 8261 function initDeferredComponents() { 8262 document.querySelectorAll('[x-data][x-defer]').forEach(el => { 8263 el.setAttribute('x-ignore', ''); 8264 const deferUntil = (el.getAttribute('x-defer') || '').trim(); 8265 switch (deferUntil) { 8266 case 'interact': 8267 hasInteract.then(() => runComponent(el)); 8268 break; 8269 case 'intersect': 8270 onIntersect(el).then(() => runComponent(el)) 8271 break; 8272 case 'idle': 8273 window.requestIdleCallback 8274 ? window.requestIdleCallback(() => runComponent(el), {timeout: 4000}) 8275 : setTimeout(() => runComponent(el), 4000); 8276 break; 8277 case 'eager': 8278 runComponent(el); 8279 break; 8280 default: 8281 if (deferUntil.startsWith('event:') && deferUntil.length > 6) { 8282 window.addEventListener(deferUntil.substring(6), () => runComponent(el), {once: true, passive: true}); 8283 } 8284 } 8285 }); 8286 } 8287 8288 window.addEventListener('alpine:init', initDeferredComponents, {once: true, passive: true}); 8289 })() 8290</script>
vendor: 1 bytes, line 8290
8290
8291<script> 8292// x-teleport implementation 8293(_ => { 8294 document.addEventListener('DOMContentLoaded', event => { 8295 // x-teleport available only for template 8296 if (!Alpine.version.startsWith('2')) { 8297 return; 8298 } 8299 8300 const teleportDirective = (el, expression) => { 8301 let target = document.querySelector(expression) 8302 let clone = el.content.cloneNode(true).firstElementChild; 8303 8304 target.appendChild(clone); 8305 setTimeout(() => { 8306 target.querySelectorAll('[x-data]') 8307 .forEach(el => Alpine.initializeComponent(el)); 8308 }, 0) 8309 }; 8310 8311 document.querySelectorAll('template').forEach(el => { 8312 if (!el.hasAttribute('x-teleport')) { 8313 return; 8314 } 8315 8316 teleportDirective(el, el.getAttribute('x-teleport')); 8317 }); 8318 8319 document.querySelectorAll('[x-data]').forEach(el => { 8320 if (el.hasAttribute('x-init')) { 8321 return; 8322 } 8323 8324 setTimeout(() => { 8325 Alpine.initializeComponent(el); 8326 }, 0); 8327 }); 8328 }); 8329})() 8330</script>
8330 8331
8332<script type="module" 8333 src="https://www.studio-88.co.za/static/version1787084018/frontend/Vendor/Studio88/en_US/Hyva_Theme/js/alpine3.min.js" 8334 defer 8335 crossorigin 8336></script>
vendor: 1 bytes, line 8336
8336
8337<script> 8338 'use strict'; 8339 function dispatchMessages(messages, hideAfter) { 8340 const messagesEvent = new CustomEvent("messages-loaded", { 8341 detail: { 8342 messages: messages, 8343 hideAfter: hideAfter 8344 } 8345 }); 8346 window.dispatchEvent(messagesEvent); 8347 } 8348 8349 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 8350 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 8351 } 8352 8353 (function( hyva, undefined ) { 8354 8355 hyva.initFormKey = () => { 8356 const inputSelector = 'input[name="form_key"]', 8357 formKey = hyva.getFormKey(); 8358 8359 Array.from(document.querySelectorAll(inputSelector)).map(function (input) { 8360 input.value = formKey 8361 }); 8362 } 8363 8364 hyva.initMessages = () => { 8365 try { 8366 const messages = hyva.getCookie('mage-messages'); 8367 window.mageMessages = messages ? JSON.parse(decodeURIComponent(messages).replace(/\+/g, ' ')) : []; 8368 8369 dispatchMessages(window.mageMessages); 8370 8371 // empty `mage-messages` cookie 8372 const skipSetDomain = true; 8373 hyva.setCookie('mage-messages','', -1, skipSetDomain); 8374 8375 } catch (error) { 8376 console.warn('Error parsing Cookie Messages:', error); 8377 } 8378 } 8379 8380 window.addEventListener('DOMContentLoaded', hyva.initFormKey); 8381 hyva.alpineInitialized(hyva.initMessages) 8382 8383 }( window.hyva = window.hyva || {} )); 8384</script>
vendor: 1 bytes, line 8384
8384
8385<script> 8386 'use strict'; 8387 { 8388 const private_content_key = 'mage-cache-storage'; 8389 const private_content_expire_key = 'mage-cache-timeout'; 8390 const private_content_version_key = 'private_content_version'; 8391 const section_data_ids_key = 'section_data_ids'; 8392 const mage_cache_session_id_key = 'mage-cache-sessid'; 8393 const last_visited_store_key = 'last_visited_store'; 8394 8395 const ttl = 3600; 8396 8397 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 8398 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 8399 } 8400 8401 function loadSectionData () { 8402 const browserStorage = hyva.getBrowserStorage(); 8403 if (!browserStorage) { 8404 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 8405 [{ 8406 type: "warning", 8407 text: "Please enable LocalStorage in your browser." 8408 }] 8409 ); 8410 return; 8411 } 8412 try { 8413 let isInvalid = false; 8414 8415 if (hyva.getCookie(last_visited_store_key) !== CURRENT_STORE_CODE) { 8416 isInvalid = true; 8417 } 8418 hyva.setCookie(last_visited_store_key, CURRENT_STORE_CODE, false, false); 8419 8420 if (!hyva.getCookie(mage_cache_session_id_key)) { 8421 isInvalid = true; 8422 browserStorage.removeItem(private_content_key); 8423 const skipSetDomain = true; 8424 const days = false; 8425 hyva.setCookie(mage_cache_session_id_key, true, days, skipSetDomain) 8426 } 8427 8428 const cookieVersion = hyva.getCookie(private_content_version_key); 8429 const storageVersion = browserStorage.getItem(private_content_version_key); 8430 8431 if (cookieVersion && !storageVersion || cookieVersion !== storageVersion) { 8432 isInvalid = true; 8433 } 8434 8435 const privateContentExpires = browserStorage.getItem(private_content_expire_key); 8436 if (privateContentExpires && new Date(privateContentExpires) < new Date()) { 8437 browserStorage.removeItem(private_content_key); 8438 } 8439 8440 if (isInvalid && cookieVersion) { 8441 fetchPrivateContent([]); 8442 } else if (cookieVersion && storageVersion && cookieVersion === storageVersion) { 8443 const privateContent = JSON.parse(browserStorage.getItem(private_content_key)); 8444 if ( 8445 privateContent && 8446 privateContentExpires && 8447 privateContent.cart && 8448 privateContent.customer 8449 ) { 8450 dispatchPrivateContent(privateContent); 8451 } else { 8452 fetchPrivateContent([]); 8453 } 8454 } else { 8455 if (document.getElementById('default-section-data')) { 8456 const privateContent = JSON.parse(document.getElementById('default-section-data').innerText.trim()); 8457 dispatchPrivateContent(privateContent); 8458 } else { 8459 dispatchPrivateContent({}); 8460 } 8461 } 8462 8463 } catch (error) { 8464 console.warn('Error retrieving Private Content:', error); 8465 } 8466 } 8467 8468 hyva.alpineInitialized(loadSectionData) 8469 window.addEventListener('reload-customer-section-data', loadSectionData); 8470 8471 function dispatchPrivateContent(data) { 8472 const privateContentEvent = new CustomEvent("private-content-loaded", { 8473 detail: { 8474 data: data 8475 } 8476 }); 8477 window.dispatchEvent(privateContentEvent); 8478 } 8479 8480 function fetchPrivateContent(sections) { 8481 fetch(`${BASE_URL}customer/section/load/?sections=${encodeURIComponent(sections.join(','))}`, { 8482 method: 'GET', 8483 headers: { 8484 'Content-Type': 'application/json', 8485 'X-Requested-With': 'XMLHttpRequest' 8486 } 8487 }) 8488 .then(response => response.json()) 8489 .then( 8490 data => { 8491 if (data) { 8492 try { 8493 const browserStorage = hyva.getBrowserStorage(); 8494 8495 // merge new data preserving non-invalidated sections 8496 const oldSectionData = JSON.parse(browserStorage.getItem(private_content_key) || '{}') || {}; 8497 8498 if ((! data.cart || ! data.cart.cartId) && oldSectionData['checkout-data']) { 8499 delete oldSectionData['checkout-data']; 8500 } 8501 const newSectionData = Object.assign(oldSectionData, data); 8502 8503 dispatchPrivateContent(newSectionData); 8504 8505 // don't persist messages, they've been dispatched already 8506 if (newSectionData.messages && newSectionData.messages.messages ) { 8507 newSectionData.messages.messages = []; 8508 } 8509 8510 browserStorage.setItem(private_content_key, JSON.stringify(newSectionData)); 8511 8512 const expiresAt = new Date(Date.now() + (ttl * 1000)).toISOString(); 8513 browserStorage.setItem(private_content_expire_key, expiresAt); 8514 8515 const newCookieVersion = hyva.getCookie(private_content_version_key); 8516 browserStorage.setItem(private_content_version_key, newCookieVersion); 8517 8518 // We don't need the section_data_ids in Hyvä, but we store them for compatibility 8519 // with Luma Fallback. Otherwise, not all sections are loaded in Luma Checkout 8520 hyva.setCookie( 8521 section_data_ids_key, 8522 JSON.stringify( 8523 Object.keys(data).reduce((sectionDataIds, sectionKey) => { 8524 sectionDataIds[sectionKey] = data[sectionKey]['data_id']; 8525 return sectionDataIds; 8526 }, {}) 8527 ), 8528 false, 8529 true 8530 ); 8531 } catch (error) { 8532 console.warn("Couldn't store privateContent", error); 8533 } 8534 } 8535 } 8536 ); 8537 } 8538 } 8539</script>
vendor: 1 bytes, line 8539
8539
8540<script> 8541 (() => { 8542 document.addEventListener('submit', event => event.target.action = event.target.action.replace('%25uenc%25', hyva.getUenc())); 8543 })() 8544</script>
vendor: 1 bytes, line 8544
8544
8545<script> 8546 (events => { 8547 const dispatchUserInteractionEvent = () => { 8548 events.forEach(type => window.removeEventListener(type, dispatchUserInteractionEvent)) 8549 window.dispatchEvent(new Event('init-external-scripts')) 8550 }; 8551 events.forEach(type => window.addEventListener(type, dispatchUserInteractionEvent, {once: true, passive: true})) 8552 })(['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown']) 8553</script>
vendor: 1 bytes, line 8553
8553
8554<script> 8555 function initWishlist() { 8556 return { 8557 addToWishlist(productId) { 8558 const formKey = hyva.getFormKey(); 8559 const postUrl = BASE_URL + 'wishlist/index/add/'; 8560 8561 fetch(postUrl, { 8562 "headers": { 8563 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 8564 }, 8565 "body": "form_key=" + formKey + "&product=" + productId + "&uenc=" + hyva.getUenc(), 8566 "method": "POST", 8567 "mode": "cors", 8568 "credentials": "include" 8569 }).then(function (response) { 8570 if (response.redirected) { 8571 window.location.href = response.url; 8572 } else if (response.ok) { 8573 return response.json(); 8574 } else { 8575 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 8576 [{ 8577 type: "warning", 8578 text: "Could\u0020not\u0020add\u0020item\u0020to\u0020wishlist." 8579 }], 5000 8580 ); 8581 } 8582 }).then(function (response) { 8583 if (!response) { return } 8584 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 8585 [{ 8586 type: (response.success) ? "success" : "error", 8587 text: (response.success) 8588 ? "Product\u0020has\u0020been\u0020added\u0020to\u0020your\u0020Wish\u0020List." 8589 : response.error_message 8590 }], 5000 8591 ); 8592 const reloadCustomerDataEvent = new CustomEvent("reload-customer-section-data"); 8593 window.dispatchEvent(reloadCustomerDataEvent); 8594 }).catch(function (error) { 8595 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 8596 [{ 8597 type: "error", 8598 text: error 8599 }], 5000 8600 ); 8601 }); 8602 } 8603 } 8604 } 8605</script>
vendor: 1 bytes, line 8605
8605
8606<script> 8607 function initPriceBox() 8608 { 8609 return { 8610 updatePrice(priceData) { 8611 const regularPriceLabel = this.$root.querySelector('.normal-price .price-label'); 8612 const regularPriceElement = this.$root.querySelector('.normal-price [data-price-type=finalPrice].price-wrapper .price'); 8613 const basePriceElement = this.$root.querySelector('.normal-price [data-price-type=basePrice].price-wrapper .price'); 8614 if (priceData.finalPrice.amount < priceData.oldPrice.amount) { 8615 regularPriceLabel && regularPriceLabel.classList.add('hidden'); 8616 } else { 8617 regularPriceLabel && regularPriceLabel.classList.remove('hidden'); 8618 } 8619 regularPriceElement && (regularPriceElement.innerText = hyva.formatPrice(priceData.finalPrice.amount)); 8620 basePriceElement && (basePriceElement.innerText = hyva.formatPrice(priceData.basePrice.amount)); 8621 } 8622 } 8623 } 8624</script>
8624 8625<div id="checkOut" class="bg-black text-center py-6"> 8626<p class="text-sm text-gray-200 text-white"> 8627 Copyright © Studio 88 â All Rights Reserved. 8628</p> 8629</div>
8629<script> 8630 function initConfigurableOptions(productId, optionConfig) { 8631 8632 function findPathParam(key) { 8633 const baseUrl = (BASE_URL.substring(0, 2) === '//' ? 'http:' : '') + BASE_URL; 8634 const baseUrlParts = (new URL(baseUrl)).pathname.replace(/\/$/, '').split('/'); 8635 const pathParts = window.location.pathname.split('/').slice(baseUrlParts.length + 3); 8636 for (let i = 0; i < pathParts.length; i += 2) { 8637 if (pathParts[i] === key && pathParts.length > i) { 8638 return pathParts[i + 1]; 8639 } 8640 } 8641 } 8642 8643 return { 8644 optionConfig, 8645 productId, 8646 itemId: (new URLSearchParams(window.location.search)).get('id') || findPathParam('id'), 8647 allowedAttributeOptions: [], 8648 selectedValues: [], 8649 init() { 8650 this.findAllowedAttributeOptions(); 8651 this.$nextTick(() => { 8652 if (typeof this.optionConfig.defaultValues === 'object') { 8653 for (const [attributeId, value] of Object.entries(this.optionConfig.defaultValues)) { 8654 this.changeOption(attributeId, value + ''); 8655 } 8656 } 8657 }); 8658 }, 8659 findSimpleIndex() { 8660 this.productIndex = this.calculateSimpleIndexForPartialSelection(this.selectedValues); 8661 }, 8662 calculateSimpleIndexForPartialSelection(selectedValues) { 8663 if (selectedValues.length === 0) return 0; 8664 let productIndexIds = Object.keys(this.optionConfig.index); 8665 Object.keys(this.optionConfig.attributes).forEach((attribute) => { 8666 const productsWithAttributeMatch = selectedValues[attribute] 8667 ? productIndexIds.filter((productIndex) => { 8668 return this.optionConfig.index[productIndex][attribute] === this.selectedValues[attribute] 8669 }) 8670 : []; 8671 8672 productIndexIds = productsWithAttributeMatch.length ? productsWithAttributeMatch : productIndexIds 8673 }) 8674 return productIndexIds[0]; 8675 }, 8676 calculateSimpleIndexForFullSelection(selectedValues) { 8677 const productIndexes = this.optionConfig.index; 8678 return Object.keys(productIndexes).find(productIndex => { 8679 const productCandidateOptions = productIndexes[productIndex]; 8680 8681 for (const productOption in productCandidateOptions) { 8682 if ( 8683 ! selectedValues[productOption] || 8684 selectedValues[productOption] !== productCandidateOptions[productOption] 8685 ) { 8686 return false; 8687 } 8688 } 8689 return productIndex; 8690 }); 8691 }, 8692 productIndex: 0, 8693 findAllowedAttributeOptions() { 8694 this.allowedAttributeOptions = this.calculateAllowedAttributeOptions(this.selectedValues); 8695 }, 8696 calculateAllowedAttributeOptions(selectedValues) { 8697 const allAttributes = this.optionConfig.attributes; 8698 const allAttributesSorted = Object.values(allAttributes).sort((a,b) => { 8699 return a.position - b.position 8700 }); 8701 8702 const newAllowedAttributeOptions = []; 8703 8704 allAttributesSorted.forEach(attribute => { 8705 const selectionWithoutAttr = Object.assign({}, this.removeAttrFromSelection(selectedValues, attribute.id)); 8706 const availableIndexes = this.calculateAvailableProductIndexes(selectionWithoutAttr); 8707 newAllowedAttributeOptions[attribute.id] = allAttributes[attribute.id].options.filter(option => { 8708 return !!option.products.find(product => { 8709 return availableIndexes.includes(product); 8710 }) 8711 }); 8712 }); 8713 return newAllowedAttributeOptions; 8714 }, 8715 calculateAvailableProductIndexes(selectedOptions) { 8716 if (Object.keys(selectedOptions).length === 0) { 8717 if (Object.values(this.optionConfig.salable || {}).length) { 8718 return [].concat.apply([], [].concat.apply([], Object.values(this.optionConfig.salable).map(Object.values))).filter((x, i, a) => a.indexOf(x) === i) 8719 } 8720 return Object.keys(this.optionConfig.index); 8721 } 8722 8723 const selectedIds = Object.keys(selectedOptions); 8724 if (Object.values(this.optionConfig.salable || {}).length) { 8725 const selectedOptionIndexes = selectedIds.map(attrId => { 8726 const optionValue = selectedOptions[attrId]; 8727 return this.optionConfig.salable[attrId] && this.optionConfig.salable[attrId][optionValue] || [] 8728 }) 8729 return selectedOptionIndexes.reduce((acc, optionIndexes) => { 8730 return acc.filter(index => optionIndexes.includes(index)); 8731 }); 8732 } else { 8733 const productIndexes = this.optionConfig.index; 8734 return Object.keys(productIndexes).filter(index => { 8735 for (const attrId of selectedIds) { 8736 if (productIndexes[index][attrId] !== `${selectedOptions[attrId]}`) return false 8737 } 8738 return true 8739 }); 8740 } 8741 }, 8742 findAttributeByOptionId(optionId) { 8743 for (const attributeId in this.optionConfig.attributes) { 8744 const attributeOptions = this.optionConfig.attributes[attributeId].options || []; 8745 if (attributeOptions.find(option => option.id === optionId)) { 8746 return attributeId; 8747 } 8748 } 8749 }, 8750 getAllowedAttributeOptions(attributeId) { 8751 return this.allowedAttributeOptions[attributeId] || [] 8752 }, 8753 getProductIdsForOption(option) { 8754 const attributeId = this.findAttributeByOptionId(option.id); 8755 const allOptions = this.optionConfig.attributes[attributeId]; 8756 const opt = (allOptions && allOptions.options || []).find(o => o.id === option.id); 8757 return opt && opt.products 8758 ? opt.products 8759 : []; 8760 }, 8761 findProductIdsForPartialSelection(optionSelection) { 8762 const candidateProducts = Object.values(optionSelection).reduce((candidates, optionId) => { 8763 const newCandidates = this.getProductIdsForOption({id: optionId}); 8764 return candidates === null 8765 ? newCandidates 8766 : candidates.filter(productId => newCandidates.includes(productId)); 8767 }, null); 8768 return candidateProducts || []; 8769 },
8770 findCheapestProductForPartialSelection(optionSelection) { 8771 const candidateProducts = this.findProductIdsForPartialSelection(optionSelection); 8772 return candidateProducts.reduce((cheapest, simpleIdx) => { 8773 // in the first iteration we start with simpleIdx as the currently cheapest product 8774 if (! this.optionConfig.optionPrices[cheapest]) return simpleIdx; 8775 const knownCheapestPrice = this.optionConfig.optionPrices[cheapest].finalPrice.amount; 8776 return knownCheapestPrice > this.optionConfig.optionPrices[simpleIdx].finalPrice.amount 8777 ? simpleIdx 8778 : cheapest; 8779 }, 0) 8780 }, 8781 findProductIdToUseForOptionPrice(option) { 8782 // try to find a product for a complete selection 8783 const attributeId = this.findAttributeByOptionId(option.id); 8784 const optionSelection = Object.assign({}, this.selectedValues, {[attributeId]: option.id}); 8785 const matchingSimpleIndex = this.calculateSimpleIndexForFullSelection(optionSelection); 8786 // if there is no complete selection, use the cheapest product for the option 8787 return matchingSimpleIndex || this.findCheapestProductForPartialSelection(optionSelection); 8788 }, 8789 getAttributeOptionLabel(option) { 8790 const optionProduct = this.findProductIdToUseForOptionPrice(option); 8791 if ((! optionProduct) || (optionProduct === this.productIndex)) { 8792 return option.label; 8793 } 8794 8795 const currentPrice = this.getOptionPriceAdjustmentBasePrice(); 8796 8797 if (this.optionConfig.optionPrices[optionProduct]) { 8798 const optionPrice = this.optionConfig.optionPrices[optionProduct].finalPrice.amount; 8799 if (optionPrice !== currentPrice){ 8800 return option.label + ' ' + hyva.formatPrice(optionPrice - currentPrice, true); 8801 } 8802 } 8803 return option.label; 8804 }, 8805 getOptionPriceAdjustmentBasePrice() { 8806 if (this.optionConfig.optionPrices[this.productIndex]) { 8807 return this.optionConfig.optionPrices[this.productIndex].finalPrice.amount 8808 } 8809 const cheapestForSelection = this.findCheapestProductForPartialSelection(this.selectedValues); 8810 return this.optionConfig.optionPrices[cheapestForSelection] 8811 ? this.optionConfig.optionPrices[cheapestForSelection].finalPrice.amount 8812 : this.optionConfig.prices.finalPrice.amount; // default price if no option selection 8813 }, 8814 clearOptionIfActive(optionId, value) { 8815 if (this.selectedValues[optionId] === value) { 8816 this.blurLabel() 8817 this.changeOption(optionId, '') 8818 } 8819 }, 8820 removeAttrFromSelection(selectedValues, attributeId) { 8821 attributeId = parseInt(attributeId); 8822 return selectedValues.reduce((newSelection, val, attr) => { 8823 if (attr !== attributeId) { 8824 newSelection[attr] = val; 8825 } 8826 return newSelection; 8827 }, []); 8828 }, 8829 changeOption(attributeId, value) { 8830 if (value === '') { 8831 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, attributeId) 8832 } else if (value && this.getAllowedAttributeOptions(attributeId).find(option => option.id === value)) { 8833 this.selectedValues[attributeId] = value; 8834 } 8835 this.findSimpleIndex(); 8836 this.findAllowedAttributeOptions(); 8837 this.updatePrices(); 8838 this.updateGallery(); 8839 window.dispatchEvent( 8840 new CustomEvent( 8841 'configurable-selection-changed', 8842 { 8843 detail: { 8844 productId: this.productId, 8845 optionId: attributeId, 8846 value: value, 8847 productIndex: this.productIndex, 8848 selectedValues: this.selectedValues, 8849 candidates: this.findProductIdsForPartialSelection(this.selectedValues), 8850 } 8851 } 8852 ) 8853 ); 8854 }, 8855 calculateIsMinimalPrice() { 8856 return ( 8857 this.selectedValues.filter(value => !!value).length < 8858 Object.keys(this.optionConfig.attributes).length 8859 ); 8860 }, 8861 updatePrices() { 8862 const value = this.productIndex ? 8863 this.optionConfig.optionPrices[this.productIndex] : 8864 this.optionConfig.prices; 8865 window.dispatchEvent( 8866 new CustomEvent( 8867 "update-prices-" + this.productId, 8868 { 8869 detail: Object.assign( 8870 value, 8871 { isMinimalPrice: this.calculateIsMinimalPrice() } 8872 ) 8873 } 8874 ) 8875 ); 8876 }, 8877 updateGallery () { 8878 if (this.productIndex) { 8879 const images = this.optionConfig.images[this.productIndex]; 8880 images && window.dispatchEvent(new CustomEvent( 8881 "update-gallery", 8882 { detail: this.sortImagesByPosition(images) } 8883 )); 8884 } else { 8885 window.dispatchEvent(new Event("reset-gallery")); 8886 } 8887 }, 8888 sortImagesByPosition(images) { 8889 return images.sort((x, y) => { 8890 return x.position === y.position ? 0 : (parseInt(x.position) > parseInt(y.position) ? 1 : -1) 8891 }); 8892 }, 8893 onGetCartData(data) { 8894 }, 8895 preselectCartItems(data) { 8896 // pre-select options based on cart data for current (quote) itemId 8897 const cart = data && data.cart; 8898 if (cart && cart.items) { 8899 const cartItem = cart.items.find((item) => { 8900 return ( 8901 item.item_id === this.itemId 8902 && item.product_id === this.productId 8903 ) 8904 }); 8905 if (cartItem && cartItem.options && cartItem.options.length) { 8906 cartItem.options.map(option => { 8907 this.changeOption(option.option_id, option.option_value); 8908 }) 8909 } 8910 } 8911 }, 8912 preselectQuerystringItems() { 8913 // pre-select option like ?size=167 8914 const urlQueryParams = new URLSearchParams(window.location.search.replace('?','')); 8915 this.preselectItemsBasedOnLocation(attribute => urlQueryParams.get(attribute.code)); 8916 }, 8917 preselectLocationHashItems() { 8918 // pre-select option like #144=167 8919 const urlHashParams = new URLSearchParams(window.location.hash.replace('#','')); 8920 this.preselectItemsBasedOnLocation(attribute => urlHashParams.get(attribute.id)); 8921 }, 8922 preselectItemsBasedOnLocation(getLocationValue) { 8923 Object.values(this.optionConfig.attributes).map(attribute => { 8924 this.changeOption(attribute.id, getLocationValue(attribute)) 8925 }); 8926 } 8927 } 8928 } 8929 8930</script>
vendor: 1 bytes, line 8930
8930
8931<script> 8932 function initSwatchOptions(swatchConfig) { 8933 return { 8934 swatchConfig, 8935 getAttributeSwatchData(attributeId) { 8936 const swatchConfig = Object.assign({}, this.swatchConfig[attributeId]); 8937 swatchConfig['details'] = JSON.parse(swatchConfig['additional_data']); 8938 8939 return swatchConfig; 8940 }, 8941 getAllAttributeOptions(attributeId) { 8942 return ( 8943 this.optionConfig.attributes[attributeId] && 8944 this.optionConfig.attributes[attributeId].options 8945 ) || [] 8946 }, 8947 optionIsActive(attributeId, optionId) { 8948 // return true if a product with this option is in stock 8949 return !!this.getAllowedAttributeOptions(attributeId).find( 8950 option => option.id === optionId 8951 ) 8952 }, 8953 optionIsEnabled(attributeId, optionId) { 8954 // return true if a product with this option is enabled 8955 for (const productId in this.optionConfig.index) { 8956 if (this.optionConfig.index[productId][attributeId] === optionId) { 8957 return true; 8958 } 8959 } 8960 return false; 8961 }, 8962 mapSwatchTypeNumberToTypeCode(typeNumber) { 8963 switch ("" + typeNumber) { 8964 case "1": 8965 return "color" 8966 case "2": 8967 return "image" 8968 case "3": 8969 return "empty" 8970 case "0": 8971 default: 8972 return "text" 8973 } 8974 }, 8975 getTypeOfFirstOption(attributeId) { 8976 for (const optionId in this.swatchConfig[attributeId]) { 8977 const option = this.swatchConfig[attributeId][optionId]; 8978 if (typeof option.type !== 'undefined') { 8979 return this.mapSwatchTypeNumberToTypeCode(option.type); 8980 } 8981 } 8982 }, 8983 getVisualSwatchType(attributeId, targetOptionId) { 8984 // If a type configuration is present for the given option id, use it 8985 const config = this.swatchConfig[attributeId]; 8986 if (config[targetOptionId] && typeof config[targetOptionId].type !== 'undefined') { 8987 return this.mapSwatchTypeNumberToTypeCode(config[targetOptionId].type); 8988 } 8989 8990 // Otherwise - if no config is present for the target option - use the type of the first option 8991 // with a type property from the attribute, thus assuming its the same type as the target option. 8992 // (This edge case condition can occur on single swatch products if some options are not salable) 8993 return this.getTypeOfFirstOption(attributeId); 8994 }, 8995 getSwatchType(attributeId, optionId) { 8996 // Deserialize the attribute details the first time they are used 8997 if (this.swatchConfig[attributeId] && ! this.swatchConfig[attributeId].details) { 8998 this.swatchConfig[attributeId] = this.getAttributeSwatchData(attributeId); 8999 } 9000 const type = this.swatchConfig[attributeId] && 9001 this.swatchConfig[attributeId].details && 9002 this.swatchConfig[attributeId].details.swatch_input_type || 9003 "empty"; 9004 return type === 'visual' ? this.getVisualSwatchType(attributeId, optionId) : type; 9005 }, 9006 isTextSwatch(attributeId, optionId) { 9007 return this.getSwatchType(attributeId, optionId) === 'text'; 9008 }, 9009 isVisualSwatch(attributeId, optionId) { 9010 const type = this.getSwatchType(attributeId, optionId); 9011 9012 return ['image', 'color'].includes(type); 9013 }, 9014 getSwatchBackgroundStyle(attributeId, optionId) {
9015 const config = this.getSwatchConfig(attributeId, optionId); 9016 const type = this.getSwatchType(attributeId, optionId); 9017 9018 if (type === "color") { 9019 return 'background-color:' + config.value; 9020 } else if (type === "image") { 9021 return "background: #ffffff url('" + config.value + "') no-repeat center"; 9022 } else { 9023 return ''; 9024 } 9025 }, 9026 getSwatchText(attributeId, optionId) { 9027 const config = this.getSwatchConfig(attributeId, optionId); 9028 return config.label || config.value || this.getOptionLabelFromOptionConfig(attributeId, optionId); 9029 }, 9030 getOptionLabelFromOptionConfig(attributeId, optionId) { 9031 // Fallback if no value is present in swatchConfig data 9032 // Reference issue https://gitlab.hyva.io/hyva-themes/magento2-default-theme/-/issues/190 9033 const option = this.getAllAttributeOptions(attributeId).filter(option => option.id === optionId); 9034 return option && option[0] && option[0].label ||''; 9035 }, 9036 getSwatchConfig(attributeId, optionId) { 9037 return this.swatchConfig[attributeId] && this.swatchConfig[attributeId][optionId] 9038 ? this.swatchConfig[attributeId][optionId] 9039 : false; 9040 }, 9041 activeTooltipItem: false, 9042 tooltipPositionElement: false, 9043 isTooltipVisible() { 9044 return this.activeTooltipItem &&
9045 this.getSwatchConfig( 9046 this.activeTooltipItem.attribute, 9047 this.activeTooltipItem.item 9048 ); 9049 }, 9050 isFirstItemCol() { 9051 return this.activeTooltipItem.index === 0; 9052 }, 9053 getTooltipImageStyle(attributeId, optionId) { 9054 const config = this.getSwatchConfig(attributeId, optionId); 9055 const type = this.getSwatchType(attributeId, optionId); 9056 9057 if (type === "color") { 9058 return 'background-color:' + config.value + '; width: 110px; height: 90px;'; 9059 } else if (type === "image") { 9060 return "background: #ffffff url('" + config.thumb + 9061 "') center center no-repeat; width: 110px; height: 90px;"; 9062 } else { 9063 return 'display:none'; 9064 } 9065 }, 9066 getTooltipPosition() { 9067 return this.tooltipPositionElement ? 9068 `top: ${this.tooltipPositionElement.offsetTop}px;` + 9069 `left: ${ 9070 this.tooltipPositionElement.offsetLeft - ( 9071 this.tooltipPositionElement.closest('.snap') && 9072 this.tooltipPositionElement.closest('.snap').scrollLeft || 9073 0 9074 ) 9075 }px;` : '' 9076 }, 9077 getTooltipLabel() { 9078 return this.getSwatchConfig(this.activeTooltipItem.attribute, this.activeTooltipItem.item).label 9079 }, 9080 focusedLabel: false, 9081 focusLabel(optionId) { 9082 this.focusedLabel = optionId; 9083 }, 9084 blurLabel() { 9085 this.focusedLabel = false; 9086 }, 9087 showSwatches: false, 9088 initShowSwatchesIntersect() { 9089 if ('IntersectionObserver' in window && !window.scrollY) { 9090 let io = new IntersectionObserver( 9091 entries => { 9092 entries.map(entry => { 9093 if (entry.isIntersecting) { 9094 this.showSwatches = true; 9095 io.unobserve(this.$root); 9096 } 9097 }) 9098 } 9099 ); 9100 io.observe(this.$root); 9101 } else { 9102 this.showSwatches = true 9103 } 9104 } 9105 } 9106 } 9107</script>
vendor: 1 bytes, line 9107
9107
9108<script> 9109 'use strict'; 9110 9111 const grecaptchaV2LoadCallbacks = []; 9112 9113 function executeRecaptchaLoadCallbacks () { 9114 while (window.grecaptcha && grecaptchaV2LoadCallbacks.length > 0) { 9115 const callback = grecaptchaV2LoadCallbacks.pop(); 9116 typeof callback === 'string' ? window[callback]() : callback(); 9117 } 9118 } 9119 9120 (function() { 9121 const loadRecaptchaScript = (event) => { 9122 9123 if (window.grecaptcha || !event.target.form) { 9124 return; 9125 } 9126 9127 const url = 'https://www.google.com/recaptcha/api.js?onload=executeRecaptchaLoadCallbacks'; 9128 const siteKey = '6LetacAaAAAAAFWxYRKZBsU_O08KlZ_zlaPiaegM'; 9129 const script = document.createElement('script'); 9130 script.type = 'text/javascript'; 9131 script.src = siteKey ? `${url}&render=${siteKey}` : `${url}&render=explicit`; 9132 script.async = true; 9133 document.head.appendChild(script); 9134 9135 script.addEventListener('load', () => { 9136 Array.from(document.querySelectorAll('button[data-recaptcha-btn]:disabled')).forEach(btn => btn.removeAttribute('disabled')); 9137 }); 9138 } 9139 9140 window.forceLoadRecaptchaScript = (form) => { 9141 loadRecaptchaScript({target: {form: form || 'dummy'}}); 9142 executeRecaptchaLoadCallbacks(); 9143 }; 9144 9145 document.body.addEventListener('input', loadRecaptchaScript, { once: true }); 9146 document.body.addEventListener('focus', loadRecaptchaScript, { once: true }); 9147 9148 }()); 9149 9150</script>
9150 9151 9152 <div 9153 class="z-10 fixed bottom-4 inset-x-4 sm:w-96 max-w-prose mx-auto p-4 rounded shadow-2xl bg-container-lighter 9154 lg:absolute lg:bottom-auto lg:right-auto lg:top-[var(--msrp-block-offset)] lg:left-[var(--msrp-inline-offset)] lg:shadow-lg lg:max-w-xs" 9155 :style="setPosition()" 9156 x-data="initMsrpPopover()" 9157 x-cloak 9158 x-show="open" 9159 x-transition:enter="transition ease-out duration-300 opacity-0 translate-y-full lg:-translate-y-0" 9160 x-transition:enter-end="transition ease-out duration-300 opacity-100 translate-y-0" 9161 x-transition:leave="transition ease-in duration-150 opacity-0 translate-y-full lg:-translate-y-0" 9162 @click.outside="closePopover($event)" 9163 @msrp-popover.window="showPopover($event.detail)" 9164 @resize.window.debounce="closePopover()" 9165 @keydown.window.escape="closePopover()" 9166 > 9167 <div class="flex justify-between gap-2 mb-2"> 9168 <strong id="map-popup-heading-price" class="font-bold" x-text="title"></strong> 9169 <button type="button" @click="closePopover()"> 9170 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" role="img"> 9171 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 9172<title>x</title></svg> 9173 </button> 9174 </div> 9175 9176 <template x-if="type !== 'help'"> 9177 <div class="mb-4 text-sm"> 9178 <dl class="price-box"> 9179 <div class="flex gap-2"> 9180 <dt class="font-semibold whitespace-nowrap">Price:</dt> 9181 <dd x-html="msrpPrice" class="flex flex-col"></dd> 9182 </div> 9183 <div class="flex gap-2"> 9184 <dt class="font-semibold whitespace-nowrap">Actual Price:</dt> 9185 <dd x-html="actualPrice" class="flex flex-col"></dd> 9186 </div> 9187 </dl> 9188 </div> 9189 </template> 9190 9191 <div class="text-sm" x-html="content"></div> 9192 9193
9193<script> 9194 function initMsrpPopover() { 9195 return { 9196 open: false, 9197 type: "price", // price or help 9198 title: '', 9199 content: 'Our price is lower than the manufacturer's "minimum advertised price." As a result, we cannot show you the price in catalog or the product page. <br><br> You have no obligation to purchase the product once you know the price. You can simply remove the item from your cart.', 9200 isSaleable: false, 9201 actualPrice: null, 9202 msrpPrice: null, 9203 productId: null, 9204 anchor: null, 9205 position: ['0', '0'], 9206 isInViewport() { 9207 const pageWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; 9208 const offset = this.anchor.offsetLeft + this.$root.offsetWidth; 9209 return pageWidth > offset; 9210 }, 9211 setPosition() { 9212 return { 9213 '--msrp-block-offset': this.position[0], 9214 '--msrp-inline-offset': this.position[1] 9215 } 9216 }, 9217 calcPosition() { 9218 const target = this.anchor; 9219 const pageTop = target.getBoundingClientRect().top + window.scrollY; 9220 const pageLeft = target.getBoundingClientRect().left + window.scrollX; 9221 const positionBlock = `${pageTop + target.clientHeight}px`; 9222 const positionInline = this.isInViewport() 9223 ? `${pageLeft}px` 9224 : `${(pageLeft - (this.$root.offsetWidth - target.offsetWidth))}px`; 9225 this.position = [positionBlock, positionInline] 9226 }, 9227 closePopover(event) { 9228 if (event && event.target === this.anchor) return; 9229 this.open = false; 9230 }, 9231 showPopover(event) { 9232 const data = event.msrpData; 9233 if (!data) return; 9234 9235 this.open = true; 9236 this.type = data.type; 9237 this.productId = data.id; 9238 this.isSaleable = data.isSaleable; 9239 this.actualPrice = data.realPrice; 9240 this.msrpPrice = data.msrpPrice; 9241 this.title = this.type === 'help' 9242 ? '' 9243 : data.productName; 9244 this.content = this.type === 'help' 9245 ? 'Our price is lower than the manufacturer's "minimum advertised price." As a result, we cannot show you the price in catalog or the product page. <br><br> You have no obligation to purchase the product once you know the price. You can simply remove the item from your cart.' 9246 : 'Our price is lower than the manufacturer's "minimum advertised price." As a result, we cannot show you the price in catalog or the product page. <br><br> You have no obligation to purchase the product once you know the price. You can simply remove the item from your cart.'; 9247 9248 if (event.target) { 9249 this.anchor = event.target; 9250 this.$nextTick(() => this.calcPosition()); 9251 } 9252 } 9253 } 9254 } 9255 </script>
9255 9256 </div>
9257<script> 9258 'use strict'; 9259 9260 (function () { 9261 const modals = []; 9262 const excludedFromFocusTrapping = new Set(); 9263 9264 function trapFocusInNextModalWithOverlay() { 9265 for (let idx = modals.length -1; idx >= 0; idx--) { 9266 const nextOnStack = modals[idx]; 9267 const nextDialogElement = nextOnStack.instance.$refs[nextOnStack.name]; 9268 if (! isOverlayDisabled(nextDialogElement)) { 9269 hyva.trapFocus(nextDialogElement); 9270 break; 9271 } 9272 } 9273 } 9274 9275 function focusables(dialogElement) { 9276 const selector = 'button, [href], input, textarea, select, details, [tabindex]:not([tabindex="-1"])'; 9277 return Array.from(dialogElement.querySelectorAll(selector)) 9278 .filter(el => !el.hasAttribute('disabled')); 9279 } 9280 9281 function firstVisible(elements) { 9282 const a = Array.from(elements); 9283 for (let i = 0; i < a.length; i++) { 9284 if (a[i].offsetWidth || a[i].offsetHeight || a[i].getClientRects().length) return a[i]; 9285 } 9286 return null; 9287 } 9288 9289 function isInViewport(element) { 9290 const rect = element && element.getBoundingClientRect(); 9291 return rect && 9292 rect.top >= 0 && 9293 rect.left >= 0 && 9294 rect.right <= window.innerWidth && 9295 rect.bottom <= window.innerHeight; 9296 } 9297 9298 function setFocusAfterTransition(dialogElement, duration) { 9299 const nested = Array.from(dialogElement.querySelectorAll('[role="dialog"]')); 9300 const candidates = Array.from(dialogElement.querySelectorAll('[x-focus-first]')); 9301 next: for (let candidate of candidates) { 9302 for (let child of nested) { 9303 if (child.contains(candidate)) continue next; 9304 } 9305 setTimeout(() => candidate.focus(), 50); 9306 break; 9307 } 9308 window.setTimeout(() => { 9309 const focusElement = firstVisible(dialogElement.querySelectorAll('[x-focus-first]')) || 9310 focusables(dialogElement)[0] || 9311 null; 9312 focusElement && isInViewport(focusElement) && focusElement.focus(); 9313 }, Math.max(1, duration)); 9314 } 9315 9316 function determineTrigger($refs, dialog, trigger) { 9317 if (typeof trigger === 'undefined' && typeof dialog === 'object' && dialog.target instanceof HTMLElement) { 9318 return dialog.target; 9319 } 9320 if (typeof dialog === 'string' && typeof trigger === 'object' && trigger.target instanceof HTMLElement) { 9321 return trigger.target; 9322 } 9323 if (typeof trigger === 'string') { 9324 try { 9325 return $refs[trigger] || document.querySelector(trigger) 9326 } catch (e) {} 9327 } 9328 if (trigger instanceof Element) { 9329 return trigger; 9330 } 9331 9332 return null; 9333 } 9334 9335 function isOverlayDisabled(dialog) { 9336 return dialog && dialog.hasAttribute('x-no-overlay') 9337 } 9338 9339 function areRemainingModalsWithoutOverlay(modals) 9340 { 9341 const overflowDisabled = modals.map(modal => modal.instance.$refs[modal.name]).filter(isOverlayDisabled); 9342 9343 return overflowDisabled.length === modals.length; 9344 } 9345 9346 window.hyva.modal = function(options) { 9347 9348 const config = Object.assign({ 9349 dialog: 'dialog', duration: 300, transitionEnter: 'transition ease-out duration-300', 9350 transitionEnterStart: 'opacity-0', 9351 transitionEnterEnd: 'opacity-100', 9352 transitionLeave: 'transition ease-in duration-300', 9353 transitionLeaveStart: 'opacity-100', 9354 transitionLeaveEnd: 'opacity-0', 9355 }, options); 9356 let lastHide = 0; 9357 9358 return { 9359 opened: {}, 9360 show(dialog, trigger) { 9361 const focusTargetAfterHide = determineTrigger(this.$refs, dialog, trigger); 9362 const name = typeof dialog === 'string' ? dialog : config.dialog; 9363 const dialogElement = this.$refs[name]; 9364 if (! dialogElement) { 9365 return; 9366 } 9367 const useOverlay = ! dialogElement.hasAttribute('x-no-overlay'); 9368 9369 dialogElement.scrollTop = 0; 9370 9371 if (this.opened[name]) { 9372 return; 9373 } 9374 9375 if (focusTargetAfterHide) { 9376 focusTargetAfterHide.setAttribute('aria-expanded', 'true'); 9377 } 9378 9379 this.opened[name] = true; 9380 useOverlay && this.$nextTick(() => hyva.trapFocus(dialogElement)); 9381 setFocusAfterTransition(dialogElement, config.duration); 9382 9383 const frame = {name, instance: this, focusTarget: focu
9383sTargetAfterHide, time: Date.now()}; 9384 9385 modals.push(frame); 9386 if (useOverlay) { 9387 document.body.classList.add('overflow-hidden'); 9388 } 9389 return new Promise(resolve => frame.resolve = resolve); 9390 }, 9391 cancel() { 9392 this.hide(false); 9393 }, 9394 ok() { 9395 this.hide(true); 9396 }, 9397 hide(value) { 9398 if (Date.now() - lastHide < config.duration) { 9399 return; 9400 } 9401 lastHide = Date.now(); 9402 9403 const modal = modals.pop() || {}; 9404 const name = modal.name; 9405 this.opened[name] = false; 9406 hyva.releaseFocus(modal.instance.$refs[modal.name]) 9407 trapFocusInNextModalWithOverlay(); 9408 9409 const nextFocusAfterHide = modal.focusTarget; 9410 nextFocusAfterHide && setTimeout(() => { 9411 nextFocusAfterHide.setAttribute('aria-expanded', 'false'); 9412 nextFocusAfterHide.focus() 9413 }, config.duration); 9414 9415 if (modals.length === 0 || areRemainingModalsWithoutOverlay(modals)) { 9416 document.body.classList.remove('overflow-hidden'); 9417 } 9418 9419 modal.resolve(value); 9420 }, 9421 overlay(dialog) { 9422 const name = typeof dialog === 'string' ? dialog : config.dialog; 9423 return { 9424 ['x-show']() { 9425 return this.opened[name] 9426 }, 9427 ['x-transition:enter']: config.transitionEnter, 9428 ['x-transition:enter-start']: config.transitionEnterStart, 9429 ['x-transition:enter-end']: config.transitionEnterEnd, 9430 ['x-transition:leave']: config.transitionLeave, 9431 ['x-transition:leave-start']: config.transitionLeaveStart, 9432 ['x-transition:leave-end']: config.transitionLeaveEnd, 9433 ['@hyva-modal-show.window'](event) { 9434 event.detail && event.detail.dialog === name && this.show(name, event.detail.focusAfterHide) 9435 } 9436 }; 9437 } 9438 }; 9439 } 9440 9441 window.hyva.modal.peek = () => modals.length > 0 && modals[modals.length -1] 9442 9443 window.hyva.modal.pop = function () { 9444 if (modals.length > 0) { 9445 const modal = modals[modals.length -1]; 9446 modal.instance.hide(); 9447 } 9448 } 9449 9450 window.hyva.modal.excludeSelectorsFromFocusTrap = function (selectors) { 9451 typeof selectors === 'string' || selectors instanceof String 9452 ? excludedFromFocusTrapping.add(selectors) 9453 : selectors.map(selector => excludedFromFocusTrapping.add(selector)); 9454 } 9455 9456 window.hyva.modal.eventListeners = { 9457 keydown: event => { 9458 if (event.key === 'Escape') { 9459 window.hyva.modal.pop(); 9460 } 9461 }, 9462 click: event => { 9463 if (modals.length > 0) { 9464 const modal = modals[modals.length -1]; 9465 const dialog = modal.instance.$refs[modal.name]; 9466 if (modal.time + 50 < Date.now() && // if last click processing is more than 50ms ago 9467 ! isOverlayDisabled(dialog) && // if dialog has overlay 9468 ! dialog.contains(event.target)) { // if click is outside of dialog 9469 modal.instance.hide(); 9470 } 9471 } 9472 } 9473 }; 9474 9475 document.addEventListener('keydown', window.hyva.modal.eventListeners.keydown); 9476 9477 document.addEventListener('click', window.hyva.modal.eventListeners.click); 9478 })(); 9479</script>
9479 9480
9481<script> 9482 (function () { 9483 'use strict'; 9484 9485 const storageName = 'AmastyCookieGroups'; 9486 9487 !!hyva.amStorage && hyva.amStorage.create(storageName, { 9488 /** 9489 * @type {{ 9490 * checked: boolean, 9491 * cookies: {description: string, lifetime: number|null, name: string, provider: string, type: string}[], 9492 * description: string, 9493 * groupId: string|number, 9494 * isEssential: boolean, 9495 * name: string 9496 * }[]} name 9497 */ 9498 groupData: [], 9499 cookiePolicy: undefined, 9500 lastUpdate: undefined, 9501 9502 /** 9503 * @returns {string|number[]} 9504 */ 9505 getEssentialGroupIds: function () { 9506 return this.groupData.reduce(function (essentialGroupIds, group) { 9507 if (group.isEssential) { 9508 essentialGroupIds.push(group.groupId) 9509 } 9510 9511 return essentialGroupIds; 9512 }, []); 9513 }, 9514 9515 /** 9516 * @returns {string|number[]} 9517 */ 9518 getCheckedGroupIds: function () { 9519 return this.groupData.reduce(function (checkedGroupIds, group) { 9520 if (group.checked) { 9521 checkedGroupIds.push(group.groupId); 9522 } 9523 9524 return checkedGroupIds; 9525 }, []); 9526 }, 9527 9528 /** 9529 * @param {string} cookieName 9530 * @returns {boolean} 9531 */ 9532 isCookieEssential: function (cookieName) { 9533 const allEssentialCookies = this.getEssentialCookies(); 9534 9535 return !!allEssentialCookies.find(cookie => cookie.name === cookieName); 9536 }, 9537 9538 /** 9539 * @returns {array} 9540 */ 9541 getEssentialCookies: function () { 9542 return this.groupData 9543 .filter(group => group.isEssential) 9544 .map(group => group.cookies) 9545 .flat(); 9546 }, 9547 9548 /** 9549 * @returns {boolean} 9550 */ 9551 hasGroupData: function () { 9552 return !!this.groupData.length; 9553 }, 9554 9555 /** 9556 * @param {string} cookieName 9557 * @param {string} pattern 9558 * @returns {boolean} 9559 */ 9560 isEssentialByPattern: function (cookieName, pattern) { 9561 const essentialCookiePatterns = this.getEssentialCookiesPattern(pattern); 9562 9563 return essentialCookiePatterns.some((pattern) => !!cookieName.match(this.getCookiePattern(pattern))); 9564 }, 9565 9566 /** 9567 * @param {string} pattern 9568 * @return {Array} 9569 */ 9570 getEssentialCookiesPattern: function (pattern) { 9571 return this.getCookiesByPattern(pattern, this.getEssentialCookies()); 9572 }, 9573 9574 /** 9575 * @param {string} pattern 9576 * @param {Array|undefined} cookies 9577 * @return {Array} 9578 */ 9579 getCookiesByPattern: function (pattern, cookies) { 9580 if (typeof cookie === 'undefined') { 9581 cookies = this.getAllCookies(); 9582 } 9583 9584 return cookies
9585 .map((cookie) => typeof cookie === 'object' ? cookie.name : cookie.split('=')[0].trim()) 9586 .filter((cookie) => cookie.match(pattern) !== null); 9587 }, 9588 9589 /** 9590 * @returns {array} 9591 */ 9592 getDisallowedCookies: function () { 9593 const disallowedCookie = decodeURIComponent(hyva.getCookie('amcookie_disallowed') ?? ''); 9594 if (!disallowedCookie) { 9595 return []; 9596 } 9597 9598 return disallowedCookie.split(','); 9599 }, 9600 9601 /** 9602 * @returns {string} 9603 */ 9604 getCookiePattern: function (pattern) { 9605 return `^${pattern.replaceAll('*', '.*')}$`; 9606 }, 9607 9608 /** 9609 * @returns {array} 9610 */ 9611 getAllCookies: function () { 9612 return document.cookie.split(';'); 9613 }, 9614 9615 /** 9616 * @param {boolean} setAcceptance 9617 * @returns {Promise} 9618 */ 9619 updateGroupData: function (setAcceptance = false) { 9620 const cookieFetchUrl = `${BASE_URL}amcookie/cookie/cookies`; 9621 return fetch(cookieFetchUrl, { 9622 method: "POST", 9623 headers: { 9624 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 9625 'X-Requested-With': 'XMLHttpRequest' 9626 }, 9627 body: new URLSearchParams({ 9628 allowed: hyva.getCookie('amcookie_allowed'), 9629 restriction: hyva.getCookie('amcookie_policy_restriction') 9630 }) 9631 }).then(response => { 9632 if (response.ok) { 9633 return response.json(); 9634 } 9635 9636 return Promise.reject(`${response.status} ${response.statusText}`); 9637 }).then(data => { 9638 this.cookiePolicy = data.cookiePolicy ?? 'allowed'; 9639 this.lastUpdate = data.lastUpdate ?? 0; 9640 this.groupData = [...data.groupData]; 9641 9642 hyva.setCookie('amcookie_policy_restriction', this.cookiePolicy, 10); 9643 if (setAcceptance) { 9644 amastyCookieManager.setLastCookieAcceptance(this.lastUpdate); 9645 } 9646 9647 return data; 9648 }).catch(error => { 9649 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 9650 { 9651 type: "error", 9652 text: error 9653 } 9654 ]); 9655 }).finally(() => window.dispatchEvent(new CustomEvent('amasty-cookie-group-updated'))); 9656 } 9657 }); 9658 })(); 9659</script>
vendor: 1 bytes, line 9659
9659
9660<script> 9661 (function (amastyCookieManager) { 9662 'use strict'; 9663 9664 amastyCookieManager.pattern = '{(?<cookiePattern>.*)}'; 9665 9666 amastyCookieManager.getAmastyCookieGroupStorage = function () { 9667 return hyva?.amStorage?.getStorage('AmastyCookieGroups') ?? {}; 9668 }; 9669 9670 amastyCookieManager.deleteDisallowedCookie = function () { 9671 const amastyCookieGroupStorage = this.getAmastyCookieGroupStorage(); 9672 const disallowedCookies = amastyCookieGroupStorage?.getDisallowedCookies() ?? []; 9673 9674 disallowedCookies.forEach((name) => { 9675 const pattern = name.match(this.pattern); 9676 if (!!pattern && !!amastyCookieGroupStorage) { 9677 const cookiePattern = amastyCookieGroupStorage.getCookiePattern(pattern.groups.cookiePattern) ?? ''; 9678 9679 amastyCookieGroupStorage.getCookiesByPattern(cookiePattern).forEach((cookieName) => { 9680 this.deleteNotEssentialCookie(cookieName); 9681 }); 9682 } else { 9683 this.deleteNotEssentialCookie(name); 9684 } 9685 }); 9686 }; 9687 9688 amastyCookieManager.isCookieAllowed = function (cookieName) { 9689 const allowedGroups = hyva.getCookie('amcookie_allowed'); 9690 const disallowedCookie = this.getAmastyCookieGroupStorage()?.getDisallowedCookies() ?? []; 9691 const isCookiePolicyAllowed = hyva.getCookie('amcookie_policy_restriction') === 'allowed'; 9692 const isCookieEssential = this.getAmastyCookieGroupStorage()?.isCookieEssential(cookieName); 9693 const isCookieEssentialByPattern = this.getAmastyCookieGroupStorage()?.isEssentialByPattern(cookieName, this.pattern); 9694 9695 if (!isCookiePolicyAllowed || isCookieEssential || isCookieEssentialByPattern) { 9696 return true; 9697 } 9698 9699 return !((!allowedGroups && !disallowedCookie) || this.isCookieDisallowed(cookieName)); 9700 }; 9701 9702 amastyCookieManager.isCookieDisallowed = function (cookieName) { 9703 const amastyCookieGroupStorage = this.getAmastyCookieGroupStorage(); 9704 const disallowedCookies = amastyCookieGroupStorage?.getDisallowedCookies() ?? []; 9705 9706 return disallowedCookies.some((cookie) => { 9707 const pattern = cookie.match(this.pattern); 9708 9709 return !!pattern && !!cookieName.match(amastyCookieGroupStorage?.getCookiePattern(pattern.groups.cookiePattern)); 9710 }) || disallowedCookies.indexOf(cookieName) !== -1; 9711 }; 9712 9713 amastyCookieManager.processManageableCookies = function () { 9714 let cookieScripts = document.querySelectorAll('script[data-amcookie-groupid][type="text/plain"]'); 9715 9716 cookieScripts.forEach((elem) => { 9717 if (this.isChecked(elem.dataset['amcookie-groupid'])) { 9718 let elClone = elem.cloneNode(true).setAttribute('type', 'text/javascript'); 9719 9720 elem.remove(); 9721 document.body.appendChild(elClone); 9722 } 9723 }); 9724 }; 9725 9726 amastyCookieManager.deleteNotEssentialCookie = function (cookieName) { 9727 if (!this.getAmastyCookieGroupStorage()?.isCookieEssential(cookieName)) { 9728 hyva.setCookie(cookieName, '', -1, true); 9729 } 9730 }; 9731 9732 amastyCookieManager.setLastCookieAcceptance = function (lastUpdate) { 9733 localStorage.setItem('am-last-cookie-acceptance', lastUpdate); 9734 }; 9735 9736 window.addEventListener('amasty-cookie-group-updated', () => { 9737 amastyCookieManager.deleteDisallowedCookie(); 9738 }); 9739 9740 window.addEventListener('private-content-loaded', () => { 9741 amastyCookieManager.getAmastyCookieGroupStorage()?.hasGroupData() && amastyCookieManager.deleteDisallowedCookie(); 9742 }); 9743 }(window.amastyCookieManager = window.amastyCookieManager || {})); 9744</script>
vendor: 1 bytes, line 9744
9744
9745<script> 9746 (function () { 9747 'use strict'; 9748 9749 window.addEventListener('cookiebar-action-accept', (event) => { 9750 const groups = event.detail; 9751 const url = `${BASE_URL}amcookie/cookie/savegroups`; 9752 const contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 9753 const body = new URLSearchParams({ 9754 form_key: hyva.getFormKey() 9755 }); 9756 groups.length && groups.map(groupId => body.append('groups[]', groupId)); 9757 9758 fetch(url, {method: 'POST', body, headers: {'Content-Type': contentType}}) 9759 .then(response => { 9760 if (response.ok) { 9761 return response.json(); 9762 } 9763 9764 return Promise.reject(`${response.status} ${response.statusText}`); 9765 }) 9766 .then(data => { 9767 if (data.success) { 9768 !!hyva.amStorage && hyva.amStorage.getStorage('AmastyCookieGroups').updateGroupData(true); 9769 } 9770 }) 9771 .catch(error => { 9772 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 9773 { 9774 type: "error", 9775 text: error 9776 } 9777 ]); 9778 }) 9779 .finally(() => { 9780 amastyCookieManager.processManageableCookies(); 9781 window.dispatchEvent(new CustomEvent('cookiebar-action-accept-finish')); 9782 window.dispatchEvent(new CustomEvent('amasty-cookie-group-updated')); 9783 // Wait cookiebar close animation 9784 setTimeout(() => { 9785 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 9786 }, 100); 9787 }); 9788 }); 9789 })(); 9790</script>
vendor: 1 bytes, line 9790
9790
9791<script> 9792 (function () { 9793 'use strict'; 9794 9795 window.addEventListener('cookiebar-action-allow', () => { 9796 const url = `${BASE_URL}amcookie/cookie/allow`; 9797 const contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 9798 const body = new URLSearchParams({ 9799 form_key: hyva.getFormKey() 9800 }); 9801 fetch(url, {method: 'POST', body, headers: {'Content-Type': contentType}}) 9802 .then(response => { 9803 if (response.ok) { 9804 return response; 9805 } 9806 9807 return Promise.reject(`${response.status} ${response.statusText}`); 9808 }) 9809 .then(() => { 9810 !!hyva.amStorage && hyva.amStorage.getStorage('AmastyCookieGroups').updateGroupData(true); 9811 }) 9812 .catch(error => { 9813 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages([ 9814 { 9815 type: "error", 9816 text: error 9817 } 9818 ]); 9819 }) 9820 .finally(() => { 9821 amastyCookieManager.processManageableCookies(); 9822 window.dispatchEvent(new CustomEvent('cookiebar-action-allow-finish')); 9823 // Wait cookiebar close animation 9824 setTimeout(() => { 9825 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 9826 }, 100); 9827 }); 9828 }); 9829 })(); 9830</script>
9830 9831 9832<style> 9833 :root { 9834 /* Bar Color Settings */ 9835 --ambar-background: #fff; 9836 --ambar-links-color: #326ed1; 9837 --ambar-policy-text: #374151; 9838 --ambar-toggle-active-color: #2563eb; 9839 --ambar-toggle-inactive-color: #fff; 9840 9841 /* Buttons Color Settings */ 9842 --ambar-button-accept-color: #337ab7; 9843 --ambar-button-accept-hover-color: #337ab7; 9844 --ambar-button-accept-text: #fff; 9845 --ambar-button-accept-hover-text: #fff; 9846 9847 --ambar-button-allow-color: #337ab7; 9848 --ambar-button-allow-hover-color: #337ab7; 9849 --ambar-button-allow-text: #fff; 9850 --ambar-button-allow-hover-text: #fff; 9851 9852 --ambar-button-decline-color: #337ab7; 9853 --ambar-button-decline-hover-color: #337ab7; 9854 --ambar-button-decline-text: #fff; 9855 --ambar-button-decline-hover-text: #fff; 9856 9857 --ambar-button-settings-color: #337ab7; 9858 --ambar-button-settings-hover-color: #337ab7; 9859 --ambar-button-settings-text: #fff; 9860 --ambar-button-settings-hover-text: #fff; 9861 9862 /* Buttons Order Settings */ 9863 --ambar-button-accept-order: 0; 9864 --ambar-button-allow-order: 0; 9865 --ambar-button-settings-order: 0; 9866 --ambar-button-decline-order: 0; 9867 9868 /* Cookie Information Bar Color Settings */ 9869 --ambar-information-background: #fff; 9870 --ambar-information-title: #374151; 9871 --ambar-information-description: #374151; 9872 --ambar-information-table-header: #374151; 9873 --ambar-information-table-content: #374151; 9874 9875 /* Cookie Information Bar Done Button Settings */ 9876 --ambar-information-button-color: #1d4ed8; 9877 --ambar-information-button-hover-color: #1d4ed8; 9878 --ambar-information-button-text: #fff; 9879 --ambar-information-button-hover-text: #fff; 9880 9881 /* Settings Bar Done Button Settings */ 9882 --ambar-setting-button-color: #1d4ed8; 9883 --ambar-setting-button-hover-color: #1d4ed8; 9884 --ambar-setting-button-text: #fff; 9885 --ambar-setting-button-hover-text: #fff; 9886 } 9887</style> 9888 9889<section x-data="{isLoading: false}" 9890 @amgdpr-loading-start.window="isLoading = true" @amgdpr-loading-stop.window="isLoading = false"> 9891 <div class="flex flex-row justify-center items-center w-full h-full fixed select-none z-50" 9892 style="left: 50%;top: 50%;transform: translateX(-50%) translateY(-50%);background: rgba(255,255,255,0.7);" 9893 x-show="isLoading" 9894 x-cloak 9895 x-transition:enter="ease-out duration-200" 9896 x-transition:enter-start="opacity-0" 9897 x-transition:enter-end="opacity-100" 9898 x-transition:leave="ease-in duration-200" 9899 x-transition:leave-start="opacity-100" 9900 x-transition:leave-end="opacity-0"> 9901 <!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL --> 9902<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57 57" stroke="currentColor" class="text-primary" width="57" height="57" role="img"> 9903 <g fill="none" fill-rule="evenodd"> 9904 <g transform="translate(1 1)" stroke-width="2"> 9905 <circle cx="5" cy="50" r="5"> 9906 <animate attributeName="cy" begin="0s" dur="2.2s" values="50;5;50;50" calcMode="linear" repeatCount="indefinite"/> 9907 <animate attributeName="cx" begin="0s" dur="2.2s" values="5;27;49;5" calcMode="linear" repeatCount="indefinite"/> 9908 </circle> 9909 <circle cx="27" cy="5" r="5"> 9910 <animate attributeName="cy" begin="0s" dur="2.2s" from="5" to="5" values="5;50;50;5" calcMode="linear" repeatCount="indefinite"/> 9911 <animate attributeName="cx" begin="0s" dur="2.2s" from="27" to="27" values="27;49;5;27" calcMode="linear" repeatCount="indefinite"/> 9912 </circle> 9913 <circle cx="49" cy="50" r="5"> 9914 <animate attributeName="cy" begin="0s" dur="2.2s" values="50;50;5;50" calcMode="linear" repeatCount="indefinite"/> 9915 <animate attributeName="cx" from="49" to="49" begin="0s" dur="2.2s" values="49;5;27;49" calcMode="linear" repeatCount="indefinite"/> 9916 </circle> 9917 </g> 9918 </g> 9919<title>loader</title></svg> 9920 <div class="ml-10 text-primary text-xl"> 9921 Loading... </div> 9922</div> 9923</section> 9924
9925<script> 9926 'use strict'; 9927 9928 function initParentContainerProcessor() { 9929 const selectors = { 9930 labelContainer: '.amasty-label-container', 9931 galleryImg: '#gallery img:not(.amasty-label-image)' 9932 } 9933 9934 return { 9935 /** 9936 * @param {AmlabelComponent} label 9937 * @return {void} 9938 */ 9939 process: function (label) { 9940 this.moveLabelToParent(label); 9941 this.setParentStyles(label); 9942 }, 9943 9944 /** 9945 * @param {AmlabelComponent} label 9946 * @return {void} 9947 */ 9948 moveLabelToParent: function (label) { 9949 const newParent = this.getNewParent(label); 9950 9951 if (!newParent) { 9952 return; 9953 } 9954 9955 const wrapperElement = newParent.querySelector(label.wrapperClass); 9956 label.parent = newParent; 9957 9958 // if same wrapper already exists - move label there 9959 if (wrapperElement && !wrapperElement.isSameNode(label.element)) { 9960 this.insertLabelByPriority(label, wrapperElement); 9961 } else { 9962 newParent.prepend(label.element); 9963 } 9964 }, 9965 9966 /** 9967 * @param {AmlabelComponent} label 9968 * @return {HTMLElement|null} 9969 */ 9970 getNewParent: function (label) { 9971 // move label to container from settings 9972 let newParent = label.element.closest(label.config.path) 9973 || this.findPreviousNodeExcludingScriptsAndDifferentLabels(label.element)?.querySelector(label.config.path) 9974 || label.parent.querySelector(label.config.path); 9975 9976 // Try to find default gallery container on product page if none of the config selectors found 9977 if (!newParent && label.config.move !== 1 && label.config.mode === 'prod') { 9978 newParent = document.querySelector(selectors.galleryImg)?.parentElement; 9979 } 9980 9981 return newParent; 9982 }, 9983 9984 /** 9985 * @param {HTMLElement} element 9986 * @return {HTMLElement|null} 9987 */ 9988 findPreviousNodeExcludingScriptsAndDifferentLabels: function (element) { 9989 const previousElement = element?.previousElementSibling; 9990 if (!previousElement) { 9991 return null; 9992 } 9993 9994 if (previousElement?.tagName !== 'SCRIPT' 9995 && !previousElement?.className?.includes?.('amlabel-position-wrapper') 9996 ) { 9997 return previousElement; 9998 } 9999 10000 return this.findPreviousNodeExcludingScriptsAndDifferentLabels(previousElement); 10001 }, 10002 10003 /** 10004 * @param {AmlabelComponent} label 10005 * @param {HTMLElement} wrapperElement 10006 * @return {void} 10007 */ 10008 insertLabelByPriority: function (label, wrapperElement) { 10009 const labelContainers = wrapperElement.querySelectorAll(selectors.labelContainer); 10010 const currentLabelOrder = label.labelContainer.dataset.amlabelOrder; 10011 let insertAfterContainer = null; 10012 10013 // find the label behind which we want to insert current one by priority 10014 labelContainers.forEach(labelContainer => { 10015 if (labelContainer.dataset.amlabelOrder < currentLabelOrder) { 10016 insertAfterContainer = labelContainer; 10017 } 10018 }); 10019 10020 insertAfterContainer === null 10021 ? wrapperElement.prepend(label.labelContainer) 10022 : insertAfterContainer.after(label.labelContainer); 10023 10024 label.element.remove(); 10025 label.element = wrapperElement; 10026 }, 10027 10028 /** 10029 * @param {AmlabelComponent} label 10030 * @return {void} 10031 */ 10032 setParentStyles: function (label) { 10033 /* required for child position absolute */ 10034 !(label.parent.style.position === 'absolute') && (label.parent.style.position = 'relative'); 10035 !label.parent.style.width && label.parent.classList.add('w-fit-content'); 10036 !label.parent.style.marginLeft && label.parent.classList.add('ml-auto'); 10037 !label.parent.style.marginRight && label.parent.classList.add('mr-auto'); 10038 }, 10039 } 10040 } 10041</script>
vendor: 1 bytes, line 10041
10041
10042<script> 10043 'use strict'; 10044 10045 function initLabelItemProcessor() { 10046 /** 10047 * @typedef {Object} AmlabelComponent 10048 * 10049 * @property {HTMLElement} element Label wrapper element 10050 * @property {HTMLElement} parent Element where label should be placed 10051 * @property {boolean} initialized Is label initialized 10052 * @property {string} wrapperClass Dynamic class for label wrapper to place multiple labels properly by position 10053 * @property {HTMLElement} labelContainer Container for label text image and tooltip 10054 * @property {Config} config Label Configuration 10055 * @property {Object} parentContainerProcessor 10056 * @property {Object} labelItemProcessor 10057 */ 10058 10059 /** 10060 * @typedef {Object} AmLabelConfig 10061 * 10062 * @property {0|1} alignment Labels Alignment config value (0 - Horizontal| 1 - Vertical) 10063 * @property {number} label Label Id 10064 * @property {number} margin Margin between labels config value 10065 * @property {('cat'|'prod')} mode Label mode 10066 * @property {number} move 10067 * @property {number} order Label order 10068 * @property {string} path The css selector to which label should place 10069 * @property {string} position Label position 10070 * @property {string} product Product Id 10071 * @property {string} redirect_url 10072 * @property {string} size Label size 10073 * @property {{ 10074 * backgroundColor: string, 10075 * color: string, 10076 * content: string, 10077 * status: 1|2|3 10078 * }} tooltip 10079 */ 10080 10081 return { 10082 /** 10083 * @param {AmlabelComponent} label 10084 * @return {void} 10085 */ 10086 process: function(label) { 10087 this.setLabelSize(label); 10088 this.setLinkProps(label); 10089 }, 10090 10091 /** 10092 * @param {AmlabelComponent} label 10093 * @return {void} 10094 */ 10095 setLabelSize: function (label) { 10096 if (!label.config.size) { 10097 return; 10098 } 10099 10100 label.labelContainer.style.width = label.parent.offsetWidth * label.config.size / 100 + 'px'; 10101 }, 10102 10103 /** 10104 * 10105 * @param {Object} $event 10106 * @param {HTMLElement} tooltipNode 10107 */ 10108 placeTooltip: function($event, tooltipNode) { 10109 const labelNode = $event.target; 10110 const labelNodeRect = labelNode.getBoundingClientRect(); 10111 10112 tooltipNode.style.bottom = (window.innerHeight - labelNodeRect.top) + 'px'; 10113 tooltipNode.style.left = (labelNodeRect.left + labelNode.offsetWidth / 2 ) + 'px'; 10114 }, 10115 10116 /** 10117 * @param {AmlabelComponent} label 10118 * @return {void} 10119 */ 10120 setLinkProps: function (label) { 10121 !!label.config.redirect_url && label.labelContainer.classList.add('-link', 'cursor-pointer'); 10122 }, 10123 10124 /** 10125 * @param {Object} $event Click event 10126 * @param {string} redirectUrl 10127 */ 10128 openLink: function($event, redirectUrl) { 10129 if (redirectUrl.length === 0) { 10130 return; 10131 } 10132 10133 $event.preventDefault(); 10134 window.open(redirectUrl, '_blank'); 10135 } 10136 } 10137 } 10138</script>
10138 10139
10140<script> 10141 (function () { 10142 'use strict'; 10143 10144 const observedEvent = 'amlabel-swatch-product-changed'; 10145 const selectors = { 10146 swatchOptions: 'div[x-data^=initConfigurable][x-data*=Options]', 10147 addToCartFormOnListing: 'form.product_addtocart_form', 10148 galleryImage: '#gallery img:not(.amasty-label-image)', 10149 labelWrapper: '.amlabel-position-wrapper' 10150 } 10151 10152 /** 10153 * @return {void} 10154 */ 10155 const injectLabelObserver = function () { 10156 const swatches = document.querySelectorAll(selectors.swatchOptions); 10157 10158 swatches.forEach(swatch => { 10159 const isListing = !!swatch.closest(selectors.addToCartFormOnListing); 10160 const injectedDispatch = `$dispatch('${observedEvent}', ` 10161 + `{productIndex: productIndex, element: $el, optionConfig: optionConfig, isListing: ${isListing ? 1 : 0}})`; 10162 10163 let mutatedInitAttribute = swatch.getAttribute('x-init'); 10164 mutatedInitAttribute += `; $watch('productIndex', (productIndex) => { ${injectedDispatch} });`; 10165 swatch.setAttribute('x-init', mutatedInitAttribute); 10166 }); 10167 } 10168 10169 /** 10170 * @param {{ 10171 * productIndex: number, 10172 * element: HTMLElement, 10173 * optionConfig: Object, 10174 * isListing: 0|1 10175 * }} payload 10176 * @return {void} 10177 */ 10178 const updateLabel = function ({detail: payload}) { 10179 const {productIndex, element, optionConfig, isListing} = payload; 10180 10181 if (!productIndex) { 10182 return; 10183 } 10184 10185 const reloadUrl = optionConfig.label_reload; 10186 const headers = { 10187 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 10188 }; 10189 const body = new URLSearchParams({ 10190 product_ids: productIndex, 10191 in_product_list: isListing 10192 }); 10193 const labelContainerSelector = isListing ? optionConfig.label_category : optionConfig.label_product; 10194 10195 fetch(`${reloadUrl}?${body.toString()}`, {headers: headers, method: 'GET'}).then((response) => { 10196 if (response.ok) { 10197 return response.json(); 10198 } 10199 }).then((result) => { 10200 const labelHtml = result.labels && result.labels[productIndex]; 10201 let labelContainer = findClosest(element, labelContainerSelector); 10202 10203 // Try to find default gallery container on product page if none of the config selectors found 10204 if (labelContainer === null && isListing === 0) { 10205 labelContainer = document.querySelector(selectors.galleryImage)?.parentElement; 10206 } 10207 10208 if (!!labelContainer) { 10209 !!labelHtml ? replaceLabels(labelContainer, labelHtml) : clearLabelWrapper(labelContainer); 10210 } 10211 }).catch((error) => { 10212 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 10213 [{ 10214 type: 'error', 10215 text: error 10216 }], 10217 5000 10218 ); 10219 }); 10220 } 10221 10222 /** 10223 * We use approach from hyva.replaceDomElement method to replace labels and init components 10224 * 10225 * @param {HTMLElement} labelContainer 10226 * @param {string} labelHtml 10227 * @return {void} 10228 */ 10229 function replaceLabels(labelContainer, labelHtml) { 10230 const parser = new DOMParser(); 10231 const doc = parser.parseFromString(labelHtml, 'text/html'); 10232 const nodes = doc.querySelectorAll(selectors.labelWrapper); 10233 10234 if (!nodes) { 10235 return; 10236 } 10237 10238 clearLabelWrapper(labelContainer); 10239 10240 // Extract all the script tags from the new content. 10241 // Script tags won't execute when inserted into a dom-element directly, 10242 // therefore we need to inject them to the head of the document. 10243 const tmpScripts = doc.getElementsByTagName('script'); 10244 10245 if (tmpScripts.length > 0) { 10246 // Push all script tags into an array 10247 // (to prevent dom manipulation while iterating over dom nodes) 10248 const scripts = [...tmpScripts]; 10249 10250 // Iterate over all script tags and duplicate+inject each into the head
10251 scripts.forEach(script => { 10252 let scriptNode = document.createElement('script'); 10253 scriptNode.innerHTML = script.innerHTML; 10254 document.head.appendChild(scriptNode); 10255 10256 // Remove the original (non-executing) node from the page 10257 script.parentNode.removeChild(script); 10258 }); 10259 } 10260 10261 labelContainer.append(...nodes); 10262 } 10263 10264 /** 10265 * @param {HTMLElement} labelContainer 10266 * @return {void} 10267 */ 10268 function clearLabelWrapper(labelContainer) { 10269 labelContainer.querySelectorAll(selectors.labelWrapper).forEach(wrapper => wrapper.remove()); 10270 } 10271 10272 /** 10273 * @param {HTMLElement} currentElement 10274 * @param {string} selector 10275 * @return {HTMLElement|null} 10276 */ 10277 function findClosest(currentElement, selector) { 10278 // We need to limit parent searches to prevent labels from being associated with the wrong product. 10279 const maxDepth = 3; 10280 let counter = 0; 10281 let closest = null; 10282 10283 while (currentElement.parentNode && closest === null && counter < maxDepth) { 10284 ++counter; 10285 currentElement = currentElement.parentNode; 10286 closest = currentElement.querySelector(selector); 10287 } 10288 10289 return closest; 10290 } 10291 10292 injectLabelObserver(); 10293 window.addEventListener(observedEvent, updateLabel); 10294 })(); 10295</script>
vendor: 1 bytes, line 10295
10295
10296<script> 10297 (function () { 10298 'use strict'; 10299 10300 const selectors = { 10301 body: '.catalog-product-view', 10302 productItem: 'product-item' 10303 } 10304 10305 window.addEventListener('alpine:init', () => { 10306 Alpine.directive('amintersect', (el, { expression }, { evaluate, cleanup }) => { 10307 const observer = new IntersectionObserver(entries => { 10308 entries.forEach(entry => { 10309 if (entry.isIntersecting) { 10310 evaluate(expression); 10311 observer.disconnect(); 10312 } 10313 }) 10314 }, { threshold: 0.5 }); 10315 10316 observer.observe(getElement()); 10317 10318 cleanup(() => { 10319 observer.disconnect() 10320 }); 10321 10322 function getElement() { 10323 return !!document.querySelector(selectors.body) ? el.parentNode : getCategoryProductBlock(el); 10324 } 10325 10326 function getCategoryProductBlock(el) { 10327 if (el.previousElementSibling === null) { 10328 return el; 10329 } 10330 10331 if (!el.previousElementSibling.classList.contains(selectors.productItem)) { 10332 return getCategoryProductBlock(el.previousElementSibling); 10333 } 10334 10335 return el.previousElementSibling; 10336 } 10337 }); 10338 }); 10339 })(); 10340</script>
vendor: 1 bytes, line 10340
10340
10341<script type="application/ld+json">{"@context":"https:\/\/schema.org","@type":"WebSite","url":"https:\/\/www.studio-88.co.za\/","name":"Studio 88","potentialAction":{"@type":"SearchAction","target":"https:\/\/www.studio-88.co.za\/catalogsearch\/result\/?q={search_term_string}","query-input":"required name=search_term_string"}}</script>
10341<script type="application/ld+json">{"@context":"https:\/\/schema.org","@type":"Organization","url":"https:\/\/www.studio-88.co.za\/","name":"Studio 88","logo":"https:\/\/www.studio-88.co.za\/pub\/media\/logo\/stores\/1\/Studio_88_Logo.png","description":"Studio 88 is South Africa's largest independent fashion and footwear retail chain. We make it our business to bring you the latest sneakers, footwear and ...","contactPoint":[{"@type":"ContactPoint","telephone":"+27100060888","contactType":"sales"},{"@type":"ContactPoint","telephone":"+27100060888","contactType":"technical support"},{"@type":"ContactPoint","telephone":"+27100060888","contactType":"customer service"}],"address":{"addressCountry":"ZA","postalCode":"2190","addressRegion":"Gauteng","addressLocality":"Johannesburg","streetAddress":"30 O'Connor Pl, Aeroton, Johannesburg South"},"sameAs":["https:\/\/www.facebook.com\/studio88clothing\/","https:\/\/twitter.com\/Studio_88","https:\/\/www.instagram.com\/studio88_branded\/","https:\/\/www.youtube.com\/channel\/UCDE-aOZuKGLQzPm-2xYvyZg","https:\/\/za.linkedin.com\/company\/studio-88","https:\/\/soundcloud.com\/mix-on-the-move"]}</script>
10341<script type="application/json" id="default-section-data"> 10342 {"messages":[],"customer":[],"compare-products":[],"last-ordered-items":[],"cart":[],"directory-data":{"ZA":{"name":"South Africa","regions":{"711":{"code":"EC","name":"Eastern Cape"},"712":{"code":"FS","name":"Free State"},"713":{"code":"GP","name":"Gauteng"},"714":{"code":"KZN","name":"KwaZulu Natal"},"715":{"code":"LP","name":"Limpopo"},"716":{"code":"MP","name":"Mpumalanga"},"718":{"code":"NW","name":"North West"},"717":{"code":"NC","name":"Northern Cape"},"719":{"code":"WC","name":"Western Cape"}}},"data_id":1790253837},"instant-purchase":[],"loggedAsCustomer":[],"captcha":[],"persistent":[],"review":[],"payments":[],"wishlist":{"items":[]},"amfacebook-pixel":[],"amga4_event_pool":[],"amga4_hyva_event_pool":[],"amasty-storepickup-data":[],"webforms":[],"recently_viewed_product":[],"recently_compared_product":[],"product_data_storage":[],"paypal-billing-agreement":[]}</script>
10342 10343
10344<script> 10345 (() => { 10346 const promotionsObject = { 10347 options: { 10348 observerConfig: { 10349 root: null, 10350 threshold: 1 10351 }, 10352 trackFlagAttribute: 'promo-tracked', 10353 selectPromoEvent: 'select_promotion', 10354 viewPromoEvent: 'view_promotion' 10355 }, 10356 selectors: { 10357 container: '[data-promotion-event]', 10358 }, 10359 dataAttributes: { 10360 creativeName: 'promotionCreativeName', 10361 creativeSlot: 'promotionCreativeSlot', 10362 promotionId: 'promotionId', 10363 promotionName: 'promotionName', 10364 promotionItems: 'promotionItems' 10365 }, 10366 10367 trackPromoNodes() { 10368 const untrackedPromoNodes = this.getUntrackedPromoNodes(); 10369 untrackedPromoNodes.forEach((node) => { 10370 node.addEventListener('click', (event) => { 10371 this.trackSelectPromoEvent(event); 10372 }); 10373 this.createIntersectionObserver(node); 10374 }); 10375 this.markPromoNodesAsTracked(untrackedPromoNodes); 10376 }, 10377 10378 handleViewPromotion(event) { 10379 const element = event.detail?.element; 10380 if (element) { 10381 this.pushEventInDataLayer(element, this.options.viewPromoEvent); 10382 } 10383 }, 10384 10385 trackSelectPromoEvent(event) { 10386 const target = event.target.closest(this.selectors.container); 10387 if (!target) { 10388 return; 10389 } 10390 this.pushEventInDataLayer(target, this.options.selectPromoEvent); 10391 }, 10392 10393 getUntrackedPromoNodes() { 10394 return Array.from(document.querySelectorAll(`${this.selectors.container}:not([${this.options.trackFlagAttribute}])`)); 10395 }, 10396 10397 markPromoNodesAsTracked(nodes) { 10398 nodes.forEach((node) => { 10399 node.setAttribute(this.options.trackFlagAttribute, 'true'); 10400 }); 10401 }, 10402 10403 createIntersectionObserver(target) { 10404 const observer = new IntersectionObserver((entries) => { 10405 entries.forEach((entry) => { 10406 if (!entry.isIntersecting) return; 10407 10408 this.pushEventInDataLayer(entry.target, this.options.viewPromoEvent); 10409 observer.disconnect(); 10410 }); 10411 }, this.options.observerConfig); 10412 observer.observe(target); 10413 }, 10414 10415 pushEventInDataLayer(element, eventName) { 10416 const container = element.closest(this.selectors.container); 10417 if (!container) return; 10418 const promotionData = { 10419 creative_name: container.dataset[this.dataAttributes.creativeName], 10420 creative_slot: container.dataset[this.dataAttributes.creativeSlot], 10421 promotion_id: container.dataset[this.dataAttributes.promotionId], 10422 promotion_name: container.dataset[this.dataAttributes.promotionName], 10423 }; 10424 const promotionItems = container.dataset[this.dataAttributes.promotionItems]; 10425 if (promotionItems) { 10426 promotionData.items = JSON.parse(promotionItems); 10427 } 10428 window.dataLayer = window.dataLayer || []; 10429 window.dataLayer.push({ ecommerce: null }); 10430 window.dataLayer.push(this.getPromotionObject(promotionData, eventName)); 10431 }, 10432 10433 getPromotionObject(promotionData, eventName) { 10434 return { 10435 event: eventName, 10436 ecommerce: { ...promotionData }, 10437 }; 10438 } 10439 } 10440 10441 window.addEventListener('init-external-scripts', () => { 10442 promotionsObject.trackPromoNodes(); 10443 }, { once: true, passive: true }); 10444 })(); 10445</script>
10445 10446</div></body> 10447</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.