1<!doctype html> 2<html lang="en"> 3<head > 4 <meta charset="utf-8"/> 5<meta name="title" content="Yorkville Cakes and Cookies | Dessert Lady Cafe and Bakery"/> 6<meta name="description" content="Cakes, our 3-layer Strawberry Shortcake, branded cookies, and a cafe at 1 Sultan Street in Yorkville, Toronto. Order online for pickup."/> 7<meta name="robots" content="INDEX,FOLLOW"/> 8<meta name="viewport" content="width=device-width, initial-scale=1"/> 9<title>Yorkville Cakes and Cookies | Dessert Lady Cafe and Bakery</title> 10<link rel="stylesheet" type="text/css" media="all" href="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/css/styles.css" /> 11<link rel="stylesheet" type="text/css" media="all" href="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/css/custom.css" /> 12<link rel="stylesheet" type="text/css" media="all" href="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_NoraAi/css/chatbot-widget.css" /> 13<link rel="stylesheet" type="text/css" media="all" href="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_ReactCheckoutPro/css/minicart-checkout.css" />
14<script type="text/javascript" src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_ReactCheckoutPro/js/rcp-auth-redirect.js"></script>
vendor: 1 bytes, line 14
14
15<script type="text/javascript" defer="defer" src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_BannerSliderHyva/js/banner-slider.js"></script>
vendor: 1 bytes, line 15
15
16<script type="text/javascript" defer="defer" src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_BannerSliderHyva/js/banner-analytic.js"></script>
16 17<link rel="icon" type="image/x-icon" href="https://dessertlady.ca/media/favicon/stores/1/favicon.png" /> 18<link rel="shortcut icon" type="image/x-icon" href="https://dessertlady.ca/media/favicon/stores/1/favicon.png" /> 19<style> 20 .home-yellow-box { 21 padding-top: 80px; 22 background: #fd9b1b; 23 padding-bottom: 80px; 24} 25.slide-text.middle { 26 text-align: center; 27} 28.slide-text.middle_left { 29 text-align: center; 30} 31.owl-nav { 32 display: none; 33} 34.cwsBestSeller { 35 display: none; 36} 37.col-des-3 { 38 float: left; 39} 40.panel-block-row.panel-block.col-des-12.block356.why-choose-block { 41 padding-top: 61px !important; 42 padding-bottom: 60px !important; 43 background-color: #fd9b1b; 44} 45.container1 { 46 position: relative; 47 width: 50%; 48 max-width: 300px; 49 overflow:hidden; 50} 51 52 .image { 53 display: block; 54 width: 100%; 55 height: auto; 56transition: 0.5s all; 57} 58 59.overlay { 60 bottom: 0; 61 background: rgb(0, 0, 0); 62 background: rgba(0, 0, 0, 0.5); / Black see-through / 63 color: #f1f1f1; 64 width: 100%; 65 transition: .5s ease; 66 opacity:1; 67 color: white; 68 font-size: 20px; 69 padding: 20px; 70 text-align: center; 71} 72.overlay a{ 73text-decoration:none; 74color:#fff; 75} 76.image:hover{ 77 opacity:0.5; 78} 79.section-builder .banner-slider { 80 min-height: 697px; 81} 82 83.header-area .line.align-items-center { 84 min-height: 48px; 85} 86.checkout-index-index .loading-mask { 87 display: none !important; 88} 89 90.cms-home-page .home-points { padding-left: 4%; padding-right: 4%; } 91 92.footer.content .bg-zinc-100 .container.py-8.mx-auto>.grid { padding-left: 4%; padding-right: 4%; } 93 94</style> 95 <!--BB3dD58niLmz01ck87V5v5IR8uPYIeKy--> 96
96<script type="text/javascript"> window.dataLayer = window.dataLayer || []; window.getAmGa4Cookie = function () { let cookies = {}; document.cookie.split(';').forEach(function (el) { let [key, value] = el.split('='); cookies[key.trim()] = value; }); return cookies['amga4_customer'] ? decodeURIComponent(cookies['amga4_customer']) : '{}'; }; (function () { /** * @var {[Object]} */ const userProperties = JSON.parse(window.getAmGa4Cookie()); if (userProperties.customerGroup) { window.dataLayer.push({'customerGroup': userProperties.customerGroup}); } if (userProperties.customerId) { window.dataLayer.push({'customerId': userProperties.customerId}); } /** * @var {[Object]} */ const dataToPush = JSON.parse('\u005B\u005D'); dataToPush.forEach((dataObject) => { window.dataLayer.push(dataObject); }); })();</script>
96 <!-- Google Tag Manager -->
96<script type="text/javascript"> function callAmGA4GTM () { (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]'); n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer', 'GTM-K57SMFBL'); }</script>
96 <!-- End Google Tag Manager -->
96<script type="text/javascript"> (function () { callAmGA4GTM(); })();</script>
96
96<script id="ze-snippet" src="https://static.zdassets.com/ekr/snippet.js?key=6f720270-1048-4c9a-99db-85ee185b4e61"> </script>
96
96<script> 97 var BASE_URL = 'https://dessertlady.ca/'; 98 var THEME_PATH = 'https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA'; 99 var COOKIE_CONFIG = { 100 "expires": null, 101 "path": "\u002F", 102 "domain": ".dessertlady.ca", 103 "secure": true, 104 "lifetime": "3600", 105 "cookie_restriction_enabled": false }; 106 var CURRENT_STORE_CODE = 'default'; 107 var CURRENT_WEBSITE_ID = '1'; 108 109 window.hyva = window.hyva || {} 110 111 window.cookie_consent_groups = window.cookie_consent_groups || {} 112 window.cookie_consent_groups['necessary'] = true; 113 114 window.cookie_consent_config = window.cookie_consent_config || {}; 115 window.cookie_consent_config['necessary'] = [].concat( 116 window.cookie_consent_config['necessary'] || [], 117 [ 118 'user_allowed_save_cookie', 119 'form_key', 120 'mage-messages', 121 'private_content_version', 122 'mage-cache-sessid', 123 'last_visited_store', 124 'section_data_ids' 125 ] 126 );</script>
126
126<script> 127 (function( hyva, undefined ) { 128 129 function lifetimeToExpires(options, defaults) { 130 131 const lifetime = options.lifetime || defaults.lifetime; 132 133 if (lifetime) { 134 const date = new Date; 135 date.setTime(date.getTime() + lifetime * 1000); 136 return date; 137 } 138 139 return null; 140 } 141 142 function generateRandomString() { 143 144 const allowedCharacters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 145 length = 16; 146 147 let formKey = '', 148 charactersLength = allowedCharacters.length; 149 150 for (let i = 0; i < length; i++) { 151 formKey += allowedCharacters[Math.round(Math.random() * (charactersLength - 1))] 152 } 153 154 return formKey; 155 } 156 157 const sessionCookieMarker = {noLifetime: true} 158 159 const cookieTempStorage = {}; 160 161 const internalCookie = { 162 get(name) { 163 const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); 164 return v ? v[2] : null; 165 }, 166 set(name, value, days, skipSetDomain) { 167 let expires, 168 path, 169 domain, 170 secure, 171 samesite; 172 173 const defaultCookieConfig = { 174 expires: null, 175 path: '/', 176 domain: null, 177 secure: false, 178 lifetime: null, 179 samesite: 'lax' 180 }; 181 182 const cookieConfig = window.COOKIE_CONFIG || {}; 183 184 expires = days && days !== sessionCookieMarker 185 ? lifetimeToExpires({lifetime: 24 * 60 * 60 * days, expires: null}, defaultCookieConfig) 186 : lifetimeToExpires(window.COOKIE_CONFIG, defaultCookieConfig) || defaultCookieConfig.expires; 187 188 path = cookieConfig.path || defaultCookieConfig.path; 189 domain = !skipSetDomain && (cookieConfig.domain || defaultCookieConfig.domain); 190 secure = cookieConfig.secure || defaultCookieConfig.secure; 191 samesite = cookieConfig.samesite || defaultCookieConfig.samesite; 192 193 document.cookie = name + "=" + encodeURIComponent(value) + 194 (expires && days !== sessionCookieMarker ? '; expires=' + expires.toGMTString() : '') + 195 (path ? '; path=' + path : '') + 196 (domain ? '; domain=' + domain : '') + 197 (secure ? '; secure' : '') + 198 (samesite ? '; samesite=' + samesite : 'lax'); 199 }, 200 isWebsiteAllowedToSaveCookie() { 201 const allowedCookies = this.get('user_allowed_save_cookie'); 202 if (allowedCookies) { 203 const allowedWebsites = JSON.parse(unescape(allowedCookies)); 204 205 return allowedWebsites[CURRENT_WEBSITE_ID] === 1; 206 } 207 return false; 208 }, 209 getGroupByCookieName(name) { 210 const cookieConsentConfig = window.cookie_consent_config || {}; 211 let group = null; 212 for (let prop in cookieConsentConfig) { 213 if (!cookieConsentConfig.hasOwnProperty(prop)) continue; 214 if (cookieConsentConfig[prop].includes(name)) { 215 group = prop; 216 break; 217 } 218 } 219 return group; 220 }, 221 isCookieAllowed(name) { 222 const cookieGroup = this.getGroupByCookieName(name); 223 return cookieGroup 224 ? window.cookie_consent_groups[cookieGroup] 225 : this.isWebsiteAllowedToSaveCookie(); 226 }, 227 saveTempStorageCookies() { 228 for (const [name, data] of Object.entries(cookieTempStorage)) { 229 if (this.isCookieAllowed(name)) { 230 this.set(name, data['value'], data['days'], data['skipSetDomain']); 231 delete cookieTempStorage[name]; 232 } 233 } 234 } 235 }; 236 237 hyva.getCookie = (name) => { 238 const cookieConfig = window.COOKIE_CONFIG || {}; 239 240 if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) { 241 return cookieTempStorage[name] ? cookieTempStorage[name]['value'] : null; 242 } 243 244 return internalCookie.get(name); 245 } 246 247 hyva.setCookie = (name, value, days, skipSetDomain) => { 248 const cookieConfig = window.COOKIE_CONFIG || {}; 249 250 if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) { 251 cookieTempStorage[name] = {value, days, skipSetDomain}; 252 return; 253 } 254 return internalCookie.set(name, value, days, skipSetDomain); 255 } 256 257 258 hyva.setSessionCookie = (name, value, skipSetDomain) => { 259 return hyva.setCookie(name, value, sessionCookieMarker, skipSetDomain) 260 } 261 262 hyva.getBrowserStorage = () => { 263 const browserStorage = window.localStorage || window.sessionStorage; 264 if (!browserStorage) { 265 console.warn('Browser Storage is unavailable'); 266 return false;
267 } 268 try { 269 browserStorage.setItem('storage_test', '1'); 270 browserStorage.removeItem('storage_test'); 271 } catch (error) { 272 console.warn('Browser Storage is not accessible', error); 273 return false; 274 } 275 return browserStorage; 276 } 277 278 hyva.postForm = (postParams) => { 279 const form = document.createElement("form"); 280 281 let data = postParams.data; 282 283 if (! postParams.skipUenc && ! data.uenc) { 284 data.uenc = btoa(window.location.href); 285 } 286 form.method = "POST"; 287 form.action = postParams.action; 288 289 Object.keys(postParams.data).map(key => { 290 const field = document.createElement("input"); 291 field.type = 'hidden' 292 field.value = postParams.data[key]; 293 field.name = key; 294 form.appendChild(field); 295 }); 296 297 const form_key = document.createElement("input"); 298 form_key.type = 'hidden'; 299 form_key.value = hyva.getFormKey(); 300 form_key.name="form_key"; 301 form.appendChild(form_key); 302 303 document.body.appendChild(form); 304 305 form.submit(); 306 } 307 308 hyva.getFormKey = function () { 309 let formKey = hyva.getCookie('form_key'); 310 311 if (!formKey) { 312 formKey = generateRandomString(); 313 hyva.setCookie('form_key', formKey); 314 } 315 316 return formKey; 317 } 318 319 hyva.formatPrice = (value, showSign, options = {}) => { 320 const groupSeparator = options.groupSeparator; 321 const decimalSeparator = options.decimalSeparator 322 delete options.groupSeparator; 323 delete options.decimalSeparator; 324 const formatter = new Intl.NumberFormat( 325 'en\u002DCA', 326 Object.assign({ 327 style: 'currency', 328 currency: 'CAD', 329 signDisplay: showSign ? 'always' : 'auto' 330 }, options) 331 ); 332 return (typeof Intl.NumberFormat.prototype.formatToParts === 'function') ? 333 formatter.formatToParts(value).map(({type, value}) => { 334 switch (type) { 335 case 'currency': 336 return '\u0024\u0020' || value; 337 case 'minusSign': 338 return '- '; 339 case 'plusSign': 340 return '+ '; 341 case 'group': 342 return groupSeparator !== undefined ? groupSeparator : value; 343 case 'decimal': 344 return decimalSeparator !== undefined ? decimalSeparator : value; 345 default : 346 return value; 347 } 348 }).reduce((string, part) => string + part) : 349 formatter.format(value); 350 } 351 352 const formatStr = function (str, nStart) { 353 const args = Array.from(arguments).slice(2); 354 355 return str.replace(/(%+)([0-9]+)/g, (m, p, n) => { 356 const idx = parseInt(n) - nStart; 357 358 if (args[idx] === null || args[idx] === void 0) { 359 return m; 360 } 361 return p.length % 2 362 ? p.slice(0, -1).replace('%%', '%') + args[idx] 363 : p.replace('%%', '%') + n; 364 }) 365 } 366 367 hyva.str = function (string) { 368 const args = Array.from(arguments); 369 args.splice(1, 0, 1); 370 371 return formatStr.apply(undefined, args); 372 } 373 374 hyva.strf = function () { 375 const args = Array.from(arguments); 376 args.splice(1, 0, 0); 377 378 return formatStr.apply(undefined, args); 379 } 380 381 /** 382 * Take a html string as `content` parameter and 383 * extract an element from the DOM to replace in 384 * the current page under the same selector, 385 * defined by `targetSelector` 386 */ 387 hyva.replaceDomElement = (targetSelector, content) => { 388 389 const parser = new DOMParser(); 390 const doc = parser.parseFromString(content, 'text/html');
391 const contentNode = doc.querySelector(targetSelector); 392 393 394 if (!contentNode || !document.querySelector(targetSelector)) { 395 return; 396 } 397 398 hyva.activateScripts(contentNode); 399 document.querySelector(targetSelector).replaceWith(contentNode); 400 401 402 window.dispatchEvent(new CustomEvent("reload-customer-section-data")); 403 hyva.initMessages(); 404 } 405 406 hyva.removeScripts = (contentNode) => { 407 const scripts = contentNode.getElementsByTagName('script'); 408 for (let i = 0; i < scripts.length; i++) { 409 scripts[i].parentNode.removeChild(scripts[i]); 410 } 411 const templates = contentNode.getElementsByTagName('template'); 412 for (let i = 0; i < templates.length; i++) { 413 const container = document.createElement('div'); 414 container.innerHTML = templates[i].innerHTML; 415 hyva.removeScripts(container); 416 templates[i].innerHTML = container.innerHTML; 417 } 418 } 419 420 hyva.activateScripts = (contentNode) => { 421 const scripts = Array.from(contentNode.getElementsByTagName('script')); 422 423 424 for (const original of scripts) { 425 const script = document.createElement('script'); 426 original.type && (script.type = original.type); 427 script.innerHTML = original.innerHTML; 428 429 430 original.parentNode.removeChild(original) 431 432 433 document.head.appendChild(script); 434 } 435 436 return contentNode; 437 } 438 439 const replace = {['+']: '-', ['/']: '_', ['=']: ','}; 440 hyva.getUenc = () => btoa(window.location.href).replace(/[+/=]/g, match => replace[match]); 441 442 let currentTrap; 443 444 const focusableElements = (rootElement) => { 445 const selector = 'button, [href], input, select, textarea, details, [tabindex]:not([tabindex="-1"]'; 446 return Array.from(rootElement.querySelectorAll(selector)) 447 .filter(el => { 448 return el.style.display !== 'none' 449 && !el.disabled 450 && el.tabIndex !== -1 451 && (el.offsetWidth || el.offsetHeight || el.getClientRects().length) 452 }) 453 } 454 455 const focusTrap = (e) => { 456 const isTabPressed = e.key === 'Tab' || e.keyCode === 9; 457 if (!isTabPressed) return; 458 459 const focusable = focusableElements(currentTrap) 460 const firstFocusableElement = focusable[0] 461 const lastFocusableElement = focusable[focusable.length - 1] 462 463 e.shiftKey 464 ? document.activeElement === firstFocusableElement && (lastFocusableElement.focus(), e.preventDefault()) 465 : document.activeElement === lastFocusableElement && (firstFocusableElement.focus(), e.preventDefault()) 466 }; 467 468 hyva.releaseFocus = (rootElement) => { 469 if (currentTrap && (!rootElement || rootElement === currentTrap)) { 470 currentTrap.removeEventListener('keydown', focusTrap) 471 currentTrap = null 472 } 473 } 474 hyva.trapFocus = (rootElement) => { 475 if (!rootElement) return; 476 hyva.releaseFocus() 477 currentTrap = rootElement 478 rootElement.addEventListener('keydown', focusTrap) 479 const firstElement = focusableElements(rootElement)[0] 480 firstElement && firstElement.focus() 481 } 482 483 hyva.safeParseNumber = (rawValue) => { 484 const number = rawValue ? parseFloat(rawValue) : null; 485 486 return Array.isArray(number) || isNaN(number) ? rawValue : number; 487 } 488 489 const toCamelCase = s => s.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(''); 490 hyva.createBooleanObject = (name, value = false, additionalMethods = {}) => { 491 492 const camelCase = toCamelCase(name); 493 const key = '__hyva_bool_' + name 494 495 return new Proxy(Object.assign( 496 additionalMethods, 497 { 498 [key]: !!value, 499 [name]() {return !!this[key]}, 500 ['!' + name]() {return !this[key]}, 501 ['not' + camelCase]() {return !this[key]}, 502 ['toggle' + camelCase]() {this[key] = !this[key]}, 503 [`set${camelCase}True`]() {this[key] = true}, 504 [`set${camelCase}False`]() {this[key] = false}, 505 } 506 ), { 507 set(target, prop, value) { 508 return prop === name 509 ? (target[key] = !!value) 510 : Reflect.set(...arguments); 511 } 512 }) 513 } 514 515 hyva.alpineInitialized = (fn) => window.addEventListener('alpine:initialized', fn, {once: true}) 516 window.addEventListener('alpine:init', () => Alpine.data('{}', () => ({})), {once: true}); 517 window.addEventListener('user-allowed-save-cookie', () => internalCookie.saveTempStorageCookies()) 518 519 }( window.hyva = window.hyva || {} ));</script>
519 520 521 <!-- BEGIN GOOGLE ADWORDS CODE --> 522
522<script> 523 { 524 (function initGoogleAdwords(){ 525 const config = { 526 conversionId: "AW\u002D11198393335", 527 gtagSiteSrc: "https\u003A\u002F\u002Fwww.googletagmanager.com\u002Fgtag\u002Fjs\u003Fid\u003DAW\u002D11198393335", 528 conversionLabel: "", 529 isCookieRestrictionModeEnabled: "", 530 cookieName: 'user_allowed_save_cookie', 531 currentWebsite: '1' 532 }; 533 const anonymize_ip = true; 534 535 let allowServices = false, 536 allowedCookies, 537 allowedWebsites; 538 539 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 540 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 541 } 542 543 if (config.isCookieRestrictionModeEnabled && typeof hyva !== 'undefined') { 544 allowedCookies = hyva.getCookie(config.cookieName); 545 546 if (allowedCookies !== null) { 547 allowedWebsites = JSON.parse(unescape(allowedCookies)); 548 549 if (allowedWebsites[config.currentWebsite] === 1) { 550 allowServices = true; 551 } 552 } 553 } else { 554 allowServices = true; 555 } 556 557 if (allowServices) { 558 if (!window.gtag) { 559 const gtagScript = document.createElement('script'); 560 gtagScript.type = 'text/javascript'; 561 gtagScript.async = true; 562 gtagScript.src = config.gtagSiteSrc; 563 document.head.insertBefore(gtagScript, document.head.children[0]); 564 565 window.dataLayer = window.dataLayer || []; 566 window.gtag = function () { 567 dataLayer.push(arguments) 568 }; 569 570 gtag('js', new Date()); 571 gtag('set', 'developer_id.dYjhlMD', true); 572 } 573 574 gtag('config', config.conversionId, {'anonymize_ip': anonymize_ip}); 575 if (config.conversionLabel) { 576 gtag( 577 'event', 578 'conversion', 579 { 580 'send_to': config.conversionId + '/' 581 + config.conversionLabel 582 } 583 ); 584 } 585 } 586 }()) 587 } 588 </script>
588 589 <!-- END GOOGLE ADWORDS CODE --> 590
590<script type="speculationrules"> 591{ 592 "tag": "Hyva Theme", 593 "prefetch": [{ 594 "source": "document", 595 "where": { 596 "and": [ 597 { "href_matches": "/*" }, 598 { "not": { 599 "href_matches": ["/customer/*","/search/*","/sales/*","/wishlist/*","/checkout/*","/paypal/*","/newsletter/*","/stores/store/redirect/*"] }}, 600 { "not": { 601 "selector_matches": [".no-preload", ".do-not-prerender", "[download]", "[rel~=nofollow]", "[target]"] 602 }} 603 ] 604 }, 605 "eagerness": "moderate" 606 }] 607}</script>
607</head> 608<body id="html-body" class="cms-home-page page-layout-1column cms-index-index page-layout-cms-full-width"> 609 <!-- Google Tag Manager (noscript) -->
vendor: 67 bytes, line 609
609<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
609GTM-K57SMFBL
vendor: 82 bytes, line 609
609" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
609<!-- End Google Tag Manager (noscript) --><input name="form_key" type="hidden" value="NMEmRNvncJnEpv9c" />
609<script> 610 document.body.addEventListener('touchstart', () => {}, {passive: true}) 611</script>
611 612<div class="page-wrapper"> <header class="page-header"><a class="action skip sr-only focus:not-sr-only focus:absolute focus:z-40 focus:bg-white 613 contentarea" 614 href="#contentarea"> 615 <span> 616 Skip to Content </span> 617</a><div class="header content"> <div class="widget block block-static-block"><div class="notification-wrapper" role="region" aria-label="Store promotion"> 618 <p class="notification-promo"> 619 Order before Oct 31st, 2026 and get 620 <span class="notification-promo__accent">5% Off</span> 621 <span class="notification-promo__divider" aria-hidden="true">·</span> 622 Promo Code: 623 <strong class="notification-promo__code">thanksgiving2026</strong> 624 </p> 625</div> 626</div></div>
626<script> 627 function initHeader () { 628 return { 629 searchOpen: false, 630 cart: {}, 631 isCartOpen: false, 632 init() { 633 this.checkHeaderSize(); 634 }, 635 checkHeaderSize() { 636 const pageHeader = document.querySelector('.page-header'); 637 if (!pageHeader) return; 638 document.documentElement.style.setProperty("--page-header-height", `${pageHeader.offsetHeight}px`); 639 }, 640 getData(data) { 641 if (data.cart) { this.cart = data.cart } 642 }, 643 isCartEmpty() { 644 return !this.cart.summary_count 645 }, 646 toggleCart(event) { 647 if (event.detail && event.detail.isOpen !== undefined) { 648 this.isCartOpen = event.detail.isOpen 649 if (!this.isCartOpen && this.$refs && this.$refs.cartButton) { 650 this.$refs.cartButton.focus() 651 } 652 } else { 653 this.isCartOpen = true 654 } 655 } 656 } 657 } 658</script>
658 659<div 660 id="header" 661 class="" 662 x-data="initHeader()" 663 @private-content-loaded.window="getData(event.detail.data)" 664 @resize.window.debounce="checkHeaderSize" 665> 666 <div class="relative container px-4 py-4 flex gap-2 sm:gap-4 items-center lg:flex lg:items-center lg:justify-between"> 667 <div class="flex items-center gap-0 lg:gap-6 xl:gap-12"> 668 <div class="order-1 lg:order-2 block lg:hidden mr-4"> 669 670<nav 671 class="navigation lg:hidden" 672 aria-label="Site navigation" 673 x-data="initMenuMobile_6ac74c9dace29()" 674 @load.window="setActiveMenu($root)" 675 @keydown.window.escape="closeMenu()" 676> 677 <button 678 type="button" 679 class="block" 680 aria-label="Open menu" 681 :aria-expanded="open" 682 x-ref="mobileMenuTrigger" 683 x-show="!open" 684 @click="openMenu()" 685 > 686 <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"> 687 <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/> 688</svg> 689 </button> 690 <div 691 x-ref="mobileMenuNavLinks" 692 class=" 693 z-50 fixed top-0 right-0 w-full h-full p-1 hidden 694 flex-col border-t border-container bg-container-lighter 695 overflow-y-auto overflow-x-hidden 696 " 697 :class="{ 'flex': open, 'hidden': !open }" 698 :aria-hidden="open ? 'false' : 'true'" 699 role="dialog" 700 aria-modal="true" 701 > 702 <ul 703 class="border-t flex flex-col gap-y-1 mt-16" 704 aria-label="Site navigation links" 705 > 706 <li 707 data-child-id="category-node-320-main" 708 class="level-0" 709 > 710 <div 711 class="flex items-center transition-transform duration-150 ease-in-out transform" 712 :class="{ 713 '-translate-x-full' : mobilePanelActiveId, 714 'translate-x-0' : !mobilePanelActiveId 715 }" 716 > 717 <a 718 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 719 bg-container-lighter border-container level-0 720 " 721 href="https://dessertlady.ca/cakes" 722 title="Cakes & Cupcakes" 723 > 724 Cakes & Cupcakes </a> 725 <button 726 @click="openSubcategory('category-node-320')" 727 class="absolute right-0 flex items-center justify-center w-11 h-11 mr-8 cursor-pointer 728 bg-container-lighter border-container" 729 aria-label="Open Cakes & Cupcakes subcategories" 730 aria-haspopup="true" 731 :aria-expanded="mobilePanelActiveId === 'category-node-320'" 732 > 733 <div class="w-8 h-8 border rounded"> 734 <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"> 735 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/> 736</svg> 737 </div> 738 </button> 739 </div> 740 <div 741 data-child-id="category-node-320" 742 class="absolute top-0 right-0 z-10 flex flex-col gap-1 w-full h-full p-1 bg-container-lighter" 743 :class="{ 744 'hidden': mobilePanelActiveId !== 'category-node-320' 745 }" 746 > 747 <ul 748 class="mt-16 transition-transform duration-200 ease-in-out translate-x-full transform" 749 :class="{ 750 'translate-x-full' : mobilePanelActiveId !== 'category-node-320', 751 'translate-x-0' : mobilePanelActiveId === 'category-node-320', 752 }" 753 aria-label="Subcategories" 754 > 755 <li> 756 <button 757 type="button" 758 class="flex items-center px-8 py-4 border-b cursor-pointer bg-container border-container w-full border-t" 759 @click="backToMainCategories('category-node-320-main')" 760 aria-label="Back to main categories" 761 > 762 <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"> 763 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/> 764</svg>
765 <span class="ml-4"> 766 Cakes & Cupcakes </span> 767 </button> 768 </li> 769 <li> 770 <a 771 href="https://dessertlady.ca/cakes" 772 title="Cakes & Cupcakes" 773 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 774 bg-container-lighter border-container 775 " 776 > 777 <span class="ml-10"> 778 View All </span> 779 </a> 780 </li> 781 <li> 782 <a 783 href="https://dessertlady.ca/cakes/cupcakes" 784 title="Cupcakes" 785 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 786 bg-container-lighter border-container 787 " 788 > 789 <span class="ml-10 text-base text-gray-700"> 790 Cupcakes </span> 791 </a> 792 </li> 793 <li> 794 <a 795 href="https://dessertlady.ca/catalog/category/view/s/specialty-cakes/id/83/" 796 title="Specialty Cakes" 797 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 798 bg-container-lighter border-container 799 " 800 > 801 <span class="ml-10 text-base text-gray-700"> 802 Specialty Cakes </span> 803 </a> 804 </li> 805 <li> 806 <a 807 href="https://dessertlady.ca/cakes/classic-cakes" 808 title="Classic Cakes" 809 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 810 bg-container-lighter border-container 811 " 812 > 813 <span class="ml-10 text-base text-gray-700"> 814 Classic Cakes </span> 815 </a> 816 </li> 817 <li> 818 <a 819 href="https://dessertlady.ca/cakes/signature-cakes" 820 title="Signature Cakes" 821 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 822 bg-container-lighter border-container 823 " 824 >
825 <span class="ml-10 text-base text-gray-700"> 826 Signature Cakes </span> 827 </a> 828 </li> 829 <li> 830 <a 831 href="https://dessertlady.ca/cakes/moussecakes" 832 title="Moussecakes" 833 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 834 bg-container-lighter border-container 835 " 836 > 837 <span class="ml-10 text-base text-gray-700"> 838 Moussecakes </span> 839 </a> 840 </li> 841 <li> 842 <a 843 href="https://dessertlady.ca/cakes/cheesecakes" 844 title="Cheesecakes" 845 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 846 bg-container-lighter border-container 847 " 848 > 849 <span class="ml-10 text-base text-gray-700"> 850 Cheesecakes </span> 851 </a> 852 </li> 853 <li> 854 <a 855 href="https://dessertlady.ca/cakes/baby-shower-cake-toronto" 856 title="Baby shower Cake Toronto" 857 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 858 bg-container-lighter border-container 859 " 860 > 861 <span class="ml-10 text-base text-gray-700"> 862 Baby shower Cake Toronto </span> 863 </a> 864 </li> 865 </ul> 866 <button 867 @click="closeMenu()" 868 class="absolute flex justify-end w-16 self-end mb-1 transition-none" 869 aria-label="Close menu" 870 > 871 <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"> 872 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 873</svg> 874 </button> 875 </div> 876 </li> 877 <li 878 data-child-id="category-node-235-main" 879 class="level-0" 880 > 881 <div 882 class="flex items-center transition-transform duration-150 ease-in-out transform" 883 :class="{ 884 '-translate-x-full' : mobilePanelActiveId, 885 'translate-x-0' : !mobilePanelActiveId 886 }" 887 > 888 <a 889 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 890 bg-container-lighter border-container level-0 891 " 892 href="https://dessertlady.ca/branded-items" 893 title="Branded Items & Catering" 894 > 895 Branded Items & Catering </a> 896 <button 897 @click="openSubcategory('category-node-235')" 898 class="absolute right-0 flex items-center justify-center w-11 h-11 mr-8 cursor-pointer 899 bg-container-lighter border-container" 900 aria-label="Open Branded Items & Catering subcategories" 901 aria-haspopup="true" 902 :aria-expanded="mobilePanelActiveId === 'category-node-235'" 903 > 904 <div class="w-8 h-8 border rounded"> 905 <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"> 906 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/> 907</svg> 908 </div> 909 </button> 910 </div> 911 <div 912 data-child-id="category-node-235" 913 class="absolute top-0 right-0 z-10 flex flex-col gap-1 w-full h-full p-1 bg-container-lighter" 914 :class="{ 915 'hidden': mobilePanelActiveId !== 'category-node-235' 916 }" 917 > 918 <ul 919 class="mt-16 transition-transform duration-200 ease-in-out translate-x-full transform" 920 :class="{ 921 'translate-x-full' : mobilePanelActiveId !== 'category-node-235', 922 'translate-x-0' : mobilePanelActiveId === 'category-node-235', 923 }" 924 aria-label="Subcategories" 925 > 926 <li> 927 <button 928 type="button" 929 class="flex items-center px-8 py-4 border-b cursor-pointer bg-container border-container w-full border-t" 930 @click="backToMainCategories('category-node-235-main')" 931 aria-label="Back to main categories" 932 > 933 <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"> 934 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/> 935</svg>
936 <span class="ml-4"> 937 Branded Items & Catering </span> 938 </button> 939 </li> 940 <li> 941 <a 942 href="https://dessertlady.ca/branded-items" 943 title="Branded Items & Catering" 944 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 945 bg-container-lighter border-container 946 " 947 > 948 <span class="ml-10"> 949 View All </span> 950 </a> 951 </li> 952 <li> 953 <a 954 href="https://dessertlady.ca/branded-items/catering" 955 title="Catering Platter" 956 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 957 bg-container-lighter border-container 958 " 959 > 960 <span class="ml-10 text-base text-gray-700"> 961 Catering Platter </span> 962 </a> 963 </li> 964 <li> 965 <a 966 href="https://dessertlady.ca/branded-items/branded-cakes" 967 title="Branded Cakes" 968 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 969 bg-container-lighter border-container 970 " 971 > 972 <span class="ml-10 text-base text-gray-700"> 973 Branded Cakes </span> 974 </a> 975 </li> 976 <li> 977 <a 978 href="https://dessertlady.ca/branded-items/branded-logo-sugar-cookies" 979 title="Branded/Logo Sugar Cookies" 980 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 981 bg-container-lighter border-container 982 " 983 > 984 <span class="ml-10 text-base text-gray-700"> 985 Branded/Logo Sugar Cookies </span> 986 </a> 987 </li> 988 <li> 989 <a 990 href="https://dessertlady.ca/branded-items/branded-logo-cupcakes" 991 title="Branded/Logo Cupcakes" 992 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 993 bg-container-lighter border-container 994 " 995 >
996 <span class="ml-10 text-base text-gray-700"> 997 Branded/Logo Cupcakes </span> 998 </a> 999 </li> 1000 </ul> 1001 <button 1002 @click="closeMenu()" 1003 class="absolute flex justify-end w-16 self-end mb-1 transition-none" 1004 aria-label="Close menu" 1005 > 1006 <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"> 1007 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1008</svg> 1009 </button> 1010 </div> 1011 </li> 1012 <li 1013 data-child-id="category-node-378-main" 1014 class="level-0" 1015 > 1016 <div 1017 class="flex items-center transition-transform duration-150 ease-in-out transform" 1018 :class="{ 1019 '-translate-x-full' : mobilePanelActiveId, 1020 'translate-x-0' : !mobilePanelActiveId 1021 }" 1022 > 1023 <a 1024 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1025 bg-container-lighter border-container level-0 1026 " 1027 href="https://dessertlady.ca/baked-goods" 1028 title="Baked Goods" 1029 > 1030 Baked Goods </a> 1031 <button 1032 @click="openSubcategory('category-node-378')" 1033 class="absolute right-0 flex items-center justify-center w-11 h-11 mr-8 cursor-pointer 1034 bg-container-lighter border-container" 1035 aria-label="Open Baked Goods subcategories" 1036 aria-haspopup="true" 1037 :aria-expanded="mobilePanelActiveId === 'category-node-378'" 1038 > 1039 <div class="w-8 h-8 border rounded"> 1040 <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"> 1041 <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/> 1042</svg> 1043 </div> 1044 </button> 1045 </div> 1046 <div 1047 data-child-id="category-node-378" 1048 class="absolute top-0 right-0 z-10 flex flex-col gap-1 w-full h-full p-1 bg-container-lighter" 1049 :class="{ 1050 'hidden': mobilePanelActiveId !== 'category-node-378' 1051 }" 1052 > 1053 <ul 1054 class="mt-16 transition-transform duration-200 ease-in-out translate-x-full transform" 1055 :class="{ 1056 'translate-x-full' : mobilePanelActiveId !== 'category-node-378', 1057 'translate-x-0' : mobilePanelActiveId === 'category-node-378', 1058 }" 1059 aria-label="Subcategories" 1060 > 1061 <li> 1062 <button 1063 type="button" 1064 class="flex items-center px-8 py-4 border-b cursor-pointer bg-container border-container w-full border-t" 1065 @click="backToMainCategories('category-node-378-main')" 1066 aria-label="Back to main categories" 1067 > 1068 <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"> 1069 <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/> 1070</svg>
1071 <span class="ml-4"> 1072 Baked Goods </span> 1073 </button> 1074 </li> 1075 <li> 1076 <a 1077 href="https://dessertlady.ca/baked-goods" 1078 title="Baked Goods" 1079 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1080 bg-container-lighter border-container 1081 " 1082 > 1083 <span class="ml-10"> 1084 View All </span> 1085 </a> 1086 </li> 1087 <li> 1088 <a 1089 href="https://dessertlady.ca/baked-goods/biscotti-and-loaves" 1090 title="Biscotti and Loaves " 1091 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1092 bg-container-lighter border-container 1093 " 1094 > 1095 <span class="ml-10 text-base text-gray-700"> 1096 Biscotti and Loaves </span> 1097 </a> 1098 </li> 1099 <li> 1100 <a 1101 href="https://dessertlady.ca/baked-goods/pie-and-premium-tarts" 1102 title="Pie and Premium Tarts " 1103 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1104 bg-container-lighter border-container 1105 " 1106 > 1107 <span class="ml-10 text-base text-gray-700"> 1108 Pie and Premium Tarts </span> 1109 </a> 1110 </li> 1111 <li> 1112 <a 1113 href="https://dessertlady.ca/baked-goods/cookies-and-shortbread" 1114 title="Cookies and Shortbread" 1115 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1116 bg-container-lighter border-container 1117 " 1118 > 1119 <span class="ml-10 text-base text-gray-700"> 1120 Cookies and Shortbread </span> 1121 </a> 1122 </li> 1123 <li> 1124 <a 1125 href="https://dessertlady.ca/baked-goods/gift-baskets" 1126 title="Gift Basket" 1127 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1128 bg-container-lighter border-container 1129 " 1130 >
1131 <span class="ml-10 text-base text-gray-700"> 1132 Gift Basket </span> 1133 </a> 1134 </li> 1135 </ul> 1136 <button 1137 @click="closeMenu()" 1138 class="absolute flex justify-end w-16 self-end mb-1 transition-none" 1139 aria-label="Close menu" 1140 > 1141 <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"> 1142 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1143</svg> 1144 </button> 1145 </div> 1146 </li> 1147 <li 1148 data-child-id="category-node-403-main" 1149 class="level-0" 1150 > 1151 <div 1152 class="flex items-center transition-transform duration-150 ease-in-out transform" 1153 :class="{ 1154 '-translate-x-full' : mobilePanelActiveId, 1155 'translate-x-0' : !mobilePanelActiveId 1156 }" 1157 > 1158 <a 1159 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1160 bg-container-lighter border-container level-0 1161 " 1162 href="https://dessertlady.ca/birthday-cake-toronto" 1163 title="Birthday Cake Toronto" 1164 > 1165 Birthday Cake Toronto </a> 1166 </div> 1167 </li> 1168 <li 1169 data-child-id="category-node-373-main" 1170 class="level-0" 1171 > 1172 <div 1173 class="flex items-center transition-transform duration-150 ease-in-out transform" 1174 :class="{ 1175 '-translate-x-full' : mobilePanelActiveId, 1176 'translate-x-0' : !mobilePanelActiveId 1177 }" 1178 > 1179 <a 1180 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1181 bg-container-lighter border-container level-0 1182 " 1183 href="https://dessertlady.ca/candles-sparklers" 1184 title="Candles & Sparklers" 1185 > 1186 Candles & Sparklers </a> 1187 </div> 1188 </li> 1189 <li 1190 data-child-id="category-node-406-main" 1191 class="level-0" 1192 > 1193 <div 1194 class="flex items-center transition-transform duration-150 ease-in-out transform" 1195 :class="{ 1196 '-translate-x-full' : mobilePanelActiveId, 1197 'translate-x-0' : !mobilePanelActiveId 1198 }" 1199 > 1200 <a 1201 class="flex items-center w-full px-8 py-4 border-b cursor-pointer 1202 bg-container-lighter border-container level-0 1203 " 1204 href="https://dessertlady.ca/valentines-day" 1205 title="Valentines Day" 1206 > 1207 Valentines Day </a> 1208 </div> 1209 </li> 1210 </ul> 1211 <button 1212 @click="closeMenu()" 1213 class="absolute flex justify-end w-16 self-end mb-1" 1214 aria-label="Close menu" 1215 type="button" 1216 > 1217 <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"> 1218 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1219</svg> 1220 </button> 1221 </div> 1222</nav>
1223<script> 1224 'use strict'; 1225 1226 const initMenuMobile_6ac74c9dace29 = () => { 1227 return { 1228 mobilePanelActiveId: null, 1229 open: false, 1230 setActiveMenu(menuNode) { 1231 Array.from(menuNode.querySelectorAll('a')).filter(link => { 1232 return link.href === window.location.href.split('?')[0]; 1233 }).map(item => { 1234 item.classList.add('underline'); 1235 item.closest('li.level-0') && 1236 item.closest('li.level-0').querySelector('a.level-0').classList.add('underline'); 1237 }); 1238 }, 1239 openMenu() { 1240 this.open = true 1241 this.$nextTick(() => hyva.trapFocus(this.$refs['mobileMenuNavLinks'])); 1242 // Prevent from body scrolling while mobile menu opened 1243 document.body.style.overflow = 'hidden'; 1244 }, 1245 closeMenu() { 1246 document.body.style.overflow = ''; 1247 1248 if (this.open) { 1249 this.$nextTick(() => this.$refs['mobileMenuTrigger'].focus() || hyva.releaseFocus()); 1250 } 1251 1252 this.open = false 1253 this.mobilePanelActiveId = null 1254 }, 1255 openSubcategory(index) { 1256 const menuNodeRef = document.querySelector('[data-child-id=' + index + ']') 1257 this.mobilePanelActiveId = this.mobilePanelActiveId === index ? 0 : index 1258 this.$nextTick(() => hyva.trapFocus(menuNodeRef)) 1259 }, 1260 backToMainCategories(index) { 1261 const menuNodeRef = document.querySelector('[data-child-id=' + index + ']') 1262 this.mobilePanelActiveId = 0 1263 this.$nextTick(() => { 1264 hyva.trapFocus(this.$refs['mobileMenuNavLinks']) 1265 menuNodeRef.querySelector('a').focus() 1266 }) 1267 } 1268 } 1269 } 1270</script>
1270 </div> 1271 <div class="order-2 lg:order-1"> 1272 1273<a 1274 class="inline-block align-middle shrink-0" 1275 href="https://dessertlady.ca/" 1276 title="Go to Home page" 1277> 1278 <img 1279 class="h-12 w-auto max-w-full object-contain shrink-0 xl:h-14" 1280 src="https://dessertlady.ca/media/logo/stores/1/logo.png" 1281 alt="Dessert Lady Cafe & Bakery Ltd" 1282 width="250" 1283 height="64" 1284 loading="eager" 1285 fetchpriority="high" 1286 > 1287 </a> </div> 1288 <div class="order-3 hidden lg:block"> 1289 1290<nav 1291 class="navigation hidden lg:block" 1292 aria-label="Main menu" 1293 x-data="initMenuDesktop_6ac74c9db692b()" 1294 x-ref="nav-desktop" 1295 @load.window="setActiveMenu($root)" 1296> 1297 <ul class="flex flex-wrap lg:flex-nowrap gap-x-4 xl:gap-x-8 okmenu"> 1298 <li 1299 class="level-0 whitespace-nowrap" 1300 x-data="{ 1301 open: false, 1302 canHover(func) { 1303 const isHoverableDevice = window.matchMedia('(hover: hover) and (pointer: fine)').matches; 1304 if (!isHoverableDevice) return; 1305 func(); 1306 } 1307 }" 1308 @click.outside="open = false" 1309 @mouseenter="canHover(() => { open = true })" 1310 @mouseleave="canHover(() => { open = false })" 1311 @keydown.escape="open = false" 1312 > 1313 <button 1314 type="button" 1315 class="level-0 group w-full flex justify-between items-center gap-1 pt-4 pb-[calc(theme(padding.4)_-_3px)] transition" 1316 @click="open = !open" 1317 aria-expanded="false" 1318 :aria-expanded="open" 1319 aria-controls="desktop-menu-category-cakesCupcakes" 1320 > 1321 Cakes & Cupcakes 1322 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="transition group-aria-expanded:rotate-180 text-slate-400 group-aria-expanded:text-inherit" width="24" height="24" aria-hidden="true"> 1323 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 1324</svg> 1325 </button> 1326 <div 1327 id="desktop-menu-category-cakesCupcakes" 1328 class="z-30 absolute inset-x-0 flex gap-8 container py-6 min-h-64 border-slate-100 rounded-b-md bg-white shadow-md overflow-auto" 1329 x-show="open === true" 1330 x-transition 1331 x-cloak 1332 > 1333 <div class="grow flex flex-col justify-between"> 1334 1335 <ul class="columns-xs gap-8 navSet01"> 1336 1337 <li class="col_link"> 1338 <div class="navHdr01"> 1339 <a href="/cakes" class="">Fresh Cakes at the Bakery ð</a> 1340 </div> 1341 <ul class="navLinks"> 1342 <li><a href="https://dessertlady.ca/strawberry-shortcake" class=""><span class="txtClrRed">Our Signature Strawberry Cake</span> ð</a></li> 1343 <li><a href="https://dessertlady.ca/cakes/classic-cakes">Classic Cakes</a></li> 1344 <li><a href="https://dessertlady.ca/cakes/specialty-cakes">Specialty Cakes</a></li> 1345 <li><a href="https://dessertlady.ca/cakes/signature-cakes">Signature Cakes</a></li> 1346 <li><a href="https://dessertlady.ca/cakes/moussecakes">Mousse Cakes</a></li> 1347 <li><a href="https://dessertlady.ca/cakes/cheesecakes">Cheese Cakes</a></li> 1348 <li><a href="https://dessertlady.ca/cakes/cupcakes/regular-size-cupcakes">Regular Sized Cupcakes</a></li> 1349 <li><a href="https://dessertlady.ca/cakes/cupcakes/mini-cupcakes">Mini Cupcakes</a></li> 1350 <li><a href="https://dessertlady.ca/cakes"><b>Shop all Cakes</b></a></li> 1351 </ul> 1352 </li> 1353 1354 <li class="col_link"> 1355 <div class="navHdr01"> 1356 <div class="font-bold">Shop by Occasion</div> 1357 </div> 1358 <ul class="navLinks"> 1359 <li><a href="https://dessertlady.ca/birthday-cake-toronto">Birthday</a></li> 1360 <li><a href="https://dessertlady.ca/cakes/baby-shower-cake-toronto">Baby Shower Cake</a></li> 1361 <li><a href="/branded-items">Corporate Branding</a></li> 1362 </ul> 1363 </li> 1364 <li class="col_link"> 1365 <div class="navHdr01"> 1366 <div class="font-bold">Shop by Flavour</div> 1367 </div> 1368 <ul class="navLinks"> 1369 <li><a href="https://dessertlady.ca/categories/chocolate-desserts-toronto">Chocolate ð«</a></li> 1370 <li><a href="https://dessertlady.ca/categories/vanilla-desserts-in-toronto">Vanilla ð¨</a></li> 1371 <li><a href="https://dessertlady.ca/categories/red-velvet-cakes-cupcakes-in-toronto">Red Velvet</a></li> 1372 <li><a href="https://dessertlady.ca/categories/toronto-ube-cakes-and-cheesecakes">Ube ð </a></li> 1373 </ul> 1374 </li> 1375 <li class="col_img"> 1376 <div class="navImgBx"> 1377 <div class="navImg"> 1378 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/cupcakes-toronto.jpg"> 1379 </div> 1380 <div class="navTxt01"> 1381 <a href="https://dessertlady.ca/cakes/cupcakes">
1382 <span class="font-bold">Checkout our Cupcakes</span> ð§ 1383 </a> 1384 </div> 1385 </div> 1386 </li> 1387 <li class="col_img"> 1388 <div class="navImgBx"> 1389 <div class="navImg"> 1390 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/img_02.jpg"> 1391 </div> 1392 <div class="navTxt01"> 1393 <a href="https://dessertlady.ca/strawberry-shortcake"> 1394 <span class="txtClrRed">Our Signature Strawberry Cake</span> ð 1395 </a> 1396 </div> 1397 </div> 1398 </li> 1399 1400 </ul> 1401 1402 </div> 1403 1404 1405 </div> 1406 </li> 1407 1408 <li 1409 class="level-0 whitespace-nowrap" 1410 x-data="{ 1411 open: false, 1412 canHover(func) { 1413 const isHoverableDevice = window.matchMedia('(hover: hover) and (pointer: fine)').matches; 1414 if (!isHoverableDevice) return; 1415 func(); 1416 } 1417 }" 1418 @click.outside="open = false" 1419 @mouseenter="canHover(() => { open = true })" 1420 @mouseleave="canHover(() => { open = false })" 1421 @keydown.escape="open = false" 1422 > 1423 <button 1424 type="button" 1425 class="level-0 group w-full flex justify-between items-center gap-1 pt-4 pb-[calc(theme(padding.4)_-_3px)] transition" 1426 @click="open = !open" 1427 aria-expanded="false" 1428 :aria-expanded="open" 1429 aria-controls="desktop-menu-category-brandedCatering" 1430 > 1431 Branded & Catering 1432 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="transition group-aria-expanded:rotate-180 text-slate-400 group-aria-expanded:text-inherit" width="24" height="24" aria-hidden="true"> 1433 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 1434</svg> 1435 </button> 1436 <div 1437 id="desktop-menu-category-brandedCatering" 1438 class="z-30 absolute inset-x-0 flex gap-8 container p-0 min-h-64 border-slate-100 rounded-b-md bg-white shadow-md overflow-auto" 1439 x-show="open === true" 1440 x-transition 1441 x-cloak 1442 > 1443 <div class="grow flex flex-col justify-between"> 1444 <ul class="columns-xs gap-8 navSet02 px-6 py-6"> 1445 1446 <li class="col_link"> 1447 <div class="navHdr01"> 1448 <a href="/branded-items" class="">Branded Items</a> 1449 </div> 1450 <ul class="navLinks"> 1451 <li><a href="https://dessertlady.ca/branded-items/branded-cakes">Branded Cakes</a></li> 1452 <li><a href="https://dessertlady.ca/branded-items/branded-logo-sugar-cookies">Branded Logo Cookies</a></li> 1453 <li><a href="https://dessertlady.ca/branded-items/branded-logo-cupcakes">Branded Logo Cupcakes</a></li> 1454 </ul> 1455 </li> 1456 1457 <li class="col_link"> 1458 <div class="navHdr01"> 1459 <a href="/branded-items/catering" class="">Catering Platters</a> 1460 </div> 1461 <ul class="navLinks"> 1462 <li><a href="/branded-items/catering/savory-catering-platter">Savory Platters</a></li> 1463 <li><a href="/branded-items/catering/finger-sweet-platter">Finger Sweet Platter</a></li> 1464 <li><a href="/branded-items/catering/fruit-platter">Fruit Platters</a></li> 1465 <li><a href="/branded-items/catering"><b>Shop all Platters</b></a></li> 1466 </ul> 1467 </li> 1468 1469 <li class="col_img"> 1470 <div class="navImgBx"> 1471 <div class="navImg"> 1472 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/img_04.jpg"> 1473 </div> 1474 <div class="navTxt01"> 1475 <a href="https://dessertlady.ca/branded-items/branded-logo-cupcakes">
1476 <span class="txtClrBlack">Branded Cupcakes</span> 1477 </a> 1478 </div> 1479 </div> 1480 </li> 1481 1482 <li class="col_img"> 1483 <div class="navImgBx"> 1484 <div class="navImg"> 1485 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/img_05.jpg"> 1486 </div> 1487 <div class="navTxt01"> 1488 <a href="https://dessertlady.ca/branded-items/branded-logo-sugar-cookies"> 1489 <span class="txtClrBlack">Branded Logo Cookies</span> 1490 </a> 1491 </div> 1492 </div> 1493 </li> 1494 1495 <li class="col_img"> 1496 <div class="navImgBx"> 1497 <div class="navImg"> 1498 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/img_03.jpg"> 1499 </div> 1500 <div class="navTxt01"> 1501 <a href="https://dessertlady.ca/branded-items/branded-cakes"> 1502 <span class="txtClrBlack">Branded Cakes</span> 1503 </a> 1504 </div> 1505 </div> 1506 </li> 1507 1508 1509 </ul> 1510 <div class="navBlueNote"> 1511 <p>Corporate Orders Available for Pickup or Delivery <a href="https://dessertlady.ca/gourmet-cakes/edible-photo-faq">Learn More</a></p> 1512 </div> 1513 </div> 1514 1515 </div> 1516 </li> 1517 1518 <li 1519 class="level-0 whitespace-nowrap" 1520 x-data="{ 1521 open: false, 1522 canHover(func) { 1523 const isHoverableDevice = window.matchMedia('(hover: hover) and (pointer: fine)').matches; 1524 if (!isHoverableDevice) return; 1525 func(); 1526 } 1527 }" 1528 @click.outside="open = false" 1529 @mouseenter="canHover(() => { open = true })" 1530 @mouseleave="canHover(() => { open = false })" 1531 @keydown.escape="open = false" 1532 > 1533 <button 1534 type="button" 1535 class="level-0 group w-full flex justify-between items-center gap-1 pt-4 pb-[calc(theme(padding.4)_-_3px)] transition" 1536 @click="open = !open" 1537 aria-expanded="false" 1538 :aria-expanded="open" 1539 aria-controls="desktop-menu-category-bakedGoods" 1540 > 1541 Baked Goods 1542 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="transition group-aria-expanded:rotate-180 text-slate-400 group-aria-expanded:text-inherit" width="24" height="24" aria-hidden="true"> 1543 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 1544</svg> 1545 </button> 1546 <div 1547 id="desktop-menu-category-bakedGoods" 1548 class="z-30 absolute inset-x-0 flex gap-8 container py-6 min-h-64 border-slate-100 rounded-b-md bg-white shadow-md overflow-auto" 1549 x-show="open === true" 1550 x-transition 1551 x-cloak 1552 > 1553 <div class="grow flex flex-col justify-between"> 1554 1555 <ul class="columns-xs gap-8 navSet03 px-6 py-6"> 1556 1557 <li class="col_link"> 1558 <div class="navHdr01"> 1559 <a href="/baked-goods" class="">Baked Goods ðª</a> 1560 </div> 1561 <ul class="navLinks"> 1562 <li><a href="/baked-goods/pie-and-premium-tarts">Pies and Tarts</a></li> 1563 <li><a href="https://dessertlady.ca/baked-goods/biscotti-and-loaves">Biscotti & Loaves</a></li> 1564 <li><a href="/baked-goods/gift-baskets">Gift Baskets</a></li> 1565 <li><a href="https://dessertlady.ca/baked-goods/cookies-and-shortbread/cookie
1565s">Cookies</a></li> 1566 <li><a href="https://dessertlady.ca/baked-goods/cookies-and-shortbread/shortbread">Shortbreads</a></li> 1567 <li><a href="https://dessertlady.ca//baked-goods"><b>Shop all Baked Goods</b></a></li> 1568 </ul> 1569 </li> 1570 1571 1572 <li class="col_img"> 1573 <div class="navImgBx"> 1574 <div class="navImg"> 1575 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/gift-baskets.jpg"> 1576 </div> 1577 <div class="navTxt01"> 1578 <a href="/baked-goods/gift-baskets"> 1579 <span class="txtClrBlack">Gift Baskets</span> 1580 </a> 1581 </div> 1582 </div> 1583 </li> 1584 1585 <li class="col_img"> 1586 <div class="navImgBx"> 1587 <div class="navImg"> 1588 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/img_06.jpg"> 1589 </div> 1590 <div class="navTxt01"> 1591 <a href="https://dessertlady.ca/baked-goods/biscotti-and-loaves"> 1592 <span class="txtClrBlack">Biscottis</span> 1593 </a> 1594 </div> 1595 </div> 1596 </li> 1597 1598 <li class="col_img"> 1599 <div class="navImgBx"> 1600 <div class="navImg"> 1601 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/img_07.jpg"> 1602 </div> 1603 <div class="navTxt01"> 1604 <a href="https://dessertlady.ca/baked-goods/cookies-and-shortbread/cookies"> 1605 <span class="txtClrBlack">Cookies</span> 1606 </a> 1607 </div> 1608 </div> 1609 </li> 1610 1611 <li class="col_img"> 1612 <div class="navImgBx"> 1613 <div class="navImg"> 1614 <img src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/images/img_08.jpg"> 1615 </div> 1616 <div class="navTxt01"> 1617 <a href="https://dessertlady.ca/baked-goods/cookies-and-shortbread/shortbread"> 1618 <span class="txtClrBlack">Shortbreads</span> 1619 </a> 1620 </div> 1621 </div> 1622 </li> 1623 1624 1625 </ul> 1626 1627 </div> 1628 1629 1630 1631 </div> 1632 </li> 1633 </ul> 1634</nav>
1635<script> 1636 const initMenuDesktop_6ac74c9db692b = () => { 1637 return { 1638 setActiveMenu(menuNode) { 1639 Array.from(menuNode.querySelectorAll('a')) 1640 .filter(link => link.href === window.location.href.split('?')[0]) 1641 .forEach(item => { 1642 item.setAttribute('aria-current', 'page'); 1643 1644 for (let level = 0; level < 3; level++) { 1645 const closestMenu = item.closest(`.level-${level}`); 1646 1647 if (closestMenu) { 1648 const button = closestMenu.querySelector('button'); 1649 if (button) { 1650 button.setAttribute('data-has-current', ''); 1651 } 1652 } 1653 } 1654 } 1655 ); 1656 } 1657 } 1658 } 1659</script>
1659 </div> 1660 </div> 1661 1662 <div class="flex gap-0.5 items-center ml-auto lg:ml-0 lg:justify-self-end"> 1663 <div class="holiday-block"> 1664 </div> 1665 1666 1667 <!-- Uber Eats Link --> 1668 <a 1669 href="https://order.koomi.com/dessert-lady-RAV91px8Zx" 1670 class="hidden xl:inline-block rounded p-1 hover:bg-primary/10 outline-offset-2 mr-1" 1671 target="_blank" 1672 rel="noopener" 1673 title="Lunch Menu" 1674 > 1675 <span class="text-sm font-medium">Lunch Menu</span> 1676 </a> 1677 1678 <a 1679 href="https://www.order.store/store/dessert-lady-cafe-and-bakery/fX2KK-xDRHyo5Zu0tROe5A" 1680 class="hidden xl:inline-block rounded p-1 hover:bg-primary/10 outline-offset-2 mr-1" 1681 target="_blank" 1682 rel="noopener" 1683 title="Order via Uber Eats" 1684 > 1685 <span class="text-sm font-medium">Uber Eats</span> 1686 </a> 1687 1688 <button 1689 id="menu-search-icon" 1690 class="inline-block rounded p-1 hover:bg-primary/10 outline-offset-2" 1691 aria-label="Toggle search form" 1692 aria-haspopup="true" 1693 :aria-expanded="searchOpen" 1694 x-ref="searchButton" 1695 @click.prevent="searchOpen = !searchOpen; $dispatch('search-open')" 1696 > 1697 <span x-show="!searchOpen"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="34" height="34" aria-hidden="true"> 1698 <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/> 1699</svg> 1700</span> 1701 <span x-show="searchOpen" x-cloak><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="34" height="34" aria-hidden="true"> 1702 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 1703</svg> 1704</span> 1705 </button> 1706 1707 1708 1709<div 1710 class="relative inline-block" 1711 x-data="{ open: false }" 1712 @keyup.escape="open = false" 1713 @click.outside="open = false" 1714> 1715 <button 1716 type="button" 1717 id="customer-menu" 1718 class="block rounded p-1 hover:bg-primary/10 outline-offset-2" 1719 @click="open = !open" 1720 :aria-expanded="open ? 'true' : 'false'" 1721 aria-label="My Account" 1722 aria-haspopup="true" 1723 > 1724 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="34" height="34" aria-hidden="true"> 1725 <path stroke-linecap="round" stroke-linejoin="round" d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z"/> 1726</svg> 1727 </button> 1728 <nav 1729 class="z-30 absolute -right-4 w-48 sm:w-56 mt-2 py-2 px-1 overflow-auto 1730 rounded shadow-lg lg:mt-3 bg-container-lighter" 1731 x-cloak 1732 x-show="open" 1733 x-transition 1734 aria-labelledby="customer-menu" 1735 > 1736 1737<a id="customer.header.sign.in.link" 1738 class="block px-4 py-2 lg:px-5 lg:py-2 hover:bg-gray-100" 1739 onclick="hyva.setCookie && hyva.setCookie( 1740 'login_redirect', 1741 window.location.href, 1742 1 1743 )" 1744 href="https://dessertlady.ca/customer/account/index/" 1745 title="Sign In" 1746> 1747 Sign In</a> 1748 <a id="customer.header.register.link" 1749 class="block px-4 py-2 lg:px-5 lg:py-2 hover:bg-gray-100" 1750 href="https://dessertlady.ca/customer/account/create/" 1751 title="Create an Account" 1752 > 1753 Create an Account </a> 1754 </nav> 1755</div> 1756 1757 <button 1758 id="menu-cart-icon" 1759 class="relative inline-block rounded p-1 hover:bg-primary/10 outline-offset-2" 1760 x-ref="cartButton" 1761 :aria-disabled="isCartEmpty()" 1762 :aria-label="` 1763 Toggle minicart, 1764 ${isCartEmpty() 1765 ? 'Cart is empty' 1766 : cart.summary_count > 1 1767 ? hyva.str('%1 items', cart.summary_count) 1768 : hyva.str('%1 item', cart.summary_count) 1769 }`" 1770 @click.prevent.stop="() => {
1771 $dispatch('toggle-cart', { isOpen: true }) 1772 }" 1773 @toggle-cart.window="toggleCart($event)" 1774 :aria-expanded="isCartOpen" 1775 aria-haspopup="dialog" 1776 > 1777 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="34" height="34" aria-hidden="true"> 1778 <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 0 014 0z"/> 1779</svg> 1780 <span 1781 x-text="cart.summary_count" 1782 x-show="!isCartEmpty()" 1783 x-cloak 1784 class="absolute -top-1.5 -right-1.5 px-2 py-0.5 rounded-full bg-red-600 text-red-50 1785 text-xs font-semibold leading-none text-center uppercase tabular-nums" 1786 aria-hidden="true" 1787 ></span> 1788 </button> 1789 </div> 1790 </div> 1791 1792 <div 1793 id="search-content" 1794 class="absolute top-full inset-x-0 pb-3.5 bg-white shadow-md" 1795 x-cloak 1796 x-show="searchOpen" 1797 @click.outside="searchOpen = false" 1798 @keydown.escape="searchOpen = false; $refs.searchButton.focus()" 1799 > 1800 <div class="container">
1800<script> 1801 'use strict'; 1802 1803 function initMiniSearch() { 1804 return { 1805 minSearchLength: 3, 1806 suggestions: [], 1807 suggest() { 1808 const search = this.$refs.searchInput; 1809 if (search.value.length >= this.minSearchLength) { 1810 search.setCustomValidity(''); 1811 search.reportValidity(); 1812 this.fetchSuggestions(search.value); 1813 } else { 1814 this.suggestions = []; 1815 } 1816 }, 1817 fetchSuggestions(term) { 1818 fetch( 1819 window.BASE_URL + 'search/ajax/suggest?' + new URLSearchParams({q: term}), 1820 { 1821 headers: { 1822 'X-Requested-With': 'XMLHttpRequest' 1823 } 1824 } 1825 ) 1826 .then(response => response.json()) 1827 .then(result => this.suggestions = result); 1828 }, 1829 search(term) { 1830 const search = this.$refs.searchInput; 1831 term = term || search.value; 1832 if (term.length < this.minSearchLength) { 1833 search.setCustomValidity('Minimum\u0020Search\u0020query\u0020length\u0020is\u00203'); 1834 search.reportValidity(); 1835 } else { 1836 search.setCustomValidity(''); 1837 search.value = term; 1838 this.$refs.form.submit(); 1839 } 1840 }, 1841 focusElement(element) { 1842 if (element && element.nodeName === "DIV") { 1843 element.focus(); 1844 return true; 1845 } else { 1846 return false; 1847 } 1848 } 1849 } 1850 } 1851</script>
1851 1852<search x-data="initMiniSearch()"> 1853 <form class="form minisearch relative container px-0" id="search_mini_form" x-ref="form" @submit.prevent="search()" 1854 action="https://dessertlady.ca/catalogsearch/result/" method="get"> 1855 <div class="flex"> 1856 <label class="sr-only" for="search" data-role="minisearch-label"> 1857 <span>Search</span> 1858 </label> 1859 <input id="search" 1860 x-ref="searchInput" 1861 type="search" 1862 autocomplete="off" 1863 name="q" 1864 value="" 1865 placeholder="Search entire store here..." 1866 maxlength="128" 1867 class="w-full border-slate-300 rounded-l-md" 1868 @focus.once="suggest" 1869 @input.debounce.300="suggest" 1870 @keydown.arrow-down.prevent="focusElement($root.querySelector('[tabindex]'))" 1871 @search-open.window.debounce.10="$el.focus(); $el.select()" 1872 > 1873 <button 1874 type="submit" 1875 title="Search" 1876 class="action search btn btn-primary rounded-l-none rounded-r-md px-3" 1877 aria-label="Search" 1878 > 1879 <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"> 1880 <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/> 1881</svg> 1882 </button> 1883 </div> 1884 <template x-if="suggestions.length > 0"> 1885 <div class="z-10 absolute top-full inset-x-0 leading-normal p-2 rounded-md bg-container-lighter text-slate-800 flex flex-col mt-2"> 1886 <template x-for="suggestion in suggestions"> 1887 <div class="flex justify-between p-2 even:bg-container mb-1 cursor-pointer 1888 border border-container hover:bg-container-darker rounded-md" 1889 tabindex="0" 1890 @click="search(suggestion.title)" 1891 @keydown.enter="search(suggestion.title)" 1892 @keydown.arrow-up.prevent=" 1893 focusElement($event.target.previousElementSibling) || $refs.searchInput.focus() 1894 " 1895 @keydown.arrow-down.prevent="focusElement($event.target.nextElementSibling)" 1896 > 1897 <span x-text="suggestion.title"></span> 1898 <span x-text="suggestion.num_results"></span> 1899 </div> 1900 </template> 1901 </div> 1902 </template> 1903 </form> 1904</search></div> 1905 </div> 1906 1907 <style> 1908 [x-cloak] { display: none !important; } 1909</style>
1910<script> 1911 function initCartDrawer() { 1912 return { 1913 open: false, 1914 isLoading: false, 1915 loadingProductId: null, 1916 cart: {}, 1917 maxItemsToDisplay: 10, 1918 itemsCount: 0, 1919 crossSellProducts: [{"id":"1231","name":"Strawberry Shortcake","url":"https:\/\/dessertlady.ca\/strawberry-shortcake","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/s\/t\/strawberryshortcake_1.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1231\" data-price-box=\"product-id-1231\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1231']\" class=\"price-container price-final_price tax weee\"\n >\n <span :id=\"$id('product\\u002Dprice\\u002D1231')\" data-price-amount=\"45\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 45.00<\/span><\/span>\n <\/span> <\/div>","price_value":45,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1231\/"},{"id":"1246","name":"Belgian Chocolate Mousse Cake","url":"https:\/\/dessertlady.ca\/belgian-chocolate-mousse-cake","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/b\/e\/belgianchoco.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1246\" data-price-box=\"product-id-1246\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1246']\" class=\"price-container price-final_price tax weee\"\n >\n <span :id=\"$id('product\\u002Dprice\\u002D1246')\" data-price-amount=\"45\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 45.00<\/span><\/span>\n <\/span> <\/div>","price_value":45,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1246\/"},{"id":"1247","name":"Mango Dark Chocolate Mousse Cake","url":"https:\/\/dessertlady.ca\/mango-dark-chocolate-mousse-cake","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/m\/a\/mangochoco.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1247\" data-price-box=\"product-id-1247\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1247']\" class=\"price-container price-final_price tax weee\"\n >\n <span :id=\"$id('product\\u002Dprice\\u002D1247')\" data-price-amount=\"45\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 45.00<\/span><\/span>\n <\/span> <\/div>","price_value":45,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1247\/"},{"id":"1248","name":"Pistachio Dark Chocolate Mousse Cake","url":"https:\/\/dessertlady.ca\/pistachio-dark-chocolate-mousse-cake","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/p\/i\/pistachiochoco_1.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1248\" data-price-box=\"product-id-1248\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1248']\" class=\"price-container price-final_price tax weee\"\n >\n <span :id=\"$id('product\\u002Dprice\\u002D1248')\" data-price-amount=\"50\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 50.00<\/span><\/span>\n <\/span> <\/div>","price_value":50,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1248\/"},{"id":"1263","name":"Lemon Meringue Tart","url":"https:\/\/dessertlady.ca\/lemon-meringue-tart","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/c\/l\/classic-lemon-meringue-tart.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1263\" data-price-box=\"product-id-1263\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1263']\" class=\"price-container price-final_price tax weee\"\n >
1919\n <span :id=\"$id('product\\u002Dprice\\u002D1263')\" data-price-amount=\"35\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 35.00<\/span><\/span>\n <\/span> <\/div>","price_value":35,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1263\/"},{"id":"1264","name":"Mango Cheesecake","url":"https:\/\/dessertlady.ca\/mango-cheesecake","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/m\/a\/mangocheesecake.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1264\" data-price-box=\"product-id-1264\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1264']\" class=\"price-container price-final_price tax weee\"\n >\n <span :id=\"$id('product\\u002Dprice\\u002D1264')\" data-price-amount=\"45\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 45.00<\/span><\/span>\n <\/span> <\/div>","price_value":45,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1264\/"},{"id":"1268","name":"Classic Cheesecake","url":"https:\/\/dessertlady.ca\/classic-cheesecake","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/c\/l\/classischeesecake.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1268\" data-price-box=\"product-id-1268\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1268']\" class=\"price-container price-final_price tax weee\"\n >\n <span :id=\"$id('product\\u002Dprice\\u002D1268')\" data-price-amount=\"45\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 45.00<\/span><\/span>\n <\/span> <\/div>","price_value":45,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1268\/"},{"id":"1274","name":"Chocolate Almond Biscotti (12 Pieces)","url":"https:\/\/dessertlady.ca\/12-chocolate-almond","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/c\/h\/chocolate-almond-biscotti_1.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1274\" data-price-box=\"product-id-1274\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1274']\" class=\"price-container price-final_price tax weee\"\n >\n <span :id=\"$id('product\\u002Dprice\\u002D1274')\" data-price-amount=\"13.5\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 13.50<\/span><\/span>\n <\/span> <\/div>","price_value":13.5,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1274\/"},{"id":"1275","name":"Double Chocolate Cranberry Espresso Biscotti (12 Pieces)","url":"https:\/\/dessertlady.ca\/12-double-choc-cran-espr","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/d\/o\/double-chocolate-cranberry-espresso-biscotti_1.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1275\" data-price-box=\"product-id-1275\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1275']\" class=\"price-container price-final_price tax weee\"\n >\n <span :id=\"$id('product\\u002Dprice\\u002D1275')\" data-price-amount=\"13.5\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 13.50<\/span><\/span>\n <\/span> <\/div>","price_value":13.5,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1275\/"},{"id":"1276","name":"White Pepper Walnuts (12 Pieces)","url":"https:\/\/dessertlady.ca\/12-white-pepper-walnuts","image_url":"https:\/\/dessertlady.ca\/media\/catalog\/product\/cache\/a626c2806a03bb248192a2b9378b94a8\/w\/h\/white-pepper-walnut-biscotti.jpg","price":"<div class=\"price-box price-final_price\" data-role=\"priceBox\" data-product-id=\"1276\" data-price-box=\"product-id-1276\"> <span\n x-data x-id=\"['product\\u002Dprice\\u002D1276']\" class=\"price-container price-final_price tax weee\"\n >
1919\n <span :id=\"$id('product\\u002Dprice\\u002D1276')\" data-price-amount=\"10\"\n data-price-type=\"finalPrice\"\n class=\"price-wrapper \"\n ><span class=\"price\">$ 10.00<\/span><\/span>\n <\/span> <\/div>","price_value":10,"add_to_cart_url":"https:\/\/dessertlady.ca\/checkout\/cart\/add\/product\/1276\/"}], 1920 showCrossSells: false, 1921 cartDrawerWidth: 480, // Default width 1922 pendingOpenAfterAddToCart: false, 1923 1924 isProductLoading(productId) { 1925 return this.loadingProductId === productId; 1926 }, 1927 1928 getCrossSellPosition() { 1929 // Calculate position based on cart drawer width 1930 return `right: ${this.cartDrawerWidth}px`; 1931 }, 1932 1933 getData(data) { 1934 if (data.cart) { 1935 this.cart = data.cart; 1936 this.itemsCount = data.cart.items && data.cart.items.length || 0; 1937 this.setCartItems(); 1938 // Set showCrossSells flag based on whether we have products 1939 this.showCrossSells = this.crossSellProducts && this.crossSellProducts.length > 0; 1940 1941 const cartCount = Number(data.cart.summary_count || this.itemsCount || 0); 1942 if ((this.pendingOpenAfterAddToCart || window.__dlPendingCartDrawerOpen) && cartCount > 0) { 1943 this.pendingOpenAfterAddToCart = false; 1944 window.__dlPendingCartDrawerOpen = false; 1945 this.openCartDrawer(); 1946 } 1947 } 1948 this.isLoading = false; 1949 }, 1950 cartItems: [], 1951 getItemCountTitle() { 1952 const visibleCount = Array.isArray(this.cartItems) ? this.cartItems.length : 0; 1953 1954 if (this.maxItemsToDisplay > 0 && this.itemsCount > visibleCount && visibleCount > 0) { 1955 return hyva.strf('Showing\u0020\u00250\u0020of\u0020\u00251\u0020products\u0020in\u0020cart', visibleCount, this.itemsCount); 1956 } 1957 1958 return hyva.strf('\u00250\u0020products\u0020in\u0020cart', this.itemsCount); 1959 }, 1960 setCartItems() { 1961 this.cartItems = this.cart.items && this.cart.items.sort((a, b) => b.item_id - a.item_id) || []; 1962 1963 if (this.maxItemsToDisplay > 0) { 1964 this.cartItems = this.cartItems.slice(0, parseInt(this.maxItemsToDisplay, 10)); 1965 } 1966 1967 // Process and fix custom options 1968 this.cartItems.forEach(item => { 1969 // Ensure custom options are properly structured 1970 if (item.options && Array.isArray(item.options)) { 1971 // Map over options to preserve file uploads 1972 item.options = item.options.map(option => { 1973 // If this is a file upload option, ensure it's properly formatted 1974 if (option.option_type === 'file' || 1975 (option.label && option.label.toLowerCase().includes('image')) || 1976 (option.value && typeof option.value === 'string' && option.value.includes('.jpg'))) { 1977 1978 // Preserve the file upload option 1979 } 1980 return option; 1981 }); 1982 } 1983 }); 1984 }, 1985 addCrossSellToCart(product) { 1986 this.isLoading = true; 1987 this.loadingProductId = product.id; 1988 const formKey = hyva.getFormKey(); 1989 const formData = new FormData(); 1990 formData.append('form_key', formKey); 1991 formData.append('product', product.id); 1992 formData.append('qty', 1); 1993 1994 fetch(product.add_to_cart_url, { 1995 method: 'POST', 1996 credentials: 'include', 1997 body: formData 1998 }) 1999 .then(response => { 2000 if (!response.ok && response.status !== 200 && !response.redirected) { 2001 throw new Error('Network response was not ok'); 2002 } 2003 2004 // For Magento, even some "successful" responses might not be valid JSON 2005 // First try to parse as JSON, but don't fail if it's not 2006 return response.text().then(text => { 2007 try { 2008 return JSON.parse(text); 2009 } catch (e) { 2010 // If it's not JSON but the status was OK, consider it a success 2011 if (response.ok || response.redirected) { 2012 return { success: true }; 2013 } 2014 throw new Error('Invalid response format'); 2015 } 2016 }); 2017 }) 2018 .then(data => { 2019 window.dispatchMessages && window.dispatchMessages([{ 2020 type: 'success', 2021 text: 'Item was added to your cart!' 2022 }], 5000); 2023 2024 // Refresh the minicart data 2025 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 2026 2027 // Wait a moment for the data to reload 2028 setTimeout(() => { 2029 this.isLoading = false;
2030 this.loadingProductId = null; 2031 }, 1000); 2032 }) 2033 .catch(error => { 2034 console.error('Error adding product to cart:', error); 2035 window.dispatchMessages && window.dispatchMessages([{ 2036 type: 'error', 2037 text: 'Could not add item to cart.' 2038 }]); 2039 2040 // Refresh cart data anyway, in case product was added despite the error 2041 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 2042 2043 setTimeout(() => { 2044 this.isLoading = false; 2045 this.loadingProductId = null; 2046 }, 1000); 2047 }); 2048 }, 2049 deleteItemFromCart(itemId) { 2050 this.isLoading = true; 2051 2052 const itemData = this.cart.items.filter((item) => item['item_id'] === itemId); 2053 const formKey = hyva.getFormKey(); 2054 const postUrl = BASE_URL + 'checkout/sidebar/removeItem/'; 2055 2056 fetch(postUrl, { 2057 "headers": { 2058 "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 2059 }, 2060 "body": "form_key=" + formKey + "&item_id=" + itemId, 2061 "method": "POST", 2062 "mode": "cors", 2063 "credentials": "include" 2064 }).then(response => { 2065 if (response.redirected) { 2066 window.location.href = response.url; 2067 } else if (response.ok) { 2068 return response.json(); 2069 } else { 2070 window.dispatchMessages && window.dispatchMessages([{ 2071 type: 'warning', 2072 text: 'Could\u0020not\u0020remove\u0020item\u0020from\u0020quote.' 2073 }]); 2074 this.isLoading = false; 2075 } 2076 }).then(result => { 2077 window.dispatchMessages && window.dispatchMessages([{ 2078 type: result.success ? 'success' : 'error', 2079 text: result.success 2080 ? 'You\u0020removed\u0020the\u0020item.' 2081 : result.error_message 2082 }], result.success ? 5000 : 0); 2083 2084 if (result.success && itemData) { 2085 window.dispatchEvent(new CustomEvent('cart-item-removed', { detail: itemData })); 2086 } 2087 2088 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 2089 }); 2090 }, 2091 scrollLock(use = true) { 2092 document.body.style.overflow = use ? "hidden" : ""; 2093 }, 2094 toggleCartDrawer(event) { 2095 // Default action is to open the cart (unless explicitly instructed to close) 2096 const shouldOpen = !event.detail || event.detail.isOpen !== false; 2097 2098 if (shouldOpen) { 2099 this.openCartDrawer(); 2100 } else { 2101 this.open = false; 2102 this.scrollLock(false); 2103 if (this.$refs && this.$refs.cartDialogContent) { 2104 hyva.releaseFocus(this.$refs.cartDialogContent); 2105 } 2106 } 2107 }, 2108 openCartDrawer() { 2109 this.open = true; 2110 this.scrollLock(true); 2111 // Make sure the showCrossSells flag is set based on whether we have products 2112 this.showCrossSells = this.crossSellProducts && this.crossSellProducts.length > 0; 2113 2114 // Wait for the drawer to render, then measure its width 2115 this.$nextTick(() => { 2116 if (this.$refs && this.$refs.cartDialogContent) { 2117 this.cartDrawerWidth = this.$refs.cartDialogContent.offsetWidth; 2118 hyva.trapFocus(this.$refs.cartDialogContent); 2119 } 2120 }); 2121 }, 2122 closeCartDrawer() { 2123 this.$dispatch('toggle-cart', { isOpen: false }); 2124 // Dispatch custom cart-closed event 2125 document.dispatchEvent(new CustomEvent('cart-closed')); 2126 }, 2127 getSectionDataExtraActions() { 2128 if (!this.cart.extra_actions) { 2129 return ''; 2130 } 2131 2132 const contentNode = document.createElement('div'); 2133 contentNode.innerHTML = this.cart.extra_actions; 2134 2135 hyva.activateScripts(contentNode); 2136 2137 return contentNode.innerHTML; 2138 } 2139 } 2140 } 2141 2142 function isAddToCartSuccessMessage(node) { 2143 return node 2144 && node.nodeType === 1 2145 && node.classList 2146 && node.classList.contains('message') 2147 && node.classList.contains('success') 2148 && node.textContent 2149 && node.textContent.toLowerCase().includes('shopping cart'); 2150 } 2151 2152 function markPendingCartDrawerOpen() { 2153 window.__dlPendingCartDrawerOpen = true; 2154 const cartDrawer = document.getElementById('cart-drawer'); 2155 if (cartDrawer && cartDrawer._x_dataStack && cartDrawer._x_dataStack[0]) { 2156 cartDrawer._x_dataStack[0].pendingOpenAfterAddToCart = true; 2157 } 2158 } 2159 2160 function noteAddToCartSuccessMessages(root) { 2161 if (!root || !root.querySelectorAll) { 2162 return false;
2163 } 2164 2165 return Array.from(root.querySelectorAll('.message.success')).some(isAddToCartSuccessMessage); 2166 } 2167 2168 document.addEventListener('DOMContentLoaded', function () { 2169 if (localStorage.getItem('cart_should_stay_closed') === 'true') { 2170 localStorage.removeItem('cart_should_stay_closed'); 2171 return; 2172 } 2173 2174 if (noteAddToCartSuccessMessages(document)) { 2175 markPendingCartDrawerOpen(); 2176 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 2177 } 2178 }); 2179 2180 const addToCartMessageObserver = new MutationObserver(function (mutations) { 2181 for (const mutation of mutations) { 2182 for (const node of mutation.addedNodes) { 2183 if (isAddToCartSuccessMessage(node) || noteAddToCartSuccessMessages(node)) { 2184 markPendingCartDrawerOpen(); 2185 window.dispatchEvent(new CustomEvent('reload-customer-section-data')); 2186 return; 2187 } 2188 } 2189 } 2190 }); 2191 2192 document.addEventListener('DOMContentLoaded', function () { 2193 addToCartMessageObserver.observe(document.body, { childList: true, subtree: true }); 2194 }); 2195 2196 window.addEventListener('pageshow', function (event) { 2197 if (event.persisted || (window.performance && window.performance.navigation.type === 1)) { 2198 localStorage.setItem('cart_should_stay_closed', 'true'); 2199 } 2200 }); 2201 2202 window.addEventListener('beforeunload', function () { 2203 if (window.location.pathname !== '/' && !window.location.pathname.includes('checkout/cart')) { 2204 localStorage.setItem('navigating_away', 'true'); 2205 } 2206 }); 2207 2208 if (window.location.pathname === '/' || window.location.pathname === '/index.php/') { 2209 if (localStorage.getItem('navigating_away') === 'true') { 2210 localStorage.setItem('cart_should_stay_closed', 'true'); 2211 localStorage.removeItem('navigating_away'); 2212 } 2213 } 2214</script>
2214 2215<section 2216 id="cart-drawer" 2217 x-data="initCartDrawer()" 2218 @private-content-loaded.window="getData($event.detail.data)" 2219 @toggle-cart.window="toggleCartDrawer($event)" 2220 @keydown.escape="closeCartDrawer" 2221 x-cloak 2222> 2223 <div role="dialog" 2224 aria-labelledby="cart-drawer-title" 2225 aria-modal="true" 2226 :aria-hidden="!open" 2227 class="fixed inset-0 z-30 overflow-hidden" 2228 x-show="open" 2229 x-cloak> 2230 <!-- Backdrop that covers the entire screen --> 2231 <div class="fixed inset-0 bg-black/50 transition-opacity" 2232 x-show="open" 2233 x-transition:enter="ease-in-out duration-500" 2234 x-transition:enter-start="opacity-0" 2235 x-transition:enter-end="opacity-100" 2236 x-transition:leave="ease-in-out duration-500" 2237 x-transition:leave-start="opacity-100" 2238 x-transition:leave-end="opacity-0" 2239 role="button" 2240 @click="closeCartDrawer" 2241 aria-label="Close minicart"></div> 2242 2243 <!-- Cross-sell sidebar - positioned directly to the left of the cart drawer --> 2244 <div class="fixed right-0 top-0 bottom-0 w-60 bg-white border-l border-gray-200 z-40 shadow-xl flex flex-col" 2245 :style="showCrossSells ? getCrossSellPosition() : 'right: 0'" 2246 x-cloak 2247 x-show="open && showCrossSells" 2248 x-transition:enter="ease-in-out duration-500" 2249 x-transition:enter-start="opacity-0 translate-x-full" 2250 x-transition:enter-end="opacity-100 translate-x-0" 2251 x-transition:leave="ease-in-out duration-500" 2252 x-transition:leave-start="opacity-100 translate-x-0" 2253 x-transition:leave-end="opacity-0 translate-x-full" 2254 style="min-width: 240px;"> 2255 2256 <div class="text-center bg-[#fffbe7] py-4 -mx-0 border-b border-gray-200 flex-shrink-0"> 2257 <h3 class="font-bold text-lg">Add a bit more sweetness to your cart</h3> 2258 </div> 2259 2260 <!-- Add a container with flex-grow and overflow for scrolling --> 2261 <div class="flex-grow overflow-y-auto" style="scrollbar-width: thin;"> 2262 <div class="px-4 py-3"> 2263 <template x-for="product in crossSellProducts" :key="product.id"> 2264 <div class="mb-6 pb-6 border-b border-gray-100 last:border-b-0"> 2265 <div class="flex items-center mb-3"> 2266 <a :href="product.url" class="w-1/3"> 2267 <img :src="product.image_url" :alt="product.name" class="w-full h-auto object-cover rounded" /> 2268 </a> 2269 <div class="w-2/3 pl-3"> 2270 <div class="font-semibold text-sm mb-1" x-text="product.name"></div> 2271 <div class="text-xs mb-2" x-html="product.price"></div> 2272 </div> 2273 </div> 2274 <button 2275 class="w-full py-2 px-2 bg-primary text-white text-xs rounded hover:bg-primary-dark transition font-semibold" 2276 @click.prevent="addCrossSellToCart(product)" 2277 :disabled="isProductLoading(product.id)" 2278 > 2279 <span x-show="!isProductLoading(product.id)">Add to Cart</span> 2280 <span x-show="isProductLoading(product.id)" class="flex items-center justify-center"> 2281 <svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> 2282 <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> 2283 <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> 2284 </svg> 2285 Adding... 2286 </span> 2287 </button> 2288 </div> 2289 </template> 2290 </div> 2291 </div> 2292 </div> 2293 <!-- End Cross-sell sidebar --> 2294
2295 <!-- Main cart drawer content - fixed to the right side --> 2296 <div class="fixed right-0 top-0 bottom-0 w-screen max-w-md shadow-2xl z-40 bg-white" 2297 x-cloak 2298 x-show="open" 2299 x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700" 2300 x-transition:enter-start="translate-x-full" 2301 x-transition:enter-end="translate-x-0" 2302 x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700" 2303 x-transition:leave-start="translate-x-0" 2304 x-transition:leave-end="translate-x-full" 2305 x-ref="cartDialogContent" 2306 role="region" 2307 :tabindex="open ? 0 : -1" 2308 aria-label="My Cart" 2309 > 2310 <div class="flex flex-col h-full max-h-screen bg-white text-black shadow-xl"> 2311 2312 <header class="relative px-4 py-6 sm:px-6"> 2313 <p id="cart-drawer-title" class="text-lg font-medium leading-7 text-gray-900"> 2314 <strong>My Cart</strong> 2315 <span class="items-total text-xs" 2316 x-show="maxItemsToDisplay && maxItemsToDisplay < itemsCount" 2317 x-text="getItemCountTitle()"> 2318 </span> 2319 </p> 2320 </header> 2321 2322 2323 <template x-if="!itemsCount"> 2324 <div class="relative px-4 py-6 bg-white border-bs sm:px-6 border-container"> 2325 Cart is empty </div> 2326 </template> 2327 2328 <template x-if="itemsCount"> 2329 <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"> 2330 <template x-for="item in cartItems" :key="item.item_id"> 2331 <div class="flex items-start p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 2332 <template x-if="item.product_has_url && item.is_visible_in_site_visibility"> 2333 <a :href="item.product_url" 2334 class="w-1/4 !ml-0" 2335 :aria-label="hyva.strf('Product\u0020\u0022\u00250\u0022', item.product_name)" 2336 > 2337 <img 2338 :src="item.product_image.src" 2339 :width="item.product_image.width" 2340 :height="item.product_image.height" 2341 loading="lazy" 2342 :alt="item.product_name" 2343 /> 2344 </a> 2345 </template> 2346 <template x-if="!item.product_has_url || !item.is_visible_in_site_visibility"> 2347 <div class="w-1/4 !ml-0"> 2348 <img 2349 :src="item.product_image.src" 2350 :width="item.product_image.width" 2351 :height="item.product_image.height" 2352 loading="lazy" 2353 :alt="item.product_name" 2354 /> 2355 </div> 2356 </template> 2357 <div class="w-3/4 space-y-2"> 2358 <div> 2359 <p class="text-xl"> 2360 <span x-html="item.qty"></span> x <span x-html="item.product_name"></span> 2361 </p> 2362 <p class="text-sm"><span x-html="item.product_sku"></span></p> 2363 </div> 2364 <template x-for="option in item.options"> 2365 <div class="pt-2"> 2366 <p class="font-semibold" x-text="option.label + ':'"></p> 2367 <!-- Handle different option types differently --> 2368 <template x-if="option.option_type === 'file' || (option.label && option.label.toLowerCase().includes('image')) || (option.value && typeof option.value === 'string' && option.value.includes('.jpg'))"> 2369 <div> 2370 <p class="text-gray-700 font-italic">Uploaded image: <span x-html="option.value"></span></p> 2371 <p class="text-xs text-gray-500">Image will be applied to your product</p> 2372 </div> 2373 </template> 2374 <template x-if="!(option.option_type === 'file' || (option.label && option.label.toLowerCase().includes('image')) || (option.value && typeof option.value === 'string' && option.value.includes('.jpg')))"> 2375 <p class="text-gray-700" x-html="option.value"></p> 2376 </template> 2377 </div> 2378 </template> 2379 <p><span x-html="item.product_price"></span></p> 2380 <div class="pt-4"> 2381 <a :href="item.configure_url" 2382 x-show="item.product_type !== 'grouped' && item.is_visible_in_site_visibility" 2383 class="inline-flex p-2 mr-2 btn btn-primary" 2384 :aria-label="hyva.strf('Edit\u0020product\u
23840020\u0022\u00250\u0022', item.product_name)" 2385 > 2386 <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"> 2387 <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"/> 2388</svg> 2389 </a> 2390 <button type="button" 2391 class="inline-flex p-2 btn btn-primary" 2392 @click="deleteItemFromCart(item.item_id)" 2393 :aria-label="hyva.strf('Remove\u0020product\u0020\u0022\u00250\u0022\u0020from\u0020cart', item.product_name)" 2394 > 2395 <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"> 2396 <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"/> 2397</svg> 2398 </button> 2399 </div> 2400 2401 </div> 2402 </div> 2403 </template> 2404 </div> 2405 </template> 2406 2407 <template x-if="itemsCount"> 2408 <div> 2409 2410 <div class="relative grid gap-6 sm:gap-8 py-3 px-1 sm:px-3 bg-white"> 2411 <div class="w-full p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 2412 <p>Subtotal: <span x-html="cart.subtotal"></span></p> 2413 </div> 2414 <div class="w-full p-3 space-x-4 transition duration-150 ease-in-out rounded-lg hover:bg-gray-100"> 2415 <a @click.stop="closeCartDrawer; $dispatch('toggle-authentication', 2416 {url: 'https://dessertlady.ca/checkout/'});" 2417 href="https://dessertlady.ca/checkout/" 2418 class="inline-flex btn btn-primary rcp-minicart-checkout" 2419 > 2420 Checkout </a> 2421 <span>or</span> 2422 <a href="https://dessertlady.ca/checkout/cart/" 2423 class="underline" 2424 > 2425 View and Edit Cart </a> 2426 </div> 2427 <div x-html="getSectionDataExtraActions()"></div> 2428 </div> 2429 </div> 2430 </template> 2431 2432 </div> 2433 2434 <button 2435 type="button" 2436 @click="closeCartDrawer" 2437 aria-label="Close minicart" 2438 class="absolute top-0 right-2 p-4 mt-2 text-gray-300 transition-colors hover:text-black z-50" 2439 > 2440 <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"> 2441 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 2442</svg> 2443 </button> 2444 </div> 2445 <div 2446 class="z-50 fixed inset-0 flex justify-center items-center bg-white/70 select-none" 2447 x-show="isLoading" 2448 x-cloak 2449 x-transition.opacity.duration.200ms 2450> 2451 <div class="flex items-center"> 2452 <svg xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" class="animate-spin" aria-hidden="true"> 2453 <circle style="opacity: .3;" cx="12" cy="12" r="10"/> 2454 <path fill="none" stroke-width="2" d="M12,2 a10,10 0 0,1 10,10"/> 2455</svg> 2456 <div class="ml-10 text-xl"> 2457 Loading... </div> 2458 </div> 2459</div> </div> 2460 2461</section>
2461<script> 2462 function initAuthentication() { 2463 return { 2464 open: false, 2465 close() { 2466 this.open = false; 2467 }, 2468 forceAuthentication: false, 2469 checkoutUrl: 'https://dessertlady.ca/checkout/index/', 2470 errors: 0, 2471 hasCaptchaToken: 0, 2472 displayErrorMessage: false, 2473 errorMessages: [], 2474 setErrorMessages(message) { 2475 this.errorMessages = [message]; 2476 this.displayErrorMessage = true; 2477 }, 2478 submitForm() { 2479 // Do not rename $form, the variable is expected to be declared in the recaptcha output 2480 const $form = document.querySelector('#login-form'); 2481 if (!this.hasCaptchaToken) { 2482 this.errors = 1; 2483 2484 2485 const executeGreCaptcha = () => { 2486 grecaptcha.ready(() => { 2487 grecaptcha.execute( 2488 '6Lc4bWMtAAAAAO0UU2tu07Hp_4JXvRSooTCss9Yb', 2489 {action: 'submit'} 2490 ).then((token) => { 2491 const tokenElement = $form['g\u002Drecaptcha\u002Dresponse']; 2492 2493 if (tokenElement && token && token.length > 0) { 2494 $form['g\u002Drecaptcha\u002Dresponse'].value = token; 2495 this.errors = 0; 2496 this.hasCaptchaToken = 1; 2497 (this.submitForm || $form.submit).call(this); 2498 } else { 2499 this.errors = 1; 2500 const error = tokenElement 2501 ? 'ReCaptcha\u0020validation\u0020failed,\u0020please\u0020try\u0020again' 2502 : 'ReCaptcha\u0020token\u0020element\u0020is\u0020missing.\u0020Please\u0020refresh\u0020the\u0020page\u0020and\u0020trying\u0020again.'; 2503 if (this.setErrorMessages) { 2504 this.setErrorMessages([error]) 2505 this.displayErrorMessage = true; 2506 } else { 2507 window.dispatchMessages && window.dispatchMessages([{type: "error", text: error}], 5000); 2508 } 2509 } 2510 }).catch(exception => { 2511 console.error(exception || `An unknown error occurred during ReCaptcha validation. Are the tokens and the domain correctly set up in the Google console?`) 2512 }); 2513 }) 2514 } 2515 2516 if (window.grecaptcha) { 2517 executeGreCaptcha(); 2518 } else { 2519 const recaptchaUrl = "https://www.google.com/recaptcha/api.js?render=6Lc4bWMtAAAAAO0UU2tu07Hp_4JXvRSooTCss9Yb"; 2520 const script = document.createElement('script'); 2521 script.setAttribute('type', 'text/javascript'); 2522 script.setAttribute('src', recaptchaUrl); 2523 document.head.appendChild(script); 2524 2525 script.addEventListener("load", executeGreCaptcha, false); 2526 } 2527 } 2528 2529 if (this.errors === 0) { 2530 this.dispatchLoginRequest($form); 2531 } 2532 }, 2533 onPrivateContentLoaded() { 2534 const data = this.$event.detail.data; 2535 const isLoggedIn = data.customer && data.customer.firstname; 2536 if (data.cart && !isLoggedIn) { 2537 this.forceAuthentication = !data.cart.isGuestCheckoutAllowed; 2538 } 2539 }, 2540 redirectIfAuthenticated() { 2541 const event = this.$event; 2542 this.open = this.forceAuthentication; 2543 2544 if (event.detail && event.detail.url) { 2545 this.checkoutUrl = event.detail.url; 2546 } 2547 if (!this.forceAuthentication) { 2548 window.location.href = this.checkoutUrl; 2549 } 2550 }, 2551 resetErrors() { 2552 this.errors = 0; 2553 }, 2554 dispatchLoginRequest(form) { 2555 this.isLoading = true; 2556 const username = this.$refs['customer-email'].value; 2557 const password = this.$refs['customer-password'].value; 2558 const formKey = hyva.getFormKey(); 2559 const bodyFields = { 2560 'username': username, 2561 'password': password, 2562 'formKey': formKey 2563 }; 2564 const fieldName = 'g\u002Drecaptcha\u002Dresponse'; 2565 const recaptchaField = fieldName && form[fieldName]; 2566 if (recaptchaField) { 2567 bodyFields[fieldName] = recaptchaField.value; 2568 } 2569 fetch('https://dessertlady.ca/customer/ajax/login/', { 2570 method: 'POST', 2571 headers: { 2572 'Content-Type': 'application/json', 2573 'X-Requested-With': 'XMLHttpRequest' 2574 }, 2575 body: JSON.stringify(bodyFields) 2576 } 2577 ).then(response => { 2578 return response.json() 2579 } 2580 ).then(data => { 2581 this.isLoading = false;
2582 if (data.errors) { 2583 this.setErrorMessages(data.message); 2584 this.errors = 1; 2585 this.hasCaptchaToken = 0; 2586 } else { 2587 window.location.href = this.checkoutUrl; 2588 } 2589 }); 2590 }, 2591 eventListeners: { 2592 ['@private-content-loaded.window']() { 2593 this.onPrivateContentLoaded(); 2594 }, 2595 ['@toggle-authentication.window']() { 2596 this.redirectIfAuthenticated(); 2597 }, 2598 ['@pageshow.window'](event) { 2599 if (event.persisted) { 2600 this.close(); 2601 }; 2602 } 2603 } 2604 } 2605 } 2606 2607 window.addEventListener('alpine:init', () => Alpine.data('initAuthentication', initAuthentication), {once: true}) 2608</script>
2608 2609 2610<dialog 2611 id="authentication-popup" 2612 aria-labelledby="authentication-popup-title" 2613 class="open:flex flex-col transition ease-out duration-300 motion-reduce:duration-0 p-0" 2614 x-data="initAuthentication" 2615 x-bind="eventListeners" 2616 x-show="open" 2617 x-htmldialog="close" 2618 closeby="any" 2619 x-transition 2620> 2621 <div class="pt-8 px-8"> 2622 <div class="flex gap-2 justify-between items-center"> 2623 <p id="authentication-popup-title" class="text-xl"> 2624 <strong>Customer Login</strong> 2625 </p> 2626 <button 2627 type="button" 2628 class="btn bg-transparent border-transparent p-2" 2629 @click="close" 2630 aria-label="Close panel" 2631 > 2632 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg> 2633 </button> 2634 </div> 2635 2636 <div class="message error" x-show="errors" x-cloak> 2637 <template x-for="(message, index) in errorMessages" :key="index"> 2638 <div> 2639 <template x-if="message"> 2640 <span x-html="message"></span> 2641 </template> 2642 </div> 2643 </template> 2644 </div> 2645 </div> 2646 2647 <div class="block-customer-login p-8"> 2648 <p id="authenticate-customer-login" class="text-lg mb-2"> 2649 <strong>Checkout using your account</strong> 2650 </p> 2651 2652 <form 2653 id="login-form" 2654 class="form form-login" 2655 method="post" 2656 @submit.prevent="submitForm" 2657 > 2658 <div class="control"> 2659 <input type="hidden" 2660 value="" 2661 name="g-recaptcha-response" 2662 > 2663</div> <input name="context" type="hidden" value="checkout"> 2664 2665 <div class="flex flex-col gap-4"> 2666 <div class="field email required"> 2667 <label for="form-login-username"> 2668 <span>Email Address</span> 2669 </label> 2670 <div class="control"> 2671 <input 2672 id="form-login-username" 2673 name="username" 2674 type="email" 2675 class="form-input w-full" 2676 required 2677 autocomplete="off" 2678 x-ref="customer-email" 2679 @change="resetErrors" 2680 > 2681 </div> 2682 </div> 2683 <div class="field password required"> 2684 <label for="form-login-password"> 2685 <span>Password</span> 2686 </label> 2687 <div class="control"> 2688 <input 2689 id="form-login-password" 2690 name="password" 2691 type="password" 2692 class="form-input w-full" 2693 required 2694 autocomplete="off" 2695 x-ref="customer-password" 2696 @change="resetErrors" 2697 > 2698 </div> 2699 </div> 2700 </div> 2701 2702 <div class="actions-toolbar"> 2703 <button 2704 type="submit" 2705 class="btn btn-primary" 2706 > 2707 Sign In </button> 2708 <a 2709 href="https://dessertlady.ca/customer/account/forgotpassword/" 2710 class="underline hover:no-underline" 2711 > 2712 Forgot Your Password? </a> 2713 </div> 2714 </form> 2715 <p class="prose my-4 text-sm"> 2716 <em>This form is protected by reCAPTCHA - the <a class="underline" href="https://policies.google.com/privacy" target="_blank" rel="noopener">Google Privacy Policy</a> and <a class="underline" href="https://policies.google.com/terms" target="_blank" rel="noopener">Terms of Service</a> apply.</em> 2717</p> </div> 2718 <div class="block-new-customer p-8 border-t border-gray-300">
2719 <p id="authenticate-new-customer" class="text-lg mb-2"> 2720 <strong>Checkout as a new customer</strong> 2721 </p> 2722 2723 <div class="block-content"> 2724 <p class="mb-1"> 2725 Creating an account has many benefits: </p> 2726 <ul class="list-disc pl-5"> 2727 <li> See order and shipping status</li> 2728 <li> Track order history</li> 2729 <li> Check out faster</li> 2730 </ul> 2731 <div class="actions-toolbar mt-6 mb-2"> 2732 <a href="https://dessertlady.ca/customer/account/create/" 2733 class="inline-flex btn btn-primary"> 2734 Create an Account </a> 2735 </div> 2736 </div> 2737 </div> 2738</dialog></div></header><main id="maincontent" class="page-main-full-width"><div id="contentarea" tabindex="-1"></div><div class="page messages">
2738<script> 2739 function initMessages() { 2740 "use strict"; 2741 return { 2742 messages: window.mageMessages || [], 2743 isEmpty() { 2744 return this.messages.reduce( 2745 function (isEmpty, message) { 2746 return isEmpty && message === undefined 2747 }, true 2748 ) 2749 }, 2750 hasMessages() { 2751 return !this.isEmpty(); 2752 }, 2753 hasMessage() { 2754 return !!this.message; 2755 }, 2756 removeMessage() { 2757 this.messages[this.index] = undefined; 2758 }, 2759 addMessages(messages, hideAfter) { 2760 messages.map((message) => { 2761 if (!message.text || !message.text.trim()) return; 2762 this.messages = this.messages.concat(message); 2763 if (hideAfter === undefined && message.type === 'success' && window.defaultSuccessMessageTimeout) { 2764 hideAfter = window.defaultSuccessMessageTimeout; 2765 } 2766 if (hideAfter) { 2767 this.setHideTimeOut(this.messages.length -1, hideAfter); 2768 } 2769 }); 2770 }, 2771 setHideTimeOut(messageIndex, hideAfter) { 2772 setTimeout((messageIndex) => { 2773 this.messages[messageIndex] = undefined; 2774 }, hideAfter, messageIndex); 2775 }, 2776 eventListeners: { 2777 ['@messages-loaded.window'](event) { 2778 this.addMessages(event.detail.messages, event.detail.hideAfter) 2779 }, 2780 ['@private-content-loaded.window'](event) { 2781 const data = event.detail.data; 2782 if ( 2783 data.messages && 2784 data.messages.messages && 2785 data.messages.messages.length 2786 ) { 2787 this.addMessages(data.messages.messages); 2788 } 2789 }, 2790 ['@clear-messages.window']() { 2791 this.messages = []; 2792 }, 2793 ['@pageshow.window'](event) { 2794 if (event.persisted) { 2795 this.messages = []; 2796 }; 2797 } 2798 }, 2799 getMessageUiId() { 2800 return 'message-' + this.message.type; 2801 } 2802 } 2803 } 2804 2805 window.addEventListener('alpine:init', () => Alpine.data('initMessages', initMessages), {once: true}) 2806</script>
2806 2807<section id="messages" 2808 x-data="initMessages" 2809 x-bind="eventListeners" 2810 role="alert" 2811> 2812 <template x-if="hasMessages"> 2813 <div class="w-full"> 2814 <div class="messages container mx-auto py-3"> 2815 <template x-for="(message, index) in messages" :key="index"> 2816 <div> 2817 <template x-if="hasMessage"> 2818 <div class="message" 2819 :class="message.type" 2820 :ui-id="getMessageUiId" 2821 > 2822 <span x-html="message.text"></span> 2823 <button 2824 type="button" 2825 class="text-gray-600 hover:text-black" 2826 aria-label="Close message" 2827 @click.prevent="removeMessage" 2828 > 2829 <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"> 2830 <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/> 2831</svg> 2832 </button> 2833 </div> 2834 </template> 2835 </div> 2836 </template> 2837 </div> 2838 </div> 2839 </template> 2840</section></div><div class="columns"><div class="column main">
2840<script src="https://dessertlady.ca/livechat/getvisitor/"></script>
2840<script> 2841 window.__lc = window.__lc || {}; 2842 window.__lc.integration_name = "magento"; 2843 window.__lc.product_name = "livechat"; 2844 window.__lc.license = 19062883; 2845 if(livechat_visitor_data) { 2846 window.__lc.visitor = livechat_visitor_data; 2847 } 2848 2849 ;(function(n,t,c){ 2850 function i(n){ 2851 return e._h?e._h.apply(null,n):e._q.push(n) 2852 } 2853 var e={ 2854 _q:[], 2855 _h:null, 2856 _v:"2.0", 2857 on:function(){ 2858 i(["on",c.call(arguments)]) 2859 }, 2860 once:function(){ 2861 i(["once",c.call(arguments)]) 2862 }, 2863 off:function(){ 2864 i(["off",c.call(arguments)]) 2865 }, 2866 get:function(){ 2867 if(!e._h)throw new Error("[LiveChatWidget] You can't use getters before load."); 2868 return i(["get",c.call(arguments)]) 2869 }, 2870 call:function(){ 2871 i(["call",c.call(arguments)]) 2872 }, 2873 init:function(){ 2874 var n=t.createElement("script"); 2875 n.async=!0,n.type="text/javascript", 2876 n.src="https://cdn.livechatinc.com/tracking.js", 2877 t.head.appendChild(n) 2878 } 2879 }; 2880 !n.__lc.asyncInit&&e.init(),n.LiveChatWidget=n.LiveChatWidget||e 2881 }(window,document,[].slice)) 2882 2883 var LC_API = LC_API || {}; 2884 LC_API.on_after_load = function() { 2885 require(['Magento_Customer/js/customer-data'], function (customerData) { 2886 var cart = customerData.get('cart'); 2887 var count = cart().summary_count; 2888 2889 if(localStorage) { 2890 var storageCount = localStorage.getItem('lcCount'); 2891 if(parseInt(storageCount) !== count) { 2892 getCart(); 2893 } 2894 localStorage.setItem('lcCount', count); 2895 } 2896 2897 var count = cart().summary_count; 2898 cart.subscribe(function () { 2899 if (cart().summary_count !== count) { 2900 getCart(); 2901 if(localStorage) { 2902 localStorage.setItem('lcCount', cart().summary_count); 2903 } 2904 } 2905 }); 2906 }); 2907 } 2908 2909 function getCart() { 2910 var xhttp = new XMLHttpRequest(); 2911 xhttp.onreadystatechange = function() { 2912 if (this.readyState == 4 && this.status == 200) { 2913 var customVariables = JSON.parse(this.responseText); 2914 2915 if (customVariables.length <= 0) { 2916 customVariables = [{"name":"Cart", "value":"customer's cart is empty"}]; 2917 } 2918 2919 LC_API.set_custom_variables(customVariables); 2920 } 2921 }; 2922 xhttp.open( 2923 "GET", 2924 "https://dessertlady.ca/livechat/getcart/" + 2925 "?success=0", 2926 true 2927 ); 2928 xhttp.send(); 2929 } 2930 2931 </script>
2931 2932<section> 2933 <div class="container mx-auto px-4"> 2934 <link rel="stylesheet" type="text/css" media="all" href="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_BannerSlider/css/slider.css"> <!-- Load Swiper.js if not already loaded -->
2934<script> 2935 (function() { 2936 const dispatchEvent = () => { 2937 if (typeof Swiper !== 'undefined') { 2938 window.dispatchEvent(new CustomEvent('amswiper-loaded')); 2939 } 2940 }; 2941 2942 2943 if (!document.querySelector('link[href*="swiper-bundle.min.css"]')) { 2944 const swiperLink = document.createElement('link'); 2945 swiperLink.rel = 'stylesheet'; 2946 swiperLink.href = 'https\u003A\u002F\u002Fdessertlady.ca\u002Fstatic\u002Fversion1791040052\u002Ffrontend\u002FHyva\u002FDessertlady\u002Fen_CA\u002FToweringmedia_BannerSlider\u002Fjs\u002Fvendor\u002Fswiper\u002Fswiper\u002Dbundle.min.css'; 2947 document.head.appendChild(swiperLink); 2948 } 2949 2950 if (typeof Swiper !== 'undefined') { 2951 2952 if (document.readyState === 'loading') { 2953 document.addEventListener('DOMContentLoaded', dispatchEvent); 2954 } else { 2955 2956 setTimeout(dispatchEvent, 100); 2957 } 2958 return; 2959 } 2960 2961 const swiperScript = document.createElement('script'); 2962 swiperScript.src = 'https\u003A\u002F\u002Fdessertlady.ca\u002Fstatic\u002Fversion1791040052\u002Ffrontend\u002F
2962Hyva\u002FDessertlady\u002Fen_CA\u002FToweringmedia_BannerSlider\u002Fjs\u002Fvendor\u002Fswiper\u002Fswiper\u002Dbundle.min.js'; 2963 swiperScript.async = false; 2964 swiperScript.onload = () => { 2965 2966 if (document.readyState === 'loading') { 2967 document.addEventListener('DOMContentLoaded', dispatchEvent); 2968 } else { 2969 setTimeout(dispatchEvent, 100); 2970 } 2971 }; 2972 document.head.appendChild(swiperScript); 2973 })();</script>
2973<script> 2974 function initSwiperbannerslider6ac74c9dd9d0a() { 2975 return new Swiper('#banner\u002Dslider\u002D6ac74c9dd9d0a .swiper-container', { 2976 on: { 2977 init(swiper) { 2978 document.querySelector('#banner\u002Dslider\u002D6ac74c9dd9d0a')?.classList.add('is-initialized'); 2979 this.updateAutoHeight(0); 2980 } 2981 }, 2982 init: false, 2983 slidesPerView: 1, 2984 observer: true, 2985 observeParents: true, 2986 autoHeight: true, 2987 loop: true, 2988 rewind: false, 2989 loopAdditionalSlides: 3, 2990 speed: 500, 2991 autoplay: { 2992 delay: 5000, 2993 disableOnInteraction: false, 2994 stopOnLastSlide: false, 2995 waitForTransition: true, 2996 pauseOnMouseEnter: true }, 2997 spaceBetween: 10, 2998 effect: 'fade', 2999 fadeEffect: { 3000 crossFade: true 3001 }, 3002 pagination: { 3003 el: '#banner\u002Dslider\u002D6ac74c9dd9d0a .swiper-pagination', 3004 clickable: true, 3005 bulletActiveClass: 'swiper-pagination-bullet-active', 3006 bulletClass: 'swiper-pagination-bullet' 3007 }, 3008 navigation: { 3009 nextEl: '#banner\u002Dslider\u002D6ac74c9dd9d0a .swiper-button-next', 3010 prevEl: '#banner\u002Dslider\u002D6ac74c9dd9d0a .swiper-button-prev' 3011 }, 3012 touchEventsTarget: 'container', 3013 simulateTouch: true, 3014 allowTouchMove: true, 3015 grabCursor: true 3016 }); 3017 } 3018 3019 function runSwiperbannerslider6ac74c9dd9d0a() { 3020 return { 3021 sliderId: 'banner\u002Dslider\u002D6ac74c9dd9d0a', 3022 swiperInstance: null, 3023 initAttempts: 0, 3024 maxInitAttempts: 40, 3025 3026 destroy() { 3027 if (this.swiperInstance) { 3028 this.swiperInstance.destroy(true, true); 3029 this.swiperInstance = null; 3030 } 3031 }, 3032 3033 retrieveDatabannerslider6ac74c9dd9d0a() { 3034 if (this.swiperInstance) { 3035 return; 3036 } 3037 3038 const self = this; 3039 const root = document.getElementById(this.sliderId); 3040 3041 const checkAndInit = () => { 3042 if (self.swiperInstance) { 3043 return; 3044 } 3045 3046 if (typeof Swiper === 'undefined') { 3047 if (self.initAttempts++ < self.maxInitAttempts) { 3048 setTimeout(checkAndInit, 50); 3049 } 3050 return; 3051 } 3052 3053 const container = root?.querySelector('.swiper-container'); 3054 if (!container) { 3055 if (self.initAttempts++ < self.maxInitAttempts) { 3056 setTimeout(checkAndInit, 100); 3057 } 3058 return; 3059 } 3060 3061 const slides = container.querySelectorAll('.swiper-wrapper .swiper-slide'); 3062 if (slides.length === 0) { 3063 if (self.initAttempts++ < self.maxInitAttempts) { 3064 setTimeout(checkAndInit, 100); 3065 } 3066 return; 3067 } 3068 3069 let hasImage = false; 3070 for (let i = 0; i < slides.length; i++) { 3071 const img = slides[i].querySelector('img'); 3072 if (img && img.src && img.src !== window.location.href && !img.src.endsWith('/')) { 3073 hasImage = true; 3074 break; 3075 } 3076 } 3077 if (!hasImage) {
3078 if (self.initAttempts++ < self.maxInitAttempts) { 3079 setTimeout(checkAndInit, 100); 3080 } 3081 return; 3082 } 3083 3084 if (self.swiperInstance) { 3085 self.swiperInstance.destroy(true, true); 3086 self.swiperInstance = null; 3087 } 3088 3089 const swiperInstance = initSwiperbannerslider6ac74c9dd9d0a(); 3090 swiperInstance.init(); 3091 self.swiperInstance = swiperInstance; 3092 root.swiper = swiperInstance; 3093 container.swiper = swiperInstance; 3094 3095 if (swiperInstance.autoplay) { 3096 swiperInstance.autoplay.start(); 3097 } 3098 3099 swiperInstance.on('autoplayStop', () => { 3100 if (swiperInstance.params.autoplay && swiperInstance.autoplay && !swiperInstance.autoplay.running) { 3101 swiperInstance.autoplay.start(); 3102 } 3103 }); 3104 }; 3105 3106 checkAndInit(); 3107 } 3108 }; 3109 }</script>
3109 <div class="toweringmedia-banner-slider hyva-slider relative effect-fade -alignment-left" id="banner-slider-6ac74c9dd9d0a" x-data="runSwiperbannerslider6ac74c9dd9d0a()" x-init="retrieveDatabannerslider6ac74c9dd9d0a()" role="region" aria-label="Banner Slider" aria-live="polite"><div class="swiper-container swiper"><div class="swiper-wrapper"> <div class="swiper-slide" data-banner-index="0" data-banner-id="7" data-tm-analytics='{"sliderId":"banner-slider-6ac74c9dd9d0a","bannerId":"7","viewUrl":"https:\/\/dessertlady.ca\/toweringmedia_banner\/analytic\/view\/","clickUrl":"https:\/\/dessertlady.ca\/toweringmedia_banner\/analytic\/click\/"}'><a href="/thanksgiving-dinner-for-2" target="_self" class="block relative"><picture class="block"> <source media="(max-width: 768px)" type="image/webp" srcset="https://dessertlady.ca/media/toweringmedia/banner/thanksgiving-2026-819x1024.webp"> <source media="(max-width: 768px)" srcset="https://dessertlady.ca/media/toweringmedia/banner/thanksgiving-2026-819x1024.webp"> <source type="image/webp" srcset="https://dessertlady.ca/media/toweringmedia/banner/thanksgiving-2026-2048x682.webp"> <img src="https://dessertlady.ca/media/toweringmedia/banner/thanksgiving-2026-2048x682.webp" alt="Dessert Lady Thanksgiving dinner — order in advance" class="w-full h-auto block" loading="eager" fetchpriority="high" width="2048" height="682" decoding="async"></picture></a></div> <div class="swiper-slide" data-banner-index="1" data-banner-id="5" data-tm-analytics='{"sliderId":"banner-slider-6ac74c9dd9d0a","bannerId":"5","viewUrl":"https:\/\/dessertlady.ca\/toweringmedia_banner\/analytic\/view\/","clickUrl":"https:\/\/dessertlady.ca\/toweringmedia_banner\/analytic\/click\/"}'><a href="/cakes" target="_self" class="block relative"><picture class="block"> <source media="(max-width: 768px)" type="image/webp" srcset="https://dessertlady.ca/media/toweringmedia/banner/dessert-lady-toronto-bakery-cakes-cookies-delivery-mobile-banner-800x1000-v20260821.webp"> <source media="(max-width: 768px)" srcset="https://dessertlady.ca/media/toweringmedia/banner/dessert-lady-toronto-bakery-cakes-cookies-delivery-mobile-banner-800x1000-v20260821.webp"> <source type="image/webp" srcset="https://dessertlady.ca/media/toweringmedia/banner/dessert-lady-toronto-bakery-cakes-cookies-delivery-banner-1600x533-v20260821.webp"> <img src="https://dessertlady.ca/media/toweringmedia/banner/dessert-lady-toronto-bakery-cakes-cookies-delivery-banner-1600x533-v20260821.webp" alt="Dessert Lady Toronto bakery — cakes, cookies and delivery. Order today." class="w-full h-auto block" loading="lazy" width="1600" height="533" decoding="async"></picture></a></div> <div class="swiper-slide" data-banner-index="2" data-banner-id="6" data-tm-analytics='{"sliderId":"banner-slider-6ac74c9dd9d0a","bannerId":"6","viewUrl":"https:\/\/dessertlady.ca\/toweringmedia_banner\/analytic\/view\/","clickUrl":"https:\/\/dessertlady.ca\/toweringmedia_banner\/analytic\/click\/"}'><a href="/strawberry-shortcake" target="_self" class="block relative"><picture class="block"> <source media="(max-width: 768px)" type="image/webp" srcset="https://dessertlady.ca/media/toweringmedia/banner/dessert-lady-toronto-strawberry-shortcake-mobile-banner-800x1000-v20260821.webp"> <source media="(max-width: 768px)" srcset="https://dessertlady.ca/media/toweringmedia/banner/dessert-lady-toronto-strawberry-shortcake-mobile-banner-800x1000-v20260821.webp"> <source type="image/webp" srcset="https://dessertlady.ca/media/toweringmedia/banner/dessert-lady-toronto-strawberry-shortcake-banner-1600x533-v20260821.webp"> <img src="https://dessertlady.ca/media/toweringmedia/banner/dessert-lady-toronto-strawberry-shortcake-banner-1600x533-v20260821.webp" alt="Dessert Lady Toronto strawberry shortcake — order online." class="w-full h-auto block" loading="lazy" width="1600" height="533" decoding="async"></picture></a></div></div> <button class="swiper-button-prev swiper-button-white" aria-label="Previous slide" aria-controls="banner-slider-6ac74c9dd9d0a"></button> <button class="swiper-button-next swiper-button-white" aria-label="Next slide" aria-controls="banner-slider-6ac74c9dd9d0a"></button> <div class="swiper-pagination"></div></div></div><!-- Load Analytics Tracking JS for Hyvä -->
3109<script src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_BannerSlider/js/hyva-banner-analytic.js"></script>
3109 3110 </div> 3111</section> 3112 3113 3114<!-- 3115<section class="toronto_background_3 py-12 my-12 sm:my-2"> 3116 <div class="max-w-6xl mx-auto px-4"> 3117 3118 <div class="flex flex-col space-y-8 items-center text-center 3119 md:flex-row md:justify-between md:space-y-0 md:items-start md:text-left"> 3120 3121 3122 <div class="w-full md:w-3/5 md:pr-8"> 3123 <h2 class="text-3xl sm:text-base md:text-4xl lg:text-5xl uppercase font-bold text-gray-800 mb-4 font-toronto"> 3124 Celebrate Valentineâs Day with Dessert Ladyâs Cutie Cakes & Cupcakes 3125 </h2> 3126 <p class="mb-6"> 3127 Available for pre-order only with pickup or delivery, these limited-edition cakes and cupcakes are made in small batches and sell out quickly. Order early to make sure your Valentine gets something truly sweet. 3128 </p> 3129 3130 3131<button type="button" class="py-3 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-2xs hover:bg-gray-50 focus:outline-hidden focus:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700"> 3132 <a href="/valentines-day">Valentineâs Day Treats</a> 3133</button> 3134 3135 </div> 3136 3137 3138 <div class="w-full md:w-1/3 mt-8 md:mt-0 md:ml-auto toronto_image mx-auto md:mx-0" 3139 x-data="{ 3140 currentIndex:0, 3141 images:[ 3142 '/media/homepagedessert/dessert_lady_toronto_valentines_day_cupcakes_2.jpg', 3143 '/media/homepagedessert/dessert_lady_toronto_valentines_day_cupcakes.jpg', 3144'/media/homepagedessert/dessert_lady_toronto_valentines_day_bakery_goods.jpg', 3145'/media/homepagedessert/heart_shape_strawberry_shortcaketoronto_dessert_lady.jpg', 3146'/media/homepagedessert/dessert_lady_toronto_heart_shaped_cake_red.jpg' 3147 ], 3148 next(){ this.currentIndex=(this.currentIndex+1)%this.images.length; }, 3149 prev(){ this.currentIndex=(this.currentIndex-1+this.images.length)%this.images.length; }, 3150 init(){ setInterval(()=>this.next(),5000); } 3151 }"> 3152 <div class="relative pb-[75%] h-0 overflow-hidden rounded-lg shadow-md"> 3153 <template x-for="(image,index) in images" :key="index"> 3154 <div 3155 :style="` 3156 background-image: url('${image}'); 3157 background-size: cover; 3158 background-position: center; 3159 position: absolute; top:0; left:0; 3160 width:100%; height:100%; 3161 opacity: ${currentIndex===index?1:0}; 3162 z-index: ${currentIndex===index?10:0}; 3163 transition: opacity 1s ease; 3164 `"> 3165 </div> 3166 </template> 3167 <button @click="prev" 3168 class="absolute left-2 top-1/2 transform -translate-y-1/2 bg-white/70 3169 rounded-full p-2 shadow-md z-20"> 3170 3171 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" 3172 stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> 3173 <path stroke-linecap="round" stroke-linejoin="round" 3174 d="M15.75 19.5L8.25 12l7.5-7.5" /> 3175 </svg> 3176 </button> 3177 <button @click="next" 3178 class="absolute right-2 top-1/2 transform -translate-y-1/2 bg-white/70 3179 rounded-full p-2 shadow-md z-20"> 3180 3181 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" 3182 stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> 3183 <path stroke-linecap="round" stroke-linejoin="round" 3184 d="M8.25 4.5l7.5 7.5-7.5 7.5" /> 3185 </svg> 3186 </button> 3187 <div class="absolute bottom-2 left-1/2 transform -translate-x-1/2 flex space-x-2 z-20"> 3188 <template x-for="(_,i) in images" :key="i"> 3189 <button @click="currentIndex = i" 3190 :class="{'bg-white':currentIndex===i,'bg-gray-300':currentIndex!==i}" 3191 class="w-2 h-2 rounded-full transition-colors duration-300"> 3192 </button> 3193 </template> 3194 </div> 3195 </div> 3196 </div> 3197 3198 </div> 3199 </div> 3200</section> 3201--> 3202 3203 3204 3205 3206 3207 3208<div class="container mx-auto px-4 py-8 text-primary font-semibold text-1xl pb-3"> 3209<h1 class="font-display heading-display heading-with-lines mb-4">Shop the Bakery</h1> 3210 <div class="grid grid-cols-2 md:grid-cols-4 gap-4 sm:gap-6"> 3211 <!-- Row 1 --> 3212 <a class="group block" href="/cakes"> 3213 <div class="flex flex-col items-center overflow-hidden rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300"> 3214 <div class="w-full relative" style="padding-bottom: 100%;"> 3215 <div class="absolute inset-0 overflow-hidden"> 3216 <img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" src="/media/homepagedessert/Dessert Lady Cakes Toronto.jpg" alt="Cakes"> 3217 </div> 3218 </div> 3219 <div class="px-3 py-3.5 sm:px-4 sm:py-4 text-center bg-[#fff8f1] w-full"> 3220 <h3 class="font-sans font-medium text-base tracking-tight text-[#402e32]">Cakes</h3> 3221 </div> 3222 </div> 3223 </a> 3224 <a class="group block" href="/cakes/specialty-cakes"> 3225 <div class="flex flex-col items-center overflow-hidden rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300"> 3226 <div class="w-full relative" style="padding-bottom: 100%;"> 3227 <div class="absolute inset-0 overflow-hidden"> 3228 <img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" src="/media/homepagedessert/Dessert Lady Specialty Cakes Toronto.jpg" alt="Specialty Cakes"> 3229 </div> 3230 </div> 3231 <div class="px-3 py-3.5 sm:px-4 sm:py-4 text-center bg-[#fff8f1] w-full"> 3232 <h3 class="font-sans font-medium text-base tracking-tight text-[#402e32]">Specialty Cakes</h3> 3233 </div> 3234 </div> 3235 </a> 3236 <a class="group block" href="/cakes/cupcakes"> 3237 <div class="flex flex-col items-center overflow-hidden rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300"> 3238 <div class="w-full relative" style="padding-bottom: 100%;"> 3239 <div class="absolute inset-0 overflow-hidden"> 3240 <img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" src="/media/homepagedessert/Dessert Lady Cupcakes Platter Toronto.jpg" alt="Cupcakes"> 3241 </div> 3242 </div> 3243 <div class="px-3 py-3.5 sm:px-4 sm:py-4 text-center bg-[#fff8f1] w-full"> 3244 <h3 class="font-sans font-medium text-base tracking-tight text-[#402e32]">Cupcakes</h3> 3245 </div> 3246 </div> 3247 </a> 3248 <a class="group block" href="/branded-items"> 3249 <div class="flex flex-col items-center overflow-hidden rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300"> 3250 <div class="w-full relative" style="padding-bottom: 100%;"> 3251 <div class="absolute inset-0 overflow-hidden"> 3252 <img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" src="/media/homepagedessert/Branded Cookies Dessertlady Toronto.jpg" alt="Branded Items"> 3253 </div> 3254 </div> 3255 <div class="px-3 py-3.5 sm:px-4 sm:py-4 text-center bg-[#fff8f1] w-full"> 3256 <h3 class="font-sans font-medium text-base tracking-tight text-[#402e32]">Branded Items</h3> 3257 </div> 3258 </div> 3259 </a> 3260 <!-- Row 2 --> 3261 <a class="group block" href="/branded-items/catering"> 3262 <div class="flex flex-col items-center overflow-hidden rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300"> 3263 <div class="w-full relative" style="padding-bottom: 100%;"> 3264 <div class="absolute inset-0 overflow-hidden"> 3265 <img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" src="/media/homepagedessert/Dessert Lady Catering Platter Toronto.jpg" alt="Catering Platter"> 3266 </div> 3267 </div> 3268 <div class="px-3 py-3.5 sm:px-4 sm:py-4 text-center bg-[#fff8f1] w-full"> 3269 <h3 class="font-sans font-medium text-base tracking-tight text-[#402e32]">Catering Platter</h3> 3270 </div> 3271 </div> 3272 </a> 3273 <a class="group block" href="/baked-goods"> 3274 <div class="flex flex-col items-center overflow-hidden rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300"> 3275 <div class="w-full relative" style="padding-bottom: 100%;"> 3276 <div class="absolute inset-0 overflow-hidden"> 3277 <img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" src="/media/homepagedessert/Dessertlady Backed Goods Toronto.jpg" alt="Baked Goods"> 3278 </div> 3279 </div> 3280 <div class="px-3 py-3.5 sm:px-4 sm:py-4 text-center bg-[#fff8f1] w-full"> 3281 <h3 class="font-sans font-medium text-base tracking-tight text-[#402e32]">Baked Goods</h3> 3282 </div> 3283 </div> 3284 </a> 3285 3286 3287 <a class="group block" href="/baked-goods/gift-baskets"> 3288 <div class="flex flex-col items-center overflow-hidden rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300"> 3289 <div class="w-full relative" style="padding-bottom: 100%;"> 3290 <div class="absolute inset-0 overflow-hidden"> 3291 <img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" src="/media/homepagedessert/Giftbasket Dessertlady Toronto.jpg" alt="Gift Baskets"> 3292 </div> 3293 </div> 3294 <div class="px-3 py-3.5 sm:px-4 sm:py-4 text-center bg-[#fff8f1] w-full"> 3295 <h3 class="font-sans font-medium text-base tracking-tight text-[#402e32]">Gift Baskets</h3> 3296 </div> 3297 </div> 3298 </a> 3299 <a class="group block" href="/categories/candles-sparklers"> 3300 <div class="flex flex-col items-center overflow-hidden rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300"> 3301 <div class="w-full relative" style="padding-bottom: 100%;"> 3302 <div class="absolute inset-0 overflow-hidden"> 3303 <img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" src="/media/homepagedessert/Dessert Lady Candles Tornonto.jpg" alt="Candles & Sparklers"> 3304 </div> 3305 </div> 3306 <div class="px-3 py-3.5 sm:px-4 sm:py-4 text-center bg-[#fff8f1] w-full"> 3307 <h3 class="font-sans font-medium text-base tracking-tight text-[#402e32]">Candles</h3> 3308 </div> 3309 </div> 3310 </a> 3311 </div> 3312</div> 3313 3314<section class="home-catering-banner" aria-labelledby="home-catering-heading"> 3315 <div class="container mx-auto px-4"> 3316 <div class="home-catering-banner__inner"> 3317 3318 <!-- Text (~52% desktop) --> 3319 <div class="home-catering-banner__copy"> 3320 <h2 id="home-catering-heading" class="heading-display heading-aligned home-catering-banner__title"> 3321 Catering, Gifting & Events 3322 </h2> 3323 <p class="home-catering-banner__text"> 3324 Bring your brand to life with handmade baked goods from our Toronto bakery. 3325 From corporate gifting and client events to birthdays and celebrations, we’ll make it memorable. 3326 </p> 3327 <a href="/branded-items" class="btn btn-primary home-catering-banner__cta"> 3328 Explore Branded & Catering 3329 </a> 3330 </div> 3331 3332 <!-- Visual anchor (~46% desktop) --> 3333 <div class="home-catering-banner__media"> 3334 <div class="home-catering-banner__figure"> 3335 <img 3336 src="/media/homepagedessert/corporate_branded_cookies_toronto_dessert_lady.jpg" 3337 alt="Corporate branded cookies from Dessert Lady Toronto bakery" 3338 width="1440" 3339 height="1440" 3340 loading="lazy" 3341 decoding="async" 3342 > 3343 </div> 3344 </div> 3345 3346 </div> 3347 </div> 3348</section> 3349 3350 3351 <div class="widget block block-static-block"><section class="bg-white py-12 mt-10 rounded-md"> 3352 <div class="max-w-7xl mx-auto px-4 grid grid-cols-1 md:grid-cols-2 gap-8"> 3353 3354 <!-- Left Column --> 3355 <div class="flex flex-col items-center text-center"> 3356 <h2 class="text-2xl sm:text-3xl font-bold text-gray-900 mb-2">Fresh Baked Goods Daily</h2> 3357 <p class="text-gray-700 mb-4"> 3358 Every morning, our ovens fire up with one missionâbring you the freshest, flakiest, most irresistible baked goods in Toronto. From buttery croissants to golden pastries, everything is made from scratch with the finest ingredients and a whole lot of heart. Order online or swing by for your daily dose of something warm, comforting, and straight from the oven. 3359 </p> 3360 <img src="/media/homepagedessert/dessert_lady_bakery.jpg" alt="Baker with croissants" class="rounded-md shadow-md w-full max-w-xs mx-auto md:mx-0"> 3361 </div> 3362 3363 <!-- Right Column --> 3364 <div class="flex flex-col items-center text-center"> 3365 <h2 class="text-2xl sm:text-3xl font-bold text-gray-900 mb-2">All the Good Stuff</h2> 3366 <p class="text-gray-700 mb-4"> 3367 Every bite at Dessert Lady is a reflection of our passion for excellence. From handcrafted pastries to artfully designed de
3367sserts, we use only premium ingredients and time-honored techniques to deliver products that are as beautiful as they are delicious. Whether you're treating yourself or sharing with others, our creations are made to impressâthoughtfully crafted with love, care, and an eye for quality. 3368 </p> 3369 <img src="/media/homepagedessert/dessert_lady_bakery_2.jpg" alt="Baker with croissants" class="rounded-md shadow-md w-full max-w-xs mx-auto md:mx-0"> 3370 </div> 3371 3372 </div> 3373 </section> 3374 3375 3376<div class="home_toronto p-2"> 3377 <div class="home-box dcat"> 3378 <div class="box-title"> 3379 <h1 class="font-toronto text-center text-4xl sm:text-xl md:text-2xl lg:text-3xl font-semibold mt-2"><span>Check out our Instagram feed</span></h1> 3380 <h3><span>For more than 20 years, Dessert Lady has been making Toronto's favorite baked goods the old-fashioned way: from scratch, in small batches, and using the finest ingredients.</span></h3> 3381 </div> 3382 </div> 3383</div> 3384<!-- LightWidget WIDGET -->
3384<script src="https://cdn.lightwidget.com/widgets/lightwidget.js"></script>
3384<iframe src="https://cdn.lightwidget.com/widgets/0c167aef39505c41a117e0481af8d74d.html" scrolling="no" allowtransparency="true" class="p-2 lightwidget-widget" style="width:100%;border:0;overflow:hidden;"></iframe> 3385</div><section class="home-bestsellers" aria-labelledby="home-bestsellers-heading"> 3386 <div class="container mx-auto px-4"> 3387 <h2 id="home-bestsellers-heading" class="home-bestsellers__title heading-display"> 3388 Best-Selling Cakes </h2> 3389 3390 <ul role="list" class="home-bestsellers__grid"> 3391 <li class="home-bestsellers__item"> 3392 <a 3393 href="https://dessertlady.ca/matcha-tiramisu-sliced-1" 3394 class="home-bestsellers__card" 3395 title="Matcha Tiramisu" 3396 > 3397 <div class="home-bestsellers__media"> 3398 <img 3399 class="home-bestsellers__img" 3400 src="https://dessertlady.ca/media/catalog/product/cache/3503aa182380a04db8617446793e9826/m/a/matcha_tiramisu_cake_toronto_dessert_lady.jpg" 3401 width="360" 3402 height="360" 3403 alt="Matcha Tiramisu" 3404 loading="lazy" 3405 decoding="async" 3406 > 3407 </div> 3408 <div class="home-bestsellers__info"> 3409 <span class="home-bestsellers__name"> 3410 Matcha Tiramisu </span> 3411 <span class="home-bestsellers__price"> 3412 $ 50.00 </span> 3413 </div> 3414 </a> 3415 </li> 3416 <li class="home-bestsellers__item"> 3417 <a 3418 href="https://dessertlady.ca/blue-ombre-cake" 3419 class="home-bestsellers__card" 3420 title="Blue Ombre Cake" 3421 > 3422 <div class="home-bestsellers__media"> 3423 <img 3424 class="home-bestsellers__img" 3425 src="https://dessertlady.ca/media/catalog/product/cache/3503aa182380a04db8617446793e9826/b/l/blue_ombre_cake_toronto_dessert_lady.jpg" 3426 width="360" 3427 height="360" 3428 alt="Blue Ombre Cake" 3429 loading="lazy" 3430 decoding="async" 3431 > 3432 </div> 3433 <div class="home-bestsellers__info"> 3434 <span class="home-bestsellers__name"> 3435 Blue Ombre Cake </span> 3436 <span class="home-bestsellers__price"> 3437 $ 125.00 </span> 3438 </div> 3439 </a> 3440 </li> 3441 <li class="home-bestsellers__item"> 3442 <a 3443 href="https://dessertlady.ca/birthday-sprinkles-cake" 3444 class="home-bestsellers__card" 3445 title="Birthday Sprinkles Cake" 3446 > 3447 <div class="home-bestsellers__media"> 3448 <img 3449 class="home-bestsellers__img" 3450 src="https://dessertlady.ca/media/catalog/product/cache/3503aa182380a04db8617446793e9826/b/i/birthday_sprinkles_cake_toronto_dessert_lady.jpg" 3451 width="360" 3452 height="360" 3453 alt="Birthday Sprinkles Cake" 3454 loading="lazy" 3455 decoding="async" 3456 > 3457 </div> 3458 <div class="home-bestsellers__info">
3459 <span class="home-bestsellers__name"> 3460 Birthday Sprinkles Cake </span> 3461 <span class="home-bestsellers__price"> 3462 $ 125.00 </span> 3463 </div> 3464 </a> 3465 </li> 3466 <li class="home-bestsellers__item"> 3467 <a 3468 href="https://dessertlady.ca/carrot-cream-cheese-icing-cake" 3469 class="home-bestsellers__card" 3470 title="Carrot Cake with Cream Cheese Icing" 3471 > 3472 <div class="home-bestsellers__media"> 3473 <img 3474 class="home-bestsellers__img" 3475 src="https://dessertlady.ca/media/catalog/product/cache/3503aa182380a04db8617446793e9826/c/a/carrot_cake_dessert_lady.jpg" 3476 width="360" 3477 height="360" 3478 alt="Carrot Cake with Cream Cheese Icing" 3479 loading="lazy" 3480 decoding="async" 3481 > 3482 </div> 3483 <div class="home-bestsellers__info"> 3484 <span class="home-bestsellers__name"> 3485 Carrot Cake with Cream Cheese Icing </span> 3486 <span class="home-bestsellers__price"> 3487 $ 45.00 </span> 3488 </div> 3489 </a> 3490 </li> 3491 <li class="home-bestsellers__item"> 3492 <a 3493 href="https://dessertlady.ca/ube-purple-yam-coconut-cake-with-ube-buttercream-icing" 3494 class="home-bestsellers__card" 3495 title="Ube (Purple Yam) Coconut Cake with Ube Buttercream Icing" 3496 > 3497 <div class="home-bestsellers__media"> 3498 <img 3499 class="home-bestsellers__img" 3500 src="https://dessertlady.ca/media/catalog/product/cache/3503aa182380a04db8617446793e9826/u/b/ubecoconut_1.jpg" 3501 width="360" 3502 height="360" 3503 alt="Ube (Purple Yam) Coconut Cake with Ube Buttercream Icing" 3504 loading="lazy" 3505 decoding="async" 3506 > 3507 </div> 3508 <div class="home-bestsellers__info"> 3509 <span class="home-bestsellers__name"> 3510 Ube (Purple Yam) Coconut Cake with Ube Buttercream Icing </span> 3511 <span class="home-bestsellers__price"> 3512 $ 50.00 </span> 3513 </div> 3514 </a> 3515 </li> 3516 <li class="home-bestsellers__item"> 3517 <a 3518 href="https://dessertlady.ca/tiramisu-3030" 3519 class="home-bestsellers__card" 3520 title="Tiramisu" 3521 > 3522 <div class="home-bestsellers__media"> 3523 <img 3524 class="home-bestsellers__img" 3525 src="https://dessertlady.ca/media/catalog/product/cache/3503aa182380a04db8617446793e9826/t/i/tiramisu.jpg" 3526 width="360" 3527 height="360" 3528 alt="Tiramisu" 3529 loading="lazy" 3530 decoding="async" 3531 > 3532 </div> 3533 <div class="home-bestsellers__info">
3534 <span class="home-bestsellers__name"> 3535 Tiramisu </span> 3536 <span class="home-bestsellers__price"> 3537 $ 50.00 </span> 3538 </div> 3539 </a> 3540 </li> 3541 <li class="home-bestsellers__item"> 3542 <a 3543 href="https://dessertlady.ca/double-chocolate-cake-with-vanilla-buttercream-icing" 3544 class="home-bestsellers__card" 3545 title="Double Chocolate Cake with Vanilla Buttercream Icing" 3546 > 3547 <div class="home-bestsellers__media"> 3548 <img 3549 class="home-bestsellers__img" 3550 src="https://dessertlady.ca/media/catalog/product/cache/3503aa182380a04db8617446793e9826/d/b/dblchocovan_2.jpg" 3551 width="360" 3552 height="360" 3553 alt="Double Chocolate Cake with Vanilla Buttercream Icing" 3554 loading="lazy" 3555 decoding="async" 3556 > 3557 </div> 3558 <div class="home-bestsellers__info"> 3559 <span class="home-bestsellers__name"> 3560 Double Chocolate Cake with Vanilla Buttercream Icing </span> 3561 <span class="home-bestsellers__price"> 3562 $ 45.00 </span> 3563 </div> 3564 </a> 3565 </li> 3566 <li class="home-bestsellers__item"> 3567 <a 3568 href="https://dessertlady.ca/strawberry-shortcake" 3569 class="home-bestsellers__card" 3570 title="Strawberry Shortcake" 3571 > 3572 <div class="home-bestsellers__media"> 3573 <img 3574 class="home-bestsellers__img" 3575 src="https://dessertlady.ca/media/catalog/product/cache/3503aa182380a04db8617446793e9826/s/t/strawberryshortcake_1.jpg" 3576 width="360" 3577 height="360" 3578 alt="Strawberry Shortcake" 3579 loading="lazy" 3580 decoding="async" 3581 > 3582 </div> 3583 <div class="home-bestsellers__info"> 3584 <span class="home-bestsellers__name"> 3585 Strawberry Shortcake </span> 3586 <span class="home-bestsellers__price"> 3587 $ 45.00 </span> 3588 </div> 3589 </a> 3590 </li> 3591 </ul> 3592 3593 <div class="home-bestsellers__cta-wrap"> 3594 <a 3595 href="https://dessertlady.ca/order-in-advance/cakes/" 3596 class="btn btn-primary home-bestsellers__cta" 3597 > 3598 View All Cakes </a> 3599 </div> 3600 </div> 3601</section> <div class="widget block block-static-block"><section class="home-trust" aria-label="Why choose Dessert Lady"> 3602 <div class="container mx-auto px-4"> 3603 <ul class="home-trust__grid" role="list"> 3604 <li class="home-trust__item"> 3605 <svg class="home-trust__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> 3606 <path stroke-linecap="round" stroke-linejoin="round" d="M12 3c.5 2.5 2 4.5 4.5 5.5C14 10 12.5 12 12 15c-.5-3-2-5-4.5-6.5C10 7.5 11.5 5.5 12 3z"/> 3607 <path stroke-linecap="round" stroke-linejoin="round" d="M5 19c2-1.5 4.5-2 7-2s5 .5 7 2"/> 3608 </svg> 3609 <h3 class="home-trust__title">Fresh Ingredients</h3> 3610 <p class="home-trust__text">Homemade from scratch with simple, high-quality ingredients.</p> 3611 </li> 3612 <li class="home-trust__item"> 3613 <svg class="home-trust__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> 3614 <path stroke-linecap="round" stroke-linejoin="round" d="M3 7.5h18M5.25 7.5V18a1.5 1.5 0 001.5 1.5h10.5a1.5 1.5 0 001.5-1.5V7.5"/> 3615 <path stroke-linecap="round" stroke-linejoin="round" d="M9 11.25h6"/> 3616 </svg> 3617 <h3 class="home-trust__title">Easy Ordering</h3> 3618 <p class="home-trust__text">Quick, seamless online ordering from start to finish.</p> 3619 </li> 3620 <li class="home-trust__item"> 3621 <svg class="home-trust__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> 3622 <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m13.5 0a1.5 1.5 0 01-3 0M3.75 11.25h12.75l1.8 4.5H6.15l-2.4-4.5z"/> 3623 <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 11.25V7.5a.75.75 0 00-.75-.75H8.25"/> 3624 </svg> 3625 <h3 class="home-trust__title">Toronto Delivery</h3> 3626 <p class="home-trust__text">Fast, reliable deliveryâoften as soon as the next day.</p> 3627 </li> 3628 <li class="home-trust__item"> 3629 <svg class="home-trust__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> 3630 <path stroke-linecap="round" stroke-linejoin="round" d="M15.182 15.182a4.5 4.5 0 01-6.364 0M9.75 9.75h.008v.008H9.75V9.75zm4.5 0h.008v.008h-.008V9.75z"/> 3631 <path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/> 3632 </svg> 3633 <h3 class="home-trust__title">Friendly Service</h3> 3634 <p class="home-trust__text">Warm bakery support that makes ordering easy.</p> 3635 </li> 3636 </ul> 3637 </div> 3638</section> 3639</div></div></div></main><div class="page-bottom"> </div><footer class="page-footer"><div class="footer content"> 3640<div class="home-newsletter"> 3641 <form 3642 class="form subscribe home-newsletter__form" 3643 action="https://dessertlady.ca/newsletter/subscriber/new/" 3644 method="post" 3645 x-data="initNewsletterForm()"
3646 @submit.prevent="submitForm()" 3647 id="newsletter-validate-detail" 3648 aria-label="Subscribe to Newsletter" 3649 > 3650 <h2 class="home-newsletter__heading"> 3651 A Little Sweetness in Your Inbox </h2> 3652 <p class="home-newsletter__support"> 3653 New cakes, seasonal favorites and special offers from Dessert Lady. </p> 3654 <div class="home-newsletter__fields"> 3655 <label class="grow m-0 min-w-0"> 3656 <span class="sr-only">Email Address</span> 3657 <input 3658 type="email" 3659 id="newsletter-subscribe" 3660 name="email" 3661 class="home-newsletter__input form-input w-full" 3662 required 3663 autocomplete="email" 3664 placeholder="Enter your email address" 3665 > 3666 <input name="form_key" type="hidden" value="NMEmRNvncJnEpv9c" /> <div class="control"> 3667 <input type="hidden" 3668 value="" 3669 name="g-recaptcha-response" 3670 > 3671</div> </label> 3672 <button type="submit" class="btn btn-primary home-newsletter__submit shrink-0"> 3673 Subscribe </button> 3674 </div> 3675 <template x-if="displayErrorMessage"> 3676 <p class="flex items-center text-red mt-2"> 3677 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="inline-block mr-3" width="24" height="24" role="img"> 3678 <path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/> 3679<title>exclamation-circle</title></svg> 3680 <template x-for="errorMessage in errorMessages"> 3681 <span x-html="errorMessage"></span> 3682 </template> 3683 </p> 3684 </template> 3685 </form> 3686
3686<script> 3687 function initNewsletterForm() { 3688 return { 3689 errors: 0, 3690 hasCaptchaToken: 0, 3691 displayErrorMessage: false, 3692 errorMessages: [], 3693 setErrorMessages(messages) { 3694 this.errorMessages = [messages] 3695 this.displayErrorMessage = this.errorMessages.length 3696 }, 3697 submitForm() { 3698 // Do not rename $form, the variable is expected to be declared in the recaptcha output 3699 const $form = document.querySelector('#newsletter-validate-detail'); 3700 if (!this.hasCaptchaToken) { 3701 this.errors = 1; 3702 3703 3704 const executeGreCaptcha = () => { 3705 grecaptcha.ready(() => { 3706 grecaptcha.execute( 3707 '6Lc4bWMtAAAAAO0UU2tu07Hp_4JXvRSooTCss9Yb', 3708 {action: 'submit'} 3709 ).then((token) => { 3710 const tokenElement = $form['g\u002Drecaptcha\u002Dresponse']; 3711 3712 if (tokenElement && token && token.length > 0) { 3713 $form['g\u002Drecaptcha\u002Dresponse'].value = token; 3714 this.errors = 0; 3715 this.hasCaptchaToken = 1; 3716 (this.submitForm || $form.submit).call(this); 3717 } else { 3718 this.errors = 1; 3719 const error = tokenElement 3720 ? 'ReCaptcha\u0020validation\u0020failed,\u0020please\u0020try\u0020again' 3721 : 'ReCaptcha\u0020token\u0020element\u0020is\u0020missing.\u0020Please\u0020refresh\u0020the\u0020page\u0020and\u0020trying\u0020again.'; 3722 if (this.setErrorMessages) { 3723 this.setErrorMessages([error]) 3724 this.displayErrorMessage = true; 3725 } else { 3726 window.dispatchMessages && window.dispatchMessages([{type: "error", text: error}], 5000); 3727 } 3728 } 3729 }).catch(exception => { 3730 console.error(exception || `An unknown error occurred during ReCaptcha validation. Are the tokens and the domain correctly set up in the Google console?`) 3731 }); 3732 }) 3733 } 3734 3735 if (window.grecaptcha) { 3736 executeGreCaptcha(); 3737 } else { 3738 const recaptchaUrl = "https://www.google.com/recaptcha/api.js?render=6Lc4bWMtAAAAAO0UU2tu07Hp_4JXvRSooTCss9Yb"; 3739 const script = document.createElement('script'); 3740 script.setAttribute('type', 'text/javascript'); 3741 script.setAttribute('src', recaptchaUrl); 3742 document.head.appendChild(script); 3743 3744 script.addEventListener("load", executeGreCaptcha, false); 3745 } 3746 } 3747 3748 if (this.errors === 0) { 3749 $form.submit(); 3750 } 3751 } 3752 } 3753 } 3754 </script>
3754 3755</div><div class="bg-zinc-100 border-t border-zinc-200"> 3756 <div class="container py-8 mx-auto"> 3757 <div class="grid grid-cols-1 lg:grid-cols-4 lg:gap-6 text-slate-600"> 3758 3759 <div 3760 x-data="{ 3761 open: true, 3762 isMobile: false, 3763 checkIsMobileResolution() { this.isMobile = getComputedStyle(this.$refs.toggleIcon).display !== 'none' } 3764 }" 3765 x-id="['collapse-id']" 3766 x-init="checkIsMobileResolution()" 3767 @resize.window.debounce="checkIsMobileResolution()" 3768 @visibilitychange.window.debounce="checkIsMobileResolution()" 3769 class="border-b border-zinc-200 lg:border-b-0 last:border-b-0" 3770 > 3771 <div 3772 :id="$id('collapse-id')" 3773 :role="isMobile && 'button'" 3774 :tabindex="isMobile && '0'" 3775 class="group flex w-full items-center justify-between py-4 gap-2 font-bold text-slate-700 lg:pt-0" 3776 @click="isMobile && (open = !open)" 3777 @keyup.enter="isMobile && (open = !open)" 3778 @keyup.space="isMobile && (open = !open)" 3779 :aria-expanded="isMobile ? open : null" 3780 :aria-controls="isMobile ? $id('collapse-id') : null" 3781 > 3782 <h3>Cakes and Cupcakes</h3> 3783 <span 3784 class="transition-transform group-aria-expanded:rotate-180 lg:hidden" 3785 x-ref="toggleIcon" 3786 > 3787 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="20" height="20" aria-hidden="true"> 3788 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 3789</svg> 3790 </span> 3791 </div> 3792 <div 3793 class="lg:block" 3794 :class="{ 'hidden': isMobile && !open }" 3795 :aria-labelledby="isMobile ? $id('collapse-id') : null" 3796 :aria-hidden="isMobile && (open ? 'false' : 'true')" 3797 > 3798 <div class="pt-0.5 pb-2.5"> 3799 3800<div class="ftrSet01"> 3801 <div class="ftrlogo"> 3802 <img src="/media/logo/stores/1/logo.png" alt="Dessert Lady Cafe & Bakery" /> 3803 </div> 3804 3805 <div class="ftrTxt01"> 3806 <p>Have a question?</p> 3807 <span>We're always here to lend a helping hand.</span> 3808 </div> 3809 3810 <div class="ftrTxt02"> 3811 <p> 3812 Bakery Hours<br/> 3813 Monday to Wednesday, Sunday 8:30am - 7pm<br/> 3814 Thursday to Saturday 8:30am - 8:30pm 3815 </p> 3816 </div> 3817 3818 <div class="ftrTxt03"> 3819 <a href="/contact">CONTACT US</a> 3820 </div> 3821 3822</div> 3823 3824<!-- 3825<nav aria-label="Cakes / Cupcakes"> 3826 <ul> 3827 <li> 3828 <a 3829 href="https://dessertlady.ca/cakes/classic-cakes/" 3830 class="block py-1.5 hover:underline" 3831 >Classic Cakes</a> 3832 </li> 3833 <li> 3834 <a 3835 href="https://dessertlady.ca/cakes/speciality-cakes/" 3836 class="block py-1.5 hover:underline" 3837 >Specialty Cakes</a> 3838 </li> 3839 <li> 3840 <a 3841 href="https://dessertlady.ca/cakes/moussecakes/" 3842 class="block py-1.5 hover:underline" 3843 >Mousse Cakes</a> 3844 </li> 3845 <li> 3846 <a 3847 href="https://dessertlady.ca/cakes/cheesecakes/" 3848 class="block py-1.5 hover:underline" 3849 >Cheese Cakes</a> 3850 </li> 3851 <li> 3852 <a 3853 href="https://dessertlady.ca/cakes/cupcakes/" 3854 class="block py-1.5 hover:underline" 3855 >Cupcakes</a> 3856 </li> 3857 </ul> 3858</nav> --> 3859 3860<style> 3861 3862footer .footer .bg-zinc-100 .bg-zinc-50 { 3863 background: #fff; 3864} 3865.ftrlogo img { 3866 max-height: 36px; 3867} 3868 3869.footer.content .bg-zinc-100 .container.py-8.mx-auto { 3870 padding: 0; 3871 max-width: 100%; 3872 background: #fff; 3873} 3874 3875.footer.content .bg-zinc-100 .container.py-8.mx-auto>.grid { 3876 padding-top: 2rem; 3877 padding-bottom: 2rem; 3878 max-width: 1536px; 3879 margin: 0 auto; 3880} 3881 3882.footer.content .bg-zinc-100 .container.py-8.mx-auto>.grid .border-zinc-200:first-child>.group { 3883 display: none; 3884} 3885 3886.footer.content .bg-zinc-100 .container.py-8.mx-auto>.grid .border-zinc-200>.group { 3887 color: #000; 3888} 3889 3890.ftrTxt01 { 3891 margin: 20px 0 0 0; 3892} 3893 3894.ftrTxt02 { 3895 margin: 20px 0 0 0; 3896 font-size: 14px; 3897} 3898 3899.ftrTxt03 { 3900 margin: 20px 0 0 0; 3901} 3902 3903.ftrTxt01 p { 3904 font-size: 20px; 3905 font-weight: 600; 3906 color: #334152; 3907 line-height: 22px; 3908} 3909 3910.ftrSet01 { 3911 color: #334152; 3912} 3913 3914.ftrTxt01 span { 3915 font-size: 14px; 3916} 3917 3918footer .footer .bg-zinc-100 .ftrTxt03 a { 3919 font-size: 16px; 3920 color: #000; 3921 font-weight: 600; 3922} 3923 3924 3925</style> </div> 3926 </div> 3927 </div> 3928 3929 <div 3930 x-data="{ 3931 open: false, 3932 isMobile: false, 3933 checkIsMobileResolution() { this.isMobile = getComputedStyle(this.$refs.toggleIcon).display !== 'none' } 3934 }" 3935 x-id="['collapse-id']" 3936 x-init="checkIsMobileResolution()" 3937 @resize.window.debounce="checkIsMobileResolution()" 3938 @visibilitychange.window.debounce="checkIsMobileResolution()" 3939 class="border-b border-zinc-200 lg:border-b-0 last:border-b-0" 3940 > 3941 <div 3942 :id="$id('collapse-id')" 3943 :role="isMobile && 'button'" 3944 :tabindex="isMobile && '0'" 3945 class="group flex w-full items-center justify-between py-4 gap-2 font-bold text-slate-700 lg:pt-0" 3946 @click="isMobile && (open = !open)" 3947 @keyup.enter="isMobile && (open = !open)" 3948 @keyup.space="isMobile && (open = !open)" 3949 :aria-expanded="isMobile ? open : null" 3950 :aria-controls="isMobile ? $id('collapse-id') : null" 3951 > 3952 <h3>Shopping Links</h3>
3953 <span 3954 class="transition-transform group-aria-expanded:rotate-180 lg:hidden" 3955 x-ref="toggleIcon" 3956 > 3957 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="20" height="20" aria-hidden="true"> 3958 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 3959</svg> 3960 </span> 3961 </div> 3962 <div 3963 class="hidden lg:block" 3964 :class="{ 'hidden': isMobile && !open }" 3965 :aria-labelledby="isMobile ? $id('collapse-id') : null" 3966 :aria-hidden="isMobile && (open ? 'false' : 'true')" 3967 > 3968 <div class="pt-0.5 pb-2.5"> 3969 3970<nav aria-label="Branded Items"> 3971 <ul> 3972 <li> 3973 <a 3974 href="https://dessertlady.ca/gourmet-cakes/edible-photo-faq/" 3975 class="block py-1.5 hover:underline" 3976 >Edible Photo FAQ</a> 3977 </li> 3978 <li> 3979 <a 3980 href="https://dessertlady.ca/faq/" 3981 class="block py-1.5 hover:underline" 3982 >Frequently Asked Questions</a> 3983 </li> 3984 <li> 3985 <a 3986 href="https://dessertlady.ca/rewardpoints-policy/" 3987 class="block py-1.5 hover:underline" 3988 >Reward Points Policy</a> 3989 </li> 3990 <li> 3991 <a 3992 href="https://dessertlady.ca/return-cancellation-policy/" 3993 class="block py-1.5 hover:underline" 3994 >Return & Cancellation Policy</a> 3995 </li> 3996 </ul> 3997</nav> </div> 3998 </div> 3999 </div> 4000 4001 <div 4002 x-data="{ 4003 open: false, 4004 isMobile: false, 4005 checkIsMobileResolution() { this.isMobile = getComputedStyle(this.$refs.toggleIcon).display !== 'none' } 4006 }" 4007 x-id="['collapse-id']" 4008 x-init="checkIsMobileResolution()" 4009 @resize.window.debounce="checkIsMobileResolution()" 4010 @visibilitychange.window.debounce="checkIsMobileResolution()" 4011 class="border-b border-zinc-200 lg:border-b-0 last:border-b-0" 4012 > 4013 <div 4014 :id="$id('collapse-id')" 4015 :role="isMobile && 'button'" 4016 :tabindex="isMobile && '0'" 4017 class="group flex w-full items-center justify-between py-4 gap-2 font-bold text-slate-700 lg:pt-0" 4018 @click="isMobile && (open = !open)" 4019 @keyup.enter="isMobile && (open = !open)" 4020 @keyup.space="isMobile && (open = !open)" 4021 :aria-expanded="isMobile ? open : null" 4022 :aria-controls="isMobile ? $id('collapse-id') : null" 4023 > 4024 <h3>Bakery Links</h3> 4025 <span 4026 class="transition-transform group-aria-expanded:rotate-180 lg:hidden" 4027 x-ref="toggleIcon" 4028 > 4029 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="20" height="20" aria-hidden="true"> 4030 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 4031</svg> 4032 </span> 4033 </div> 4034 <div 4035 class="hidden lg:block" 4036 :class="{ 'hidden': isMobile && !open }" 4037 :aria-labelledby="isMobile ? $id('collapse-id') : null" 4038 :aria-hidden="isMobile && (open ? 'false' : 'true')" 4039 > 4040 <div class="pt-0.5 pb-2.5"> 4041 4042<nav aria-label="Company"> 4043 <ul> 4044 <li> 4045 <a 4046 href="https://dessertlady.ca/about-us/" 4047 class="block py-1.5 hover:underline" 4048 >About Us</a> 4049 </li> 4050 <li> 4051 <a 4052 href="https://dessertlady.ca/delivery/" 4053 class="block py-1.5 hover:underline" 4054 >Delivery of your Order</a> 4055 </li> 4056 <li> 4057 <a 4058 href="https://dessertlady.ca/pickup-information/" 4059 class="block py-1.5 hover:underline" 4060 >Store Pickup Details</a> 4061 </li> 4062 <li> 4063 <a 4064 href="https://dessertlady.ca/cafe-and-eatery/" 4065 class="block py-1.5 hover:underline" 4066 >Cafe & Eatery</a> 4067 </li> 4068 </ul> 4069</nav> </div> 4070 </div> 4071 </div> 4072 4073 <div 4074 x-data="{ 4075 open: false, 4076 isMobile: false, 4077 checkIsMobileResolution() { this.isMobile = getComputedStyle(this.$refs.toggleIcon).display !== 'none' } 4078 }" 4079 x-id="['collapse-id']" 4080 x-init="checkIsMobileResolution()" 4081 @resize.window.debounce="checkIsMobileResolution()" 4082 @visibilitychange.window.debounce="checkIsMobileResolution()" 4083 class="border-b border-zinc-200 lg:border-b-0 last:border-b-0" 4084 > 4085 <div 4086 :id="$id('collapse-id')" 4087 :role="isMobile && 'button'" 4088 :tabindex="isMobile && '0'" 4089 class="group flex w-full items-center justify-between py-4 gap-2 font-bold text-slate-700 lg:pt-0" 4090 @click="isMobile && (open = !open)" 4091 @keyup.enter="isMobile && (open = !open)" 4092 @keyup.space="isMobile && (open = !open)" 4093 :aria-expanded="isMobile ? open : null" 4094 :aria-controls="isMobile ? $id('collapse-id') : null" 4095 > 4096 <h3>Account & Orders</h3>
4097 <span 4098 class="transition-transform group-aria-expanded:rotate-180 lg:hidden" 4099 x-ref="toggleIcon" 4100 > 4101 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="20" height="20" aria-hidden="true"> 4102 <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/> 4103</svg> 4104 </span> 4105 </div> 4106 <div 4107 class="hidden lg:block" 4108 :class="{ 'hidden': isMobile && !open }" 4109 :aria-labelledby="isMobile ? $id('collapse-id') : null" 4110 :aria-hidden="isMobile && (open ? 'false' : 'true')" 4111 > 4112 <div class="pt-0.5 pb-2.5"> 4113 4114<nav aria-label="My Account"> 4115 <ul> 4116 <li> 4117 <a 4118 href="https://dessertlady.ca/customer/account/login/" 4119 class="block py-1.5 hover:underline" 4120 >Login or Create Account</a> 4121 </li> 4122 <li> 4123 <a 4124 href="https://dessertlady.ca/customer/account/" 4125 class="block py-1.5 hover:underline" 4126 >Account Dashboard</a> 4127 </li> 4128 <li> 4129 <a 4130 href="https://dessertlady.ca/sales/order/history/" 4131 class="block py-1.5 hover:underline" 4132 >My Orders</a> 4133 </li> 4134 <li> 4135 <a 4136 href="https://dessertlady.ca/checkout/cart/" 4137 class="block py-1.5 hover:underline" 4138 >Shopping Cart</a> 4139 </li> 4140 </ul> 4141</nav> </div> 4142 </div> 4143 </div> 4144 </div> 4145 </div> 4146 <div class="space-y-10 bg-zinc-50 py-4"> 4147 4148<div 4149 class="container flex gap-4 flex-wrap justify-center" 4150 aria-label="Payment Providers" 4151> 4152 <svg xmlns="http://www.w3.org/2000/svg" width="48" height="32" fill="none" viewBox="0 0 48 32" role="img"><g clip-path="url(#a)"><path fill="#d9dff7" d="M0 4a4 4 0 0 1 4-4h40a4 4 0 0 1 4 4v24a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4z"/><path fill="#1434cb" d="M19.108 10.212 14.243 21.82H11.07l-2.393-9.263c-.146-.57-.272-.78-.714-1.02-.722-.391-1.913-.759-2.962-.987l.071-.337h5.109c.651 0 1.237.434 1.385 1.184l1.264 6.715 3.124-7.9zm12.435 7.817c.013-3.063-4.236-3.232-4.206-4.6.009-.417.405-.86 1.273-.973.43-.056 1.616-.099 2.96.52l.527-2.461A8.1 8.1 0 0 0 29.29 10c-2.968 0-5.056 1.578-5.074 3.837-.019 1.67 1.491 2.603 2.629 3.158 1.17.569 1.563.934 1.558 1.443-.008.778-.933 1.122-1.798 1.135-1.509.024-2.385-.408-3.083-.733l-.544 2.543c.702.322 1.997.603 3.339.617 3.154 0 5.218-1.558 5.227-3.97m7.837 3.79h2.777l-2.424-11.607H37.17c-.577 0-1.063.336-1.278.852L31.387 21.82h3.152l.626-1.734h3.852zm-3.35-4.113 1.58-4.358.91 4.358zm-12.632-7.494L20.915 21.82h-3.002l2.483-11.607z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h48v32H0z"/></clipPath></defs><title>visa</title></svg> 4153 <svg xmlns="http://www.w3.org/2000/svg" width="48" height="32" fill="none" viewBox="0 0 48 32" role="img"><g clip-path="url(#a_2)"><path fill="#f1efeb" d="M0 4a4 4 0 0 1 4-4h40a4 4 0 0 1 4 4v24a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4z"/><path fill="#ff5f00" d="M28.376 8.139h-8.75v15.725h8.75z"/><path fill="#eb001b" d="M20.181 16a9.98 9.98 0 0 1 3.82-7.861A9.96 9.96 0 0 0 17.82 6c-5.522 0-10 4.478-10 10s4.478 10 10 10A9.96 9.96 0 0 0 24 23.861 9.98 9.98 0 0 1 20.182 16"/><path fill="#f79e1b" d="M40.178 16c0 5.522-4.477 10-10 10-2.333 0-4.477-.8-6.18-2.139A9.98 9.98 0 0 0 27.818 16a9.98 9.98 0 0 0-3.82-7.861A9.96 9.96 0 0 1 30.178 6c5.522 0 10 4.478 10 10"/></g><defs><clipPath id="a_2"><path fill="#fff" d="M0 0h48v32H0z"/></clipPath></defs><title>mastercard</title></svg> 4154</div> </div> 4155 <div class="container pt-7 pb-5 border-t border-zinc-200 flex justify-between items-center flex-col sm:flex-row text-sm"> 4156 <div class="flex justify-between"> 4157 <div class="text-white mr-4 font-semibold textshadow sm:text-center"> 4158 Designed with <span class="text-red">❤ </span> 4159 <a href="https://toweringmedia.com/" class="hover:underline"> 4160 by Towering Media </a> 4161</div> 4162 4163<div class="text-white mr-1 font-semibold textshadow sm:text-center">🍁 Proudly Made in Toronto</div> 4164 4165<div class="text-white mr-1 font-semibold textshadow sm:text-center">👩 Women Owned Bakery</div> 4166</div> <nav class="text-white" aria-label="Legal Menu"> 4167 <a 4168 href="https://dessertlady.ca/privacy-policy-cookie-restriction-mode/" 4169 class="inline-block py-1 px-2 sm:px-0 sm:mr-4 sm:last:mr-0 hover:underline" 4170 >Terms & Conditions</a> 4171 <a 4172 href="https://dessertlady.ca/privacy-policy-cookie-restriction-mode/" 4173 class="inline-block py-1 px-2 sm:px-0 sm:mr-4 sm:last:mr-0 hover:underline" 4174 >Privacy Policy</a> 4175 </nav> 4176 </div> 4177</div></div></footer>
4177<script type="text/javascript"> window.dataLayer = window.dataLayer || []; (function () { /** * @var {[Object]} */ const dataToPush = JSON.parse('\u005B\u005D'); dataToPush.forEach((dataObject) => { window.dataLayer.push(dataObject); }); })();</script>
4177
4177<script> 4178 (_ => { 4179 'use strict'; 4180 4181 const SECTION_KEY = 'amga4_hyva_event_pool'; 4182 const MAGE_CACHE_SECTION_KEY = 'mage-cache-storage'; 4183 const storage = hyva.getBrowserStorage(); 4184 4185 window.addEventListener('private-content-loaded', ({ detail }) => { 4186 if (!detail?.data) { 4187 return; 4188 } 4189 4190 const data = detail?.data[SECTION_KEY]; 4191 if (!data) { 4192 return; 4193 } 4194 4195 let eventsPool = data?.events || []; 4196 if (eventsPool.length) { 4197 eventsPool.forEach(event => { 4198 window.dataLayer.push({ecommerce: null}); 4199 window.dataLayer.push(event); 4200 }); 4201 4202 setTimeout(() => { 4203 storage.setItem( 4204 MAGE_CACHE_SECTION_KEY, 4205 JSON.stringify({ 4206 ...detail.data, 4207 [SECTION_KEY]: {} 4208 }) 4209 ); 4210 }, 0); 4211 } 4212 }); 4213 })();</script>
4213
4213<script> 4214 let SnapSlider = class { 4215 constructor(el, { 4216 labelSepparator = 'of', 4217 autoPager, 4218 groupPager, 4219 loop, 4220 forcePager 4221 } = {}) { 4222 this.el = el; 4223 this.track = this.el.querySelector("[data-track]"); 4224 if (!this.track) { 4225 console.warn( 4226 "No Slider track defined, reverting back to CSS slider.\nPlease create a wrapper for your slides with the attribute data-track", 4227 this.el 4228 ); 4229 return; 4230 } 4231 this.initialLoad = true; 4232 this.slides = []; 4233 this.inViewObserver; 4234 this.mutationObserver; 4235 this.resizeObserver; 4236 this.pager = this.el.querySelector("[data-pager]"); 4237 this.navBtns = Array.from( 4238 this.el.querySelectorAll("[data-next], [data-prev]") 4239 ); 4240 this.slideLabelSepparator = this.el.dataset.slideLabelSepparator || labelSepparator; 4241 this.useAutoPager = autoPager || this.el.hasAttribute("data-auto-pager") || false; 4242 this.useGroupPager = groupPager || this.el.hasAttribute("data-group-pager") || false; 4243 this.useLoop = loop || this.el.hasAttribute("data-loop") || false; 4244 this.forcePager = forcePager || this.el.hasAttribute("data-force-pager") || false; 4245 this.sliderLabel = this.el.hasAttribute("aria-label") && this.el.getAttribute("aria-label").toLowerCase().trim().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, ""); 4246 this.sliderId = this.el.id || this.sliderLabel || "slider"; 4247 this.markerIdName = "data-target-id"; 4248 this.pagerClasses = this.el.dataset.pagerClass || "snap-pager"; 4249 this.markerClasses = this.el.dataset.markerClass || "snap-marker"; 4250 this.init(); 4251 } 4252 init() { 4253 this.setupSlides(); 4254 this.setupNav(); 4255 if (this.useAutoPager) { 4256 this.createPager(); 4257 } else { 4258 this.setupPager(); 4259 } 4260 this.setupObservers(); 4261 this.setupMutationObserver(); 4262 this.setupResizeObserver(); 4263 this.el.addEventListener("click", this.eventHandler.bind(this)); 4264 this.el.addEventListener("keydown", this.eventHandler.bind(this)); 4265 if ("onscrollend" in window) { 4266 this.track.addEventListener("scrollend", () => { 4267 this.isNavigating = false; 4268 }); 4269 } else { 4270 this.track.addEventListener("scroll", () => { 4271 clearTimeout(this.scrollEndTimer); 4272 this.scrollEndTimer = setTimeout(() => { 4273 this.isNavigating = false; 4274 }, 100); 4275 }); 4276 } 4277 } 4278 destroy() { 4279 this.inViewObserver?.disconnect(); 4280 this.mutationObserver?.disconnect(); 4281 this.resizeObserver?.disconnect(); 4282 this.el.removeEventListener("click", this.eventHandler.bind(this)); 4283 this.el.removeEventListener("keydown", this.eventHandler.bind(this)); 4284 } 4285 roundUpIfGreaterThan(number, min = 8) { 4286 const decimalPart = number - Math.floor(number); 4287 return decimalPart >= min ? Math.ceil(number) : Math.floor(number); 4288 } 4289 getInViewItems() { 4290 const inViewSlides = Array.from(this.track.querySelectorAll("[data-in-view]")).filter((el) => this.slides.includes(el)); 4291 const firstInViewSlide = inViewSlides[0] || null; 4292 const lastInViewSlide = inViewSlides[inViewSlides.length - 1] || null; 4293 const isAtStart = firstInViewSlide === this.slides[0]; 4294 const isAtEnd = lastInViewSlide === this.slides[this.slides.length - 1]; 4295 return { 4296 inViewSlides, 4297 totalInViewSlides: inViewSlides.length, 4298 firstInViewSlide, 4299 lastInViewSlide, 4300 isAtStart, 4301 isAtEnd, 4302 hasNoOverflow: isAtStart && isAtEnd 4303 }; 4304 } 4305 groupPagerMarkers() { 4306 if (this.slides.length === 0 || !this.pager || !this.useGroupPager) return; 4307 const totalVisibleSlides = this.roundUpIfGreaterThan( 4308 this.track.offsetWidth / this.slides[0].offsetWidth 4309 ); 4310 const markers = Array.from(this.pager.querySelectorAll("a, button"));
4311 markers.forEach((marker, index) => { 4312 marker.style.display = index % totalVisibleSlides === 0 ? null : "none"; 4313 }); 4314 } 4315 handleInView(entries) { 4316 entries.forEach((entry) => { 4317 const marker = this.pager && (this.pager.querySelector(`[href="#${entry.target.id}"]`) || this.pager.querySelector( 4318 `[${this.markerIdName}="${entry.target.id}"]` 4319 )); 4320 entry.target.toggleAttribute("data-in-view", entry.isIntersecting); 4321 entry.target.toggleAttribute("inert", !entry.isIntersecting); 4322 marker?.setAttribute("aria-current", entry.isIntersecting); 4323 marker?.setAttribute("tabindex", entry.isIntersecting ? "0" : "-1"); 4324 }); 4325 const { isAtStart, isAtEnd, hasNoOverflow } = this.getInViewItems(); 4326 this.navBtns.forEach((btn) => { 4327 if (btn.hasAttribute("data-next")) { 4328 btn.style.visibility = hasNoOverflow ? "hidden" : null; 4329 isAtEnd && !this.useLoop ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled"); 4330 } 4331 if (btn.hasAttribute("data-prev")) { 4332 btn.style.visibility = hasNoOverflow ? "hidden" : null; 4333 isAtStart && !this.useLoop ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled"); 4334 } 4335 }); 4336 if (this.pager) { 4337 const hidePager = hasNoOverflow && !this.forcePager; 4338 this.pager.style.visibility = hidePager ? "hidden" : null; 4339 this.pager.toggleAttribute("inert", hidePager); 4340 } 4341 if (!this.initialLoad) { 4342 this.el.dispatchEvent( 4343 new CustomEvent("slideChange", { 4344 detail: this.getInViewItems() 4345 }) 4346 ); 4347 } else { 4348 this.initialLoad = false; 4349 } 4350 if (document.activeElement.parentElement.hasAttribute("data-pager")) { 4351 const activeItems = this.pager.querySelectorAll('[tabindex="0"]'); 4352 if (activeItems.length) { 4353 activeItems[0].focus({ preventScroll: true }); 4354 } 4355 } 4356 } 4357 getSlides() { 4358 if (!this.track) return []; 4359 return Array.from(this.track.children).filter( 4360 (child) => child.tagName.toLowerCase() !== "template" && child.tagName.toLowerCase() !== "style" && child.tagName.toLowerCase() !== "script" && child.style.display !== "none" 4361 ); 4362 } 4363 refreshSlides() { 4364 this.initialLoad = true; 4365 this.slides = this.getSlides(); 4366 this.setupSlides(); 4367 if (this.useAutoPager) { 4368 this.createPager(); 4369 } else { 4370 this.setupPager(); 4371 } 4372 this.groupPagerMarkers(); 4373 this.setupObservers(); 4374 } 4375 setupSlides() { 4376 this.slides = this.getSlides(); 4377 this.slides.forEach((slide, index) => { 4378 const totalSlides = this.slides.length; 4379 const currentSlide = index + 1; 4380 const existingLabel = slide.getAttribute("aria-label") || ""; 4381 const isImage = slide.tagName.toLowerCase() === "img" || slide.tagName.toLowerCase() === "picture"; 4382 const slideLabelTag = isImage ? "alt" : "aria-label"; 4383 const hasAutoLabel = existingLabel.startsWith( 4384 `${currentSlide} ${this.slideLabelSepparator} ` 4385 ); 4386 if (!slide.hasAttribute("id")) { 4387 slide.setAttribute( 4388 "id", 4389 `${this.sliderId}-item-${currentSlide}` 4390 ); 4391 } 4392 if (!slide.hasAttribute(slideLabelTag) || slide.getAttribute(slideLabelTag) === "" || hasAutoLabel) { 4393 slide.setAttribute( 4394 slideLabelTag, 4395 `${currentSlide} ${this.slideLabelSepparator} ${totalSlides}` 4396 ); 4397 } 4398 if (slide.tagName.toLowerCase() === "div" && !slide.hasAttribute("role")) { 4399 slide.setAttribute("role", "group"); 4400 } 4401 if (this.pager && !isImage) { 4402 slide.setAttribute("role", "tabpanel"); 4403 } 4404 if (slide.getAttribute("role") === "group") { 4405 slide.setAttribute("aria-roledescription", "item"); 4406 } 4407 }); 4408 if (!this.el.hasAttribute("role") && this.el.tagName !== "SECTION") { 4409 this.el.setAttribute("role", "region"); 4410 } 4411 if (!this.el.hasAttribute("aria-roledescription")) { 4412 this.el.setAttribute("aria-roledescription", "carousel"); 4413 } 4414 if (!this.track.hasAttribute("tabindex")) { 4415 this.track.setAttribute("tabindex", 0); 4416 } 4417 this.track.setAttribute("aria-live", "polite"); 4418 } 4419 setupNav() { 4420 this.navBtns.forEach((btn) => { 4421 btn.setAttribute("disabled", ""); 4422 btn.removeAttribute("hidden"); 4423 btn.classList.remove("invisible"); 4424 btn.style.visibility = null; 4425 }); 4426 } 4427 createPager() { 4428 const newPager = document.createElement("nav"); 4429 newPager.setAttribute("data-pager", ""); 4430 newPager.setAttribute("role", "tablist"); 4431 newPager.classList.add(...this.pagerClasses.split(" "));
4432 this.slides.forEach((slide, index) => { 4433 const marker = document.createElement("button"); 4434 const slideId = slide.id || `${this.sliderId}-item-${index + 1}`; 4435 marker.setAttribute(this.markerIdName, slideId); 4436 marker.classList.add(...this.markerClasses.split(" ")); 4437 this.setupPagerMarker(marker, index); 4438 newPager.appendChild(marker); 4439 if (slide.tagName.toLowerCase() !== "img" && slide.tagName.toLowerCase() !== "picture") { 4440 slide.removeAttribute("aria-roledescription"); 4441 slide.setAttribute("role", "tabpanel"); 4442 } 4443 }); 4444 if (this.pager) { 4445 this.pager.setAttribute("role", "tablist"); 4446 this.pager.replaceChildren(...newPager.children); 4447 } else { 4448 this.track.after(newPager); 4449 this.pager = this.el.querySelector("[data-pager]"); 4450 } 4451 } 4452 setupPager() { 4453 if (!this.pager) return; 4454 this.pager.setAttribute("role", "tablist"); 4455 const items = Array.from(this.pager.querySelectorAll("a, button")); 4456 items.forEach((marker, index) => this.setupPagerMarker(marker, index)); 4457 } 4458 setupPagerMarker(marker, index) { 4459 const markerId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(this.markerIdName); 4460 const slideId = markerId || `${this.sliderId}-item-${index + 1}`; 4461 if (!markerId) { 4462 marker.setAttribute(this.markerIdName, slideId); 4463 } 4464 marker.setAttribute("role", "tab"); 4465 marker.setAttribute("aria-controls", slideId); 4466 marker.setAttribute("aria-posinset", index + 1); 4467 marker.setAttribute("aria-setsize", this.slides.length); 4468 marker.setAttribute("tabindex", "-1"); 4469 if (!marker.hasAttribute("aria-label")) { 4470 marker.setAttribute("aria-label", `Slide ${index + 1}`); 4471 } 4472 } 4473 setupObservers() { 4474 if (this.inViewObserver) { 4475 this.inViewObserver.disconnect(); 4476 } 4477 this.inViewObserver = new IntersectionObserver( 4478 this.handleInView.bind(this), { 4479 root: this.track, 4480 threshold: 0.8 4481 } 4482 ); 4483 this.slides.forEach((slide) => this.inViewObserver.observe(slide)); 4484 } 4485 setupMutationObserver() { 4486 this.mutationObserver = new MutationObserver((mutations) => { 4487 if (mutations.some((m) => m.type === "childList" || m.target.parentElement === this.track)) { 4488 this.refreshSlides(); 4489 } 4490 }); 4491 this.mutationObserver.observe(this.track, { 4492 childList: true, 4493 subtree: true, 4494 attributeFilter: ["style"] 4495 }); 4496 } 4497 setupResizeObserver() { 4498 this.resizeObserver = new ResizeObserver( 4499 this.groupPagerMarkers.bind(this) 4500 ); 4501 this.resizeObserver.observe(this.el); 4502 } 4503 goToSlideDir(dir = "next") { 4504 if (this.isNavigating) return; 4505 const { firstInViewSlide, lastInViewSlide } = this.getInViewItems(); 4506 const isPrev = dir === "prev"; 4507 const referenceSlide = isPrev ? firstInViewSlide : lastInViewSlide; 4508 const currentIndex = this.slides.indexOf(referenceSlide); 4509 let targetIndex = isPrev ? currentIndex - 1 : currentIndex + 1; 4510 if (targetIndex < 0 || targetIndex >= this.slides.length) { 4511 if (!this.useLoop) return; 4512 targetIndex = isPrev ? this.slides.length - 1 : 0; 4513 } 4514 const targetSlide = this.slides[targetIndex]; 4515 if (!targetSlide) return; 4516 this.isNavigating = true; 4517 targetSlide.scrollIntoView({ 4518 block: "nearest", 4519 inline: isPrev ? "end" : "start" 4520 }); 4521 } 4522 pagerToSlide(event) { 4523 if (!event.target.closest("[data-pager]")) return; 4524 const marker = event.target.closest("a, button"); 4525 if (!marker) return; 4526 event.preventDefault(); 4527 const slideId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(this.markerIdName); 4528 const targetSlide = this.track.querySelector(`#${slideId}`);
4529 targetSlide?.scrollIntoView({ block: "nearest", inline: "start" }); 4530 } 4531 eventHandler(event) { 4532 const target = event.target.closest( 4533 "[data-next], [data-prev], [data-pager]" 4534 ); 4535 if (!target) return; 4536 if (event.type === "click") { 4537 if (target.hasAttribute("data-next")) { 4538 this.goToSlideDir("next"); 4539 } else if (target.hasAttribute("data-prev")) { 4540 this.goToSlideDir("prev"); 4541 } else if (target.closest("[data-pager]")) { 4542 this.pagerToSlide(event); 4543 } 4544 } 4545 if (event.type === "keydown" && target.closest("[data-pager]")) { 4546 if (event.key === "ArrowRight" || event.key === "ArrowDown") { 4547 event.preventDefault(); 4548 this.goToSlideDir("next"); 4549 } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") { 4550 event.preventDefault(); 4551 this.goToSlideDir("prev"); 4552 } 4553 } 4554 } 4555 }; 4556 4557 (() => { 4558 function alpineSnapSlider(Alpine) { 4559 Alpine.directive("snap-slider", (el, { 4560 modifiers 4561 }, { 4562 cleanup 4563 }) => { 4564 const slider = new SnapSlider(el, { 4565 autoPager: modifiers.includes("auto-pager"), 4566 groupPager: modifiers.includes("group-pager"), 4567 loop: modifiers.includes("loop"), 4568 forcePager: modifiers.includes("force-pager") 4569 }); 4570 cleanup(() => { 4571 slider.destroy(); 4572 }); 4573 }); 4574 } 4575 4576 document.addEventListener("alpine:init", () => { 4577 window.Alpine.plugin(alpineSnapSlider); 4578 }); 4579 })();</script>
4579
4579<script> 4580 (() => { 4581 4582 function dialog_default(Alpine) { 4583 Alpine.directive("htmldialog", fylgjaAlpineDialog); 4584 function fylgjaAlpineDialog(el, { expression, modifiers }, { evaluateLater, cleanup }) { 4585 const evaluate = expression.length ? evaluateLater(expression) : () => {}; 4586 const lockPageScroll = modifiers.includes("noscroll"); 4587 const isModeless = modifiers.includes("modeless"); 4588 const closeBy = el.getAttribute("closeby") || modifierValue(modifiers, "closeby", "closerequest"); 4589 el.style.display = null; 4590 el.style.length === 0 && el.removeAttribute("style"); 4591 if (el._x_isShown) { 4592 open(); 4593 } 4594 const closeBySupported = (() => { 4595 return "closedBy" in HTMLDialogElement.prototype; 4596 })(); 4597 function scrollLock(use = true) { 4598 document.body.style.overflow = use ? "hidden" : ""; 4599 } 4600 function dialogSubmit(event) { 4601 if (event.target.getAttribute("method") === "dialog" || event.submitter?.getAttribute("formmethod") === "dialog") { 4602 event.preventDefault(); 4603 evaluate(); 4604 } 4605 } 4606 function escapeDialog(event) { 4607 if (event.key !== "Escape") return; 4608 event.preventDefault(); 4609 if (closeBy === "none") return; 4610 evaluate(); 4611 } 4612 function handleCloseByEvent(event) { 4613 if (event.target !== el) return; 4614 const rect = el.getBoundingClientRect(); 4615 const isInDialog = rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width; 4616 if (!isInDialog) { 4617 if (closeBy === "any") { 4618 if (closeBySupported) { 4619 event.preventDefault(); 4620 } 4621 evaluate(); 4622 } 4623 } 4624 } 4625 function handleCloseEvent() { 4626 evaluate(); 4627 } 4628 function open() { 4629 if (el.hasAttribute("open")) return; 4630 isModeless ? el.show() : el.showModal(); 4631 scrollLock(lockPageScroll); 4632 } 4633 function close() { 4634 if (!el.hasAttribute("open")) return; 4635 el.close(); 4636 scrollLock(false); 4637 } 4638 el._x_doShow = () => open(); 4639 el._x_doHide = () => close(); 4640 el.addEventListener("keydown", escapeDialog); 4641 el.addEventListener("submit", dialogSubmit); 4642 el.addEventListener("click", handleCloseByEvent); 4643 el.addEventListener("cancel", handleCloseEvent); 4644 cleanup(() => { 4645 el.removeEventListener("keydown", escapeDialog); 4646 el.removeEventListener("submit", dialogSubmit); 4647 el.removeEventListener("click", handleCloseByEvent); 4648 el.removeEventListener("cancel", handleCloseEvent); 4649 scrollLock(false); 4650 }); 4651 } 4652 } 4653 function modifierValue(modifiers, key, fallback) { 4654 if (modifiers.indexOf(key) === -1) return fallback; 4655 const rawValue = modifiers[modifiers.indexOf(key) + 1]; 4656 if (!rawValue) return fallback; 4657 if (key === "closeby") { 4658 const allowedValues = ["auto", "none", "closerequest", "any"]; 4659 const options = allowedValues.join(", "); 4660 if (!allowedValues.includes(rawValue)) { 4661 console.warn( 4662 `"${rawValue}" is not one of the allowed values for closeby: ${options}` 4663 ); 4664 return fallback; 4665 } 4666 } 4667 return rawValue; 4668 } 4669 4670 4671 document.addEventListener("alpine:init", () => { 4672 window.Alpine.plugin(dialog_default); 4673 }); 4674 })();</script>
4674
4674<script> 4675 (() => { 4676 function src_default(Alpine) { 4677 Alpine.directive("intersect", Alpine.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => { 4678 let evaluate = evaluateLater(expression); 4679 let options = { 4680 rootMargin: getRootMargin(modifiers), 4681 threshold: getThreshold(modifiers) 4682 }; 4683 let observer = new IntersectionObserver((entries) => { 4684 entries.forEach((entry) => { 4685 if (entry.isIntersecting === (value === "leave")) 4686 return; 4687 evaluate(); 4688 modifiers.includes("once") && observer.disconnect(); 4689 }); 4690 }, options); 4691 observer.observe(el); 4692 cleanup(() => { 4693 observer.disconnect(); 4694 }); 4695 })); 4696 } 4697 function getThreshold(modifiers) { 4698 if (modifiers.includes("full")) 4699 return 0.99; 4700 if (modifiers.includes("half")) 4701 return 0.5; 4702 if (!modifiers.includes("threshold")) 4703 return 0; 4704 let threshold = modifiers[modifiers.indexOf("threshold") + 1]; 4705 if (threshold === "100") 4706 return 1; 4707 if (threshold === "0") 4708 return 0; 4709 return Number(`.${threshold}`); 4710 } 4711 function getLengthValue(rawValue) { 4712 let match = rawValue.match(/^(-?[0-9]+)(px|%)?$/); 4713 return match ? match[1] + (match[2] || "px") : void 0; 4714 } 4715 function getRootMargin(modifiers) { 4716 const key = "margin"; 4717 const fallback = "0px 0px 0px 0px"; 4718 const index = modifiers.indexOf(key); 4719 if (index === -1) 4720 return fallback; 4721 let values = []; 4722 for (let i = 1; i < 5; i++) { 4723 values.push(getLengthValue(modifiers[index + i] || "")); 4724 } 4725 values = values.filter((v) => v !== void 0); 4726 return values.length ? values.join(" ").trim() : fallback; 4727 } 4728 4729 document.addEventListener("alpine:init", () => { 4730 window.Alpine.plugin(src_default); 4731 }); 4732 })();</script>
4732
4732<script> 4733 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":"idle"})) { 4734 document.querySelectorAll(selector).forEach(el => el.setAttribute('x-defer', `${deferUntil}`)); 4735 }</script>
4735
4735<script> 4736 (() => { 4737 const hasAlpine = new Promise(resolve => { 4738 window.addEventListener('alpine:initialized', resolve, {once: true, passive: true}); 4739 }); 4740 4741 const hasInteract = new Promise(resolve => { 4742 (events => { 4743 const onInteract = () => { 4744 resolve(); 4745 events.forEach(type => window.removeEventListener(type, onInteract)); 4746 } 4747 events.forEach(type => window.addEventListener(type, onInteract, {once: true, passive: true})) 4748 })(['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown']) 4749 }); 4750 4751 const onIntersect = (el) => { 4752 return new Promise(resolve => { 4753 const observer = new IntersectionObserver(entries => { 4754 for (const entry of entries) { 4755 if (entry.isIntersecting) { 4756 observer.disconnect() 4757 resolve(); 4758 } 4759 } 4760 }, {}); 4761 observer.observe(el); 4762 }); 4763 } 4764 4765 function runComponent(el) { 4766 hasAlpine.then(() => { 4767 el.removeAttribute('x-ignore'); 4768 queueMicrotask(() => Alpine.initTree(el)); 4769 }); 4770 } 4771 4772 function initDeferredComponents() { 4773 document.querySelectorAll('[x-data][x-defer]').forEach(el => { 4774 el.setAttribute('x-ignore', ''); 4775 const deferUntil = (el.getAttribute('x-defer') || '').trim(); 4776 switch (deferUntil) { 4777 case 'interact': 4778 hasInteract.then(() => runComponent(el)); 4779 break; 4780 case 'intersect': 4781 onIntersect(el).then(() => runComponent(el)) 4782 break; 4783 case 'idle': 4784 window.requestIdleCallback 4785 ? window.requestIdleCallback(() => runComponent(el), {timeout: 4000}) 4786 : setTimeout(() => runComponent(el), 4000); 4787 break; 4788 case 'eager': 4789 runComponent(el); 4790 break; 4791 default: 4792 if (deferUntil.startsWith('event:') && deferUntil.length > 6) { 4793 window.addEventListener(deferUntil.substring(6), () => runComponent(el), {once: true, passive: true}); 4794 } 4795 } 4796 }); 4797 } 4798 4799 window.addEventListener('alpine:init', initDeferredComponents, {once: true, passive: true}); 4800 })();</script>
4800
4800<script type="module" 4801 src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Hyva_Theme/js/alpine3.min.js" 4802 defer 4803 crossorigin 4804></script>
4804
4804<script> 4805 function dispatchMessages(messages, hideAfter) { 4806 const messagesEvent = new CustomEvent("messages-loaded", { 4807 detail: { 4808 messages: messages, 4809 hideAfter: hideAfter 4810 } 4811 }); 4812 window.dispatchEvent(messagesEvent); 4813 } 4814 4815 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 4816 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 4817 } 4818 4819 (function( hyva, undefined ) { 4820 4821 hyva.initFormKey = () => { 4822 if (typeof hyva.getFormKey !== 'function') return; 4823 4824 const formKey = hyva.getFormKey(); 4825 if (!formKey) return; 4826 4827 document.querySelectorAll('input[name="form_key"]').forEach((input) => { 4828 input.value = formKey; 4829 }); 4830 } 4831 4832 hyva.initMessages = () => { 4833 try { 4834 const messages = hyva.getCookie('mage-messages'); 4835 window.mageMessages = messages ? JSON.parse(decodeURIComponent(messages).replace(/\+/g, ' ')) : []; 4836 4837 dispatchMessages(window.mageMessages); 4838 4839 const skipSetDomain = true; 4840 hyva.setCookie('mage-messages','', -1, skipSetDomain); 4841 4842 } catch (error) { 4843 console.warn('Error parsing Cookie Messages:', error); 4844 } 4845 } 4846 4847 window.addEventListener('DOMContentLoaded', hyva.initFormKey); 4848 4849 window.addEventListener('pageshow', (event) => { 4850 if (event.persisted) { 4851 hyva.initFormKey(); 4852 } 4853 }); 4854 4855 hyva.alpineInitialized(hyva.initMessages) 4856 }( window.hyva = window.hyva || {} ));</script>
4856
4856<script> 4857 (() => { 4858 const private_content_key = 'mage-cache-storage'; 4859 const private_content_expire_key = 'mage-cache-timeout'; 4860 const private_content_version_key = 'private_content_version'; 4861 const section_data_ids_key = 'section_data_ids'; 4862 const mage_cache_session_id_key = 'mage-cache-sessid'; 4863 const last_visited_store_key = 'last_visited_store'; 4864 4865 const ttl = 3600; 4866 4867 if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) { 4868 console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script"); 4869 } 4870 4871 function loadSectionData () { 4872 const browserStorage = hyva.getBrowserStorage(); 4873 if (!browserStorage) { 4874 typeof window.dispatchMessages !== "undefined" && window.dispatchMessages( 4875 [{ 4876 type: "warning", 4877 text: "Please enable LocalStorage in your browser." 4878 }] 4879 ); 4880 return; 4881 } 4882 try { 4883 let isInvalid = false; 4884 4885 if (hyva.getCookie(last_visited_store_key) !== CURRENT_STORE_CODE) { 4886 isInvalid = true; 4887 } 4888 hyva.setCookie(last_visited_store_key, CURRENT_STORE_CODE, false, false); 4889 4890 if (!hyva.getCookie(mage_cache_session_id_key)) { 4891 isInvalid = true; 4892 browserStorage.removeItem(private_content_key); 4893 const skipSetDomain = true; 4894 const days = false; 4895 hyva.setCookie(mage_cache_session_id_key, true, days, skipSetDomain) 4896 } 4897 4898 const cookieVersion = hyva.getCookie(private_content_version_key); 4899 const storageVersion = browserStorage.getItem(private_content_version_key); 4900 4901 if (cookieVersion && !storageVersion || cookieVersion !== storageVersion) { 4902 isInvalid = true; 4903 } 4904 4905 const privateContentExpires = browserStorage.getItem(private_content_expire_key); 4906 if (privateContentExpires && new Date(privateContentExpires) < new Date()) { 4907 browserStorage.removeItem(private_content_key); 4908 } 4909 4910 if (isInvalid && cookieVersion) { 4911 fetchPrivateContent([]); 4912 } else if (cookieVersion && storageVersion && cookieVersion === storageVersion) { 4913 const privateContent = JSON.parse(browserStorage.getItem(private_content_key)); 4914 if ( 4915 privateContent && 4916 privateContentExpires && 4917 privateContent.cart && 4918 privateContent.customer 4919 ) { 4920 dispatchPrivateContent(privateContent); 4921 } else { 4922 fetchPrivateContent([]); 4923 } 4924 } else { 4925 if (document.getElementById('default-section-data')) { 4926 const privateContent = JSON.parse(document.getElementById('default-section-data').textContent.trim()); 4927 dispatchPrivateContent(privateContent); 4928 } else { 4929 dispatchPrivateContent({}); 4930 } 4931 } 4932 4933 } catch (error) { 4934 console.warn('Error retrieving Private Content:', error); 4935 } 4936 } 4937 4938 hyva.alpineInitialized(loadSectionData) 4939 window.addEventListener('reload-customer-section-data', loadSectionData); 4940 4941 window.processSectionDataBeforeDispatch = window.processSectionDataBeforeDispatch || (data => data); 4942 4943 function dispatchPrivateContent(data) { 4944 4945 const privateContentEvent = new CustomEvent("private-content-loaded", { 4946 detail: { 4947 data: window.processSectionDataBeforeDispatch(data) 4948 } 4949 }); 4950 window.dispatchEvent(privateContentEvent); 4951 } 4952 4953 function fetchPrivateContent(sections) { 4954 fetch(`${BASE_URL}customer/section/load/?sections=${encodeURIComponent(sections.join(','))}`, { 4955 method: 'GET', 4956 headers: { 4957 'Content-Type': 'application/json', 4958 'X-Requested-With': 'XMLHttpRequest' 4959 } 4960 }) 4961 .then(response => response.ok && response.json()) 4962 .then( 4963 data => { 4964 if (data) { 4965 try { 4966 const browserStorage = hyva.getBrowserStorage(); 4967 4968 4969 const oldSectionData = JSON.parse(browserStorage.getItem(private_content_key) || '{}') || {}; 4970 4971 if ((! data.cart || ! data.cart.cartId) && oldSectionData['checkout-data']) { 4972 delete oldSectionData['checkout-data']; 4973 } 4974 const newSectionData = Object.assign(oldSectionData, data); 4975 4976 dispatchPrivateContent(newSectionData); 4977 4978 4979 if (newSectionData.messages && newSectionData.messages.messages ) { 4980 newSectionData.messages.messages = []; 4981 } 4982 4983 browserStorage.setItem(private_content_key, JSON.stringify(newSectionData)); 4984 4985 const expiresAt = new Date(Date.now() + (ttl * 1000)).toISOString(); 4986 browserStorage.setItem(private_content_expire_key, expiresAt); 4987 4988 const newCookieVersion = hyva.getCookie(private_content_version_key); 4989 browserStorage.setItem(private_content_version_key, newCookieVersion); 4990 4991 4992 4993 hyva.setCookie( 4994 section_data_ids_key, 4995 JSON.stringify( 4996 Object.keys(data).reduce((sectionDataIds, sectionKey) => { 4997 sectionDataIds[sectionKey] = data[sectionKey]['data_id']; 4998 return sectionDataIds; 4999 }, {}) 5000 ), 5001 false, 5002 true 5003 ); 5004 } catch (error) { 5005 console.warn("Couldn't store privateContent", error); 5006 } 5007 } 5008 } 5009 ) 5010 .catch(error => { 5011 console.warn("Couldn't fetch privateContent", error); 5012 }); 5013 } 5014 })();</script>
5014
5014<script> 5015 (() => { 5016 document.addEventListener('submit', event => event.target.action = event.target.action.replace('%25uenc%25', hyva.getUenc())); 5017 })()</script>
5017
5017<script> 5018 (events => { 5019 const dispatchUserInteractionEvent = () => { 5020 events.forEach(type => window.removeEventListener(type, dispatchUserInteractionEvent)) 5021 window.dispatchEvent(new Event('init-external-scripts')) 5022 }; 5023 events.forEach(type => window.addEventListener(type, dispatchUserInteractionEvent, {once: true, passive: true})) 5024 })(['touchstart', 'mousemove', 'wheel', 'scroll', 'keydown']);</script>
5024<style> 5025.field.choice:not(.review-field-rating) input[type="radio"]:checked + .label::after, .form .choice:not(.review-field-rating) input[type="radio"]:checked + .label::after{ 5026content:none !important; 5027} 5028</style> 5029
5030<script> 5031 function initConfigurableOptions(productId, optionConfig) { 5032 5033 function findPathParam(key) { 5034 const baseUrl = (BASE_URL.substring(0, 2) === '//' ? 'http:' : '') + BASE_URL; 5035 const baseUrlParts = (new URL(baseUrl)).pathname.replace(/\/$/, '').split('/'); 5036 const pathParts = window.location.pathname.split('/').slice(baseUrlParts.length + 3); 5037 for (let i = 0; i < pathParts.length; i += 2) { 5038 if (pathParts[i] === key && pathParts.length > i) { 5039 return pathParts[i + 1]; 5040 } 5041 } 5042 } 5043 5044 return { 5045 optionConfig, 5046 productId, 5047 itemId: (new URLSearchParams(window.location.search)).get('id') || findPathParam('id'), 5048 allowedAttributeOptions: [], 5049 selectedValues: [], 5050 init() { 5051 this.buildLookupMap(); 5052 this.findAllowedAttributeOptions(); 5053 this.$nextTick(() => { 5054 if (typeof this.optionConfig.defaultValues === 'object') { 5055 for (const [attributeId, value] of Object.entries(this.optionConfig.defaultValues)) { 5056 this.changeOption(attributeId, value + ''); 5057 } 5058 } 5059 }); 5060 }, 5061 productIndex: 0, 5062 productIndexForPrice: 0, 5063 productIndexesMap: null, 5064 productIndexesKeys: null, 5065 attributeCount: 0, 5066 optionIsActive(attributeId, optionId) { 5067 return !!this.getAllowedAttributeOptions(attributeId).find( 5068 option => option.id === optionId 5069 ) 5070 }, 5071 optionIsEnabled(attributeId, optionId) { 5072 for (const productId in this.optionConfig.index) { 5073 if (this.optionConfig.index[productId][attributeId] === optionId) { 5074 return true; 5075 } 5076 } 5077 return false; 5078 }, 5079 findSimpleIndex() { 5080 this.productIndex = this.calculateSimpleIndexForPartialSelection(this.selectedValues); 5081 this.productIndexForPrice = this.findCheapestProductForPartialSelection(this.selectedValues); 5082 }, 5083 calculateSimpleIndexForPartialSelection(selectedValues) { 5084 if (selectedValues.length === 0) return 0; 5085 return this.findProductIdsForPartialSelection(selectedValues)[0]; 5086 }, 5087 buildLookupMap() { 5088 this.productIndexesMap = new Map(); 5089 this.attributeCount = Object.keys(this.optionConfig.attributes).length; 5090 for (const id in this.optionConfig.index) { 5091 const options = this.optionConfig.index[id]; 5092 if (! this.productIndexesKeys) { 5093 this.productIndexesKeys = Object.keys(options).sort(); 5094 } 5095 const key = JSON.stringify(options, this.productIndexesKeys); 5096 this.productIndexesMap.set(key, id); 5097 } 5098 }, 5099 calculateSimpleIndexForFullSelection(selectedValues) { 5100 if (! this.isFullSelection(selectedValues)) return false; 5101 5102 const attrKeys = this.productIndexesKeys || []; 5103 const normalised = {}; 5104 for (let i = 0; i < attrKeys.length; i++) { 5105 normalised[attrKeys[i]] = selectedValues[attrKeys[i]]; 5106 } 5107 const queryKey = JSON.stringify(normalised, attrKeys); 5108 return this.productIndexesMap.get(queryKey); 5109 }, 5110 findAllowedAttributeOptions() { 5111 this.allowedAttributeOptions = this.calculateAllowedAttributeOptions(this.selectedValues); 5112 }, 5113 calculateAllowedAttributeOptions(selectedValues) { 5114 const allAttributes = this.optionConfig.attributes; 5115 const allAttributesSorted = Object.values(allAttributes).sort((a,b) => { 5116 return a.position - b.position 5117 }); 5118 5119 const newAllowedAttributeOptions = []; 5120 5121 allAttributesSorted.forEach(attribute => { 5122 const selectionWithoutAttr = Object.assign({}, this.removeAttrFromSelection(selectedValues, attribute.id)); 5123 const availableIndexes = this.calculateAvailableProductIndexes(selectionWithoutAttr); 5124 newAllowedAttributeOptions[attribute.id] = allAttributes[attribute.id].options.filter(option => { 5125 return !!option.products.find(product => { 5126 return availableIndexes.includes(product); 5127 }) 5128 }); 5129 }); 5130 return newAllowedAttributeOptions; 5131 }, 5132 calculateAvailableProductIndexes(selectedOptions) { 5133 if (Object.keys(selectedOptions).length === 0) { 5134 if (Object.values(this.optionConfig.salable || {}).length) { 5135 return [].concat.apply([], [].concat.apply([], Object.values(this.optionConfig.salable).map(Object.values))).filter((x, i, a) => a.indexOf(x) === i) 5136 } 5137 return Object.keys(this.optionConfig.index); 5138 } 5139 5140 const selectedIds = Object.keys(selectedOptions); 5141 if (Object.values(this.optionConfig.salable || {}).length) { 5142 const selectedOptionIndexes = selectedIds.map(attrId => { 5143 const optionValue = selectedOptions[attrId]; 5144 return this.optionConfig.salable[attrId] && this.optionConfig.salable[attrId][optionValue] || [] 5145 }) 5146 return selectedOptionIndexes.reduce((acc, optionIndexes) => { 5147 return acc.filter(index => optionIndexes.includes(index)); 5148 }); 5149 } else { 5150 const productIndexes = this.optionConfig.index; 5151 return Object.keys(productIndexes).filter(index => { 5152 for (const attrId of selectedIds) { 5153 if (productIndexes[index][attrId] !== `${selectedOptions[attrId]}`) return false 5154 } 5155 return true 5156 }); 5157 } 5158 }, 5159 findAttributeByOptionId(optionId) { 5160 for (const attributeId in this.optionConfig.attributes) { 5161 const attributeOptions = this.optionConfig.attributes[attributeId].options || []; 5162 if (attributeOptions.find(option => option.id === optionId)) { 5163 return attributeId; 5164 } 5165 } 5166 }, 5167 getAllowedAttributeOptions(attributeId) { 5168 return this.allowedAttributeOptions[attributeId] || [] 5169 }, 5170 getAllAttributeOptions(attributeId) { 5171 return ( 5172 this.optionConfig.attributes[attributeId] && 5173 this.optionConfig.attributes[attributeId].options 5174 ) || [] 5175 }, 5176 getProductIdsForOption(option) { 5177 const attributeId = this.findAttributeByOptionId(option.id); 5178 const allOptions = this.optionConfig.attributes[attributeId]; 5179 const opt = (allOptions && allOptions.options || []).find(o => o.id === option.id); 5180 return opt && opt.products 5181 ? opt.products 5182 : []; 5183 }, 5184 findProductIdsForPartialSelection(optionSelection) { 5185 const candidateProducts = Object.values(optionSelection).reduce((candidates, optionId) => { 5186 const newCandidates = this.getProductIdsForOption({id: optionId}); 5187 return candidates === null 5188 ? newCandidates 5189 : candidates.filter(productId => newCandidates.includes(productId)); 5190 }, null); 5191 return candidateProducts || []; 5192 },
5193 findCheapestProductForPartialSelection(optionSelection) { 5194 const candidateProducts = this.findProductIdsForPartialSelection(optionSelection); 5195 return candidateProducts.reduce((cheapest, simpleIdx) => { 5196 // in the first iteration we start with simpleIdx as the currently cheapest product 5197 if (! this.optionConfig.optionPrices[cheapest]) return simpleIdx; 5198 const knownCheapestPrice = this.optionConfig.optionPrices[cheapest].finalPrice.amount; 5199 return knownCheapestPrice > this.optionConfig.optionPrices[simpleIdx].finalPrice.amount 5200 ? simpleIdx 5201 : cheapest; 5202 }, 0) 5203 }, 5204 findProductIdToUseForOptionPrice(option) { 5205 // try to find a product for a complete selection 5206 const attributeId = this.findAttributeByOptionId(option.id); 5207 const optionSelection = Object.assign({}, this.selectedValues, {[attributeId]: option.id}); 5208 const matchingSimpleIndex = this.calculateSimpleIndexForFullSelection(optionSelection); 5209 // if there is no complete selection, use the cheapest product for the option 5210 return matchingSimpleIndex || this.findCheapestProductForPartialSelection(optionSelection); 5211 }, 5212 getAttributeOptionLabel(option) { 5213 const optionProduct = this.findProductIdToUseForOptionPrice(option); 5214 5215 if ((! optionProduct) || (optionProduct === this.productIndexForPrice)) { 5216 return option.label; 5217 } 5218 5219 const currentPrice = this.getOptionPriceAdjustmentBasePrice(); 5220 5221 if (this.optionConfig.optionPrices[optionProduct]) { 5222 const optionPrice = this.optionConfig.optionPrices[optionProduct].finalPrice.amount; 5223 if (optionPrice !== currentPrice){ 5224 return option.label + ' ' + hyva.formatPrice(optionPrice - currentPrice, true); 5225 } 5226 } 5227 return option.label; 5228 }, 5229 getOptionPriceAdjustmentBasePrice() { 5230 if (this.optionConfig.optionPrices[this.productIndexForPrice]) { 5231 return this.optionConfig.optionPrices[this.productIndexForPrice].finalPrice.amount 5232 } 5233 return this.optionConfig.prices.finalPrice.amount; // default price if no option selection 5234 }, 5235 clearOptionIfActive(optionId, value) { 5236 if (this.selectedValues[optionId] === value) { 5237 this.blurLabel() 5238 this.changeOption(optionId, '') 5239 } 5240 }, 5241 removeAttrFromSelection(selectedValues, attributeId) { 5242 attributeId = parseInt(attributeId); 5243 return selectedValues.reduce((newSelection, val, attr) => { 5244 if (attr !== attributeId) { 5245 newSelection[attr] = val; 5246 } 5247 return newSelection; 5248 }, []); 5249 }, 5250 changeOption(attributeId, value) { 5251 if (value === '') { 5252 this.selectedValues = this.removeAttrFromSelection(this.selectedValues, attributeId) 5253 } else if (value && this.getAllowedAttributeOptions(attributeId).find(option => option.id === value)) { 5254 this.selectedValues[attributeId] = value; 5255 } 5256 this.findSimpleIndex(); 5257 this.findAllowedAttributeOptions(); 5258 this.updatePrices(); 5259 this.updateGallery(); 5260 5261 const candidates = this.findProductIdsForPartialSelection(this.selectedValues); 5262 5263 window.dispatchEvent( 5264 new CustomEvent( 5265 'configurable-selection-changed', 5266 { 5267 detail: { 5268 productId: this.productId, 5269 optionId: attributeId, 5270 value: value, 5271 productIndex: this.productIndex, 5272 selectedValues: this.selectedValues, 5273 candidates: candidates, 5274 skuCandidates: Object.values(candidates).map(id =>
5274 this.optionConfig.sku?.[id] ?? null).filter(Boolean), 5275 } 5276 } 5277 ) 5278 ); 5279 }, 5280 calculateIsMinimalPrice() { 5281 return ! this.isFullSelection(this.selectedValues); 5282 }, 5283 isFullSelection(selectedValues) { 5284 return Object.keys(selectedValues).length === this.attributeCount; 5285 }, 5286 updatePrices() { 5287 const value = this.optionConfig.optionPrices[this.productIndexForPrice] || this.optionConfig.prices; 5288 window.dispatchEvent( 5289 new CustomEvent( 5290 "update-prices-" + this.productId, 5291 { 5292 detail: Object.assign( 5293 value, 5294 { isMinimalPrice: this.calculateIsMinimalPrice() } 5295 ) 5296 } 5297 ) 5298 ); 5299 }, 5300 updateGallery () { 5301 if (this.productIndex) { 5302 const images = this.optionConfig.images[this.productIndex]; 5303 images && window.dispatchEvent(new CustomEvent( 5304 "update-gallery", 5305 { detail: this.sortImagesByPosition(images) } 5306 )); 5307 } else { 5308 window.dispatchEvent(new Event("reset-gallery")); 5309 } 5310 }, 5311 sortImagesByPosition(images) { 5312 return images.sort((x, y) => { 5313 return x.position === y.position ? 0 : (parseInt(x.position) > parseInt(y.position) ? 1 : -1) 5314 }); 5315 }, 5316 onGetCartData(data) { 5317 }, 5318 preselectCartItems(data) { 5319 // pre-select options based on cart data for current (quote) itemId 5320 const cart = data && data.cart; 5321 if (cart && cart.items) { 5322 const cartItem = cart.items.find((item) => { 5323 return ( 5324 item.item_id === this.itemId 5325 && item.product_id === this.productId 5326 ) 5327 }); 5328 if (cartItem && cartItem.options && cartItem.options.length) { 5329 cartItem.options.map(option => { 5330 this.changeOption(option.option_id, option.option_value); 5331 }) 5332 } 5333 } 5334 }, 5335 preselectQuerystringItems() { 5336 // pre-select option like ?size=167 5337 const urlQueryParams = new URLSearchParams(window.location.search.replace('?','')); 5338 this.preselectItemsBasedOnLocation(attribute => urlQueryParams.get(attribute.code)); 5339 }, 5340 preselectLocationHashItems() { 5341 // pre-select option like #144=167 5342 const urlHashParams = new URLSearchParams(window.location.hash.replace('#','')); 5343 this.preselectItemsBasedOnLocation(attribute => urlHashParams.get(attribute.id)); 5344 }, 5345 preselectItemsBasedOnLocation(getLocationValue) { 5346 Object.values(this.optionConfig.attributes).map(attribute => { 5347 const v = getLocationValue(attribute); 5348 v && this.changeOption(attribute.id, v) 5349 }); 5350 } 5351 } 5352 } 5353</script>
vendor: 1 bytes, line 5353
5353
5354<script> 5355 function initSwatchOptions(swatchConfig) { 5356 return { 5357 swatchConfig, 5358 getAttributeSwatchData(attributeId) { 5359 const swatchConfig = Object.assign({}, this.swatchConfig[attributeId]); 5360 swatchConfig['details'] = JSON.parse(swatchConfig['additional_data']); 5361 5362 return swatchConfig; 5363 }, 5364 mapSwatchTypeNumberToTypeCode(typeNumber) { 5365 switch ("" + typeNumber) { 5366 case "1": 5367 return "color" 5368 case "2": 5369 return "image" 5370 case "3": 5371 return "empty" 5372 case "0": 5373 default: 5374 return "text" 5375 } 5376 }, 5377 getTypeOfFirstOption(attributeId) { 5378 for (const optionId in this.swatchConfig[attributeId]) { 5379 const option = this.swatchConfig[attributeId][optionId]; 5380 if (typeof option.type !== 'undefined') { 5381 return this.mapSwatchTypeNumberToTypeCode(option.type); 5382 } 5383 } 5384 }, 5385 getVisualSwatchType(attributeId, targetOptionId) { 5386 // If a type configuration is present for the given option id, use it 5387 const config = this.swatchConfig[attributeId]; 5388 if (config[targetOptionId] && typeof config[targetOptionId].type !== 'undefined') { 5389 return this.mapSwatchTypeNumberToTypeCode(config[targetOptionId].type); 5390 } 5391 5392 // Otherwise - if no config is present for the target option - use the type of the first option 5393 // with a type property from the attribute, thus assuming its the same type as the target option. 5394 // (This edge case condition can occur on single swatch products if some options are not salable) 5395 return this.getTypeOfFirstOption(attributeId); 5396 }, 5397 getSwatchType(attributeId, optionId) { 5398 // Deserialize the attribute details the first time they are used 5399 if (this.swatchConfig[attributeId] && ! this.swatchConfig[attributeId].details) { 5400 this.swatchConfig[attributeId] = this.getAttributeSwatchData(attributeId); 5401 } 5402 const type = this.swatchConfig[attributeId] && 5403 this.swatchConfig[attributeId].details && 5404 this.swatchConfig[attributeId].details.swatch_input_type || 5405 "empty"; 5406 return type === 'visual' ? this.getVisualSwatchType(attributeId, optionId) : type; 5407 }, 5408 isTextSwatch(attributeId, optionId) { 5409 return this.getSwatchType(attributeId, optionId) === 'text'; 5410 }, 5411 isVisualSwatch(attributeId, optionId) { 5412 const type = this.getSwatchType(attributeId, optionId); 5413 5414 return ['image', 'color'].includes(type); 5415 }, 5416 getSwatchBackgroundStyle(attributeId, optionId) { 5417 const config = this.getSwatchConfig(attributeId, optionId); 5418 const type = this.getSwatchType(attributeId, optionId); 5419 5420 if (type === "color") { 5421 return 'background-color:' + config.value; 5422 } else if (type === "image") { 5423 return "background: #fff url('" + config.value + "') no-repeat center"; 5424 } else { 5425 return ''; 5426 } 5427 }, 5428 getSwatchText(attributeId, optionId) { 5429 const config = this.getSwatchConfig(attributeId, optionId); 5430 return config.label || config.value || this.getOptionLabelFromOptionConfig(attributeId, optionId); 5431 }, 5432 getOptionLabelFromOptionConfig(attributeId, optionId) { 5433 // Fallback if no value is present in swatchConfig data 5434 // Reference issue https://gitlab.hyva.io/hyva-themes/magento2-default-theme/-/issues/190 5435 const option = this.getAllAttributeOptions(attributeId).filter(option => option.id === optionId); 5436 return option && option[0] && option[0].label ||''; 5437 }, 5438 getSwatchConfig(attributeId, optionId) { 5439 return this.swatchConfig[attributeId] && this.swatchConfig[attributeId][optionId] 5440 ? this.swatchConfig[attributeId][optionId] 5441 : false;
5442 }, 5443 activeTooltipItem: false, 5444 tooltipPositionElement: false, 5445 isTooltipVisible() { 5446 return this.activeTooltipItem && 5447 this.getSwatchConfig( 5448 this.activeTooltipItem.attribute, 5449 this.activeTooltipItem.item 5450 ); 5451 }, 5452 isFirstItemCol() { 5453 return this.activeTooltipItem.index === 0; 5454 }, 5455 getTooltipImageStyle(attributeId, optionId) { 5456 const config = this.getSwatchConfig(attributeId, optionId); 5457 const type = this.getSwatchType(attributeId, optionId); 5458 5459 if (type === "color") { 5460 return 'background-color:' + config.value + '; width: 110px; height: 90px;'; 5461 } else if (type === "image") { 5462 return "background: #ffffff url('" + config.thumb + 5463 "') center center no-repeat; width: 110px; height: 90px;"; 5464 } else { 5465 return 'display:none'; 5466 } 5467 }, 5468 getTooltipPosition() { 5469 return this.tooltipPositionElement ? 5470 `top: ${this.tooltipPositionElement.offsetTop}px;` + 5471 `left: ${ 5472 this.tooltipPositionElement.offsetLeft - ( 5473 this.tooltipPositionElement.closest('.snap') && 5474 this.tooltipPositionElement.closest('.snap').scrollLeft || 5475 0 5476 ) 5477 }px;` : '' 5478 }, 5479 getTooltipLabel() { 5480 return this.getSwatchConfig(this.activeTooltipItem.attribute, this.activeTooltipItem.item).label 5481 }, 5482 focusedLabel: false, 5483 focusLabel(optionId) { 5484 this.focusedLabel = optionId; 5485 }, 5486 blurLabel() { 5487 this.focusedLabel = false; 5488 } 5489 } 5490 } 5491</script>
vendor: 1 bytes, line 5491
5491
5492<script> 5493 'use strict'; 5494 5495 const grecaptchaV2LoadCallbacks = []; 5496 5497 function executeRecaptchaLoadCallbacks () { 5498 while (window.grecaptcha && grecaptchaV2LoadCallbacks.length > 0) { 5499 const callback = grecaptchaV2LoadCallbacks.pop(); 5500 typeof callback === 'string' ? window[callback]() : callback(); 5501 } 5502 } 5503 5504 (function() { 5505 const loadRecaptchaScript = (event) => { 5506 5507 if (window.grecaptcha || !event.target.form) { 5508 return; 5509 } 5510 5511 const url = 'https://www.google.com/recaptcha/api.js?onload=executeRecaptchaLoadCallbacks'; 5512 const siteKey = '6Lc4bWMtAAAAAO0UU2tu07Hp_4JXvRSooTCss9Yb'; 5513 const script = document.createElement('script'); 5514 script.type = 'text/javascript'; 5515 script.src = siteKey ? `${url}&render=${siteKey}` : `${url}&render=explicit`; 5516 script.async = true; 5517 document.head.appendChild(script); 5518 5519 script.addEventListener('load', () => { 5520 Array.from(document.querySelectorAll('button[data-recaptcha-btn]:disabled')).forEach(btn => btn.removeAttribute('disabled')); 5521 }); 5522 } 5523 5524 window.forceLoadRecaptchaScript = (form) => { 5525 loadRecaptchaScript({target: {form: form || 'dummy'}}); 5526 executeRecaptchaLoadCallbacks(); 5527 }; 5528 5529 document.body.addEventListener('input', loadRecaptchaScript, { once: true }); 5530 document.body.addEventListener('focus', loadRecaptchaScript, { once: true }); 5531 5532 }()); 5533 5534</script>
5534 5535 5536 <link rel="stylesheet" href="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_NoraAi/css/chatbot-widget.css?v=1.0.2"><style id="tm-chatbot-document-vars">html{--tm-chatbot-launcher-size:64px;--tm-chatbot-paybar-clearance:96px}</style><div id="tm-chatbot-config" data-config='{"endpoint":"https:\/\/dessertlady.ca\/tm_chatbot\/message\/dryrun\/","speakEndpoint":"https:\/\/dessertlady.ca\/tm_chatbot\/message\/speak\/","transcribeEndpoint":"https:\/\/dessertlady.ca\/tm_chatbot\/message\/transcribe\/","messagesEndpoint":"https:\/\/dessertlady.ca\/tm_chatbot\/message\/sessionMessages\/","shopperContextEndpoint":"https:\/\/dessertlady.ca\/tm_chatbot\/widget\/shopperContext\/","checkoutResetEndpoint":"https:\/\/dessertlady.ca\/tm_chatbot\/widget\/
5536checkoutReset\/","contactEndpoint":"https:\/\/dessertlady.ca\/tm_chatbot\/message\/submitEscalationContact\/","feedbackEndpoint":"https:\/\/dessertlady.ca\/tm_chatbot\/message\/feedback\/","feedbackEnabled":true,"voiceEnabled":false,"voiceUseCloudTts":false,"voiceUseCloudStt":false,"voiceAutoRead":false,"voiceConversationalMode":false,"voiceLang":"en-US","voiceTtsRate":1.15,"storeLocale":"en_CA","voiceConversationalMaxTurns":10,"voiceSilenceThresholdMs":2200,"voiceSpeechMinMs":1000,"voiceSpeechLevelThreshold":0.1,"unansweredContactTimeoutSeconds":30,"mode":"assistant","widgetTitle":"Nora AI","launcherLabel":"Ask Nora","headerSubtitle":"Your customer service AI assistant","welcomeMessage":"Hi, I'm Nora, the AI assistant for Dessert Lady Cafe & Bakery Ltd. How can I help?","assistantName":"Nora","storeDisplayName":"Dessert Lady Cafe & Bakery Ltd","emptyInputHint":"Please enter a message before sending.","unavailableMessage":"Chat is unavailable right now. Please try again in a moment.","serviceErrorMessage":"Unable to reach the chat service. Please try again.","helpNotice":"Guest chats are informational only. Sign in for order-specific help.","maxMessageLength":2000,"messageTooLongReply":"That message is too long. Please shorten it and try again.","persistTranscriptLocally":true,"humanTakeoverPollIntervalSeconds":5,"humanTakeoverLongPollWaitSeconds":25,"humanTakeoverDeliveryMode":"long_poll","labels":{"botName":"Nora AI","botStatus":"Online","youLabel":"You","supportFallback":"Support"},"theme":{"primary":"var(--color-primary, #3b82f6)","primary_end":"var(--color-primary-lighter, var(--color-primary, #2563eb))","primary_foreground":"var(--color-on-primary, #ffffff)","bot_avatar_background":"var(--color-primary, #3b82f6)","messages_background":"#f4f7fb","card":"#ffffff","muted_foreground":"#64748b","border":"rgba(15, 23, 42, 0.12)","input_background":"#ffffff","panel_radius":"1rem","launcher_size":"64px","header_subtitle":"Your customer service AI assistant","launcher_mode":"icon_only","show_unread_badge":true,"paybar_clearance":"96px"},"enabledCapabilities":[{"skill_id":"account.sign_in_guidance","label":"Sign-In Guidance"},{"skill_id":"catalog_discovery.search","label":"Product Search"},{"skill_id":"checkout.cart_status","label":"Cart Status"},{"skill_id":"external_research.web_search","label":"Web Search"},{"skill_id":"fulfillment.store_locator","label":"Store Locator"},{"skill_id":"
5536human_support.handoff","label":"Support Handoff"},{"skill_id":"knowledge_base.approved_entries","label":"Approved Knowledge Entries"},{"skill_id":"orders_logged_in.list","label":"Order List"},{"skill_id":"orders_mutations.cancel","label":"Cancel Order"},{"skill_id":"product_facts.attributes","label":"Attribute Grounding"},{"skill_id":"site_knowledge","label":"Site Knowledge"},{"skill_id":"site_knowledge.store_info","label":"Store Info"},{"skill_id":"checkout.guided_order","label":"Guided Order"},{"skill_id":"human_support.issue_intake","label":"Structured Issue Intake"},{"skill_id":"catalog_discovery","label":"Catalog Discovery"},{"skill_id":"catalog_discovery.bestsellers","label":"Bestsellers"},{"skill_id":"checkout.add_item","label":"Add to Cart"},{"skill_id":"fulfillment.delivery_quote","label":"Delivery Quote"},{"skill_id":"human_support.escalation","label":"Escalation Capture"},{"skill_id":"orders_logged_in.status","label":"Order Status"},{"skill_id":"orders_mutations.update_shipping","label":"Update Shipping"},{"skill_id":"product_facts.promotions","label":"Promotions Grounding"},{"skill_id":"site_knowledge.policies","label":"Store Policies"},{"skill_id":"checkout.cart_modify","label":"Modify Cart"},{"skill_id":"fulfillment.shipping_quote","label":"Magento Shipping Quote"},{"skill_id":"catalog_discovery.product_detail","label":"Product Detail"},{"skill_id":"checkout.address","label":"Shipping Address"},{"skill_id":"fulfillment.availability","label":"Pickup and Delivery Availability"},{"skill_id":"orders_logged_in.summary","label":"Order Summary"},{"skill_id":"orders_mutations.reorder","label":"Reorder"},{"skill_id":"product_facts","label":"Product Facts"},{"skill_id":"product_facts.options_sizes","label":"Options and Sizes"},{"skill_id":"site_knowledge.cms_pages","label":"CMS Pages"},{"skill_id":"catalog_discovery.product_page_context","label":"Product Page Context"},{"skill_id":"checkout.shipping","label":"Shipping Method"},{"skill_id":"fulfillment","label":"Fulfillment"},{"skill_id":"orders_logged_in.tracking","label":"Order Tracking"},{"skill_id":"site_knowledge.site_search","label":"Site Content Search"},{"skill_id":"checkout.payment","label":"Payment"},{"skill_id":"orders_logged_in","label":"Orders (Signed In)"},{"skill_id":"orders_logged_in.cancellation_read","label":"Cancellation Eligibility"},{"skill_id":"checkout.place_order","label":"Place Order"},{"skill_id":"orders_logged_in.fulfillment_status","label":"Fulfillment Production Status"},{"skill_id":"orders_mutations","label":"Order Changes"},{"skill_id":"checkout","label":"Checkout Assistant"},{"skill_id":"
5536human_support","label":"Human Support"},{"skill_id":"knowledge_base","label":"Knowledge Base"},{"skill_id":"external_research","label":"External Research"},{"skill_id":"account","label":"Account Guidance"}],"page":{"type":"other","full_action_name":"cms_index_index","path":"\/","label":""},"reactCheckoutPro":{"enabled":true,"source":"react_checkout_pro","graphqlUrl":"https:\/\/dessertlady.ca\/graphql","shopperContextUrl":"https:\/\/dessertlady.ca\/tm_chatbot\/widget\/shopperContext\/","identityClassifyUrl":"https:\/\/dessertlady.ca\/reactcheckoutpro\/identity\/classify\/","customerDefaultAddressesUrl":"https:\/\/dessertlady.ca\/reactcheckoutpro\/customer\/defaults\/","orderClaimStartUrl":"https:\/\/dessertlady.ca\/reactcheckoutpro\/orderclaim\/start\/","orderClaimVerifyUrl":"https:\/\/dessertlady.ca\/reactcheckoutpro\/orderclaim\/verify\/","loginPageUrl":"https:\/\/dessertlady.ca\/customer\/account\/login\/","createAccountUrl":"https:\/\/dessertlady.ca\/customer\/account\/create\/","loginPostPath":"\/customer\/account\/loginPost\/","forgotPasswordPostPath":"\/customer\/account\/forgotpasswordpost\/","formKeySource":"cookie_or_input","customerFormRecaptcha":{"forms":{"customer_login":{"source":"magento","type":"recaptcha_v3","tokenField":"g-recaptcha-response","v3SiteKey":"6Lc4bWMtAAAAAO0UU2tu07Hp_4JXvRSooTCss9Yb","v3Badge":"inline","invisibleSiteKey":"","invisibleBadge":"inline","checkboxSiteKey":""},"customer_forgot_password":{"source":"magento","type":"","tokenField":"g-recaptcha-response","v3SiteKey":"6Lc4bWMtAAAAAO0UU2tu07Hp_4JXvRSooTCss9Yb","v3Badge":"inline","invisibleSiteKey":"","invisibleBadge":"inline","checkboxSiteKey":""}}}},"socialLogin":{"enabled":true,"providers":{"google":{"label":"Google","loginUrl":"https:\/\/dessertlady.ca\/sociallogin\/index\/login\/provider\/google\/"}}}}'></div><div id="tm-chatbot-widget" class="tm-chatbot tm-chatbot--launcher-icon-only" style="--tm-chatbot-primary:var(--color-primary, #3b82f6);--tm-chatbot-primary-end:var(--color-primary-lighter, var(--color-primary, #2563eb));--tm-chatbot-primary-foreground:var(--color-on-primary, #ffffff);--tm-chatbot-bot-avatar-bg:var(--color-primary, #3b82f6);--tm-chatbot-messages-bg:#f4f7fb;--tm-chatbot-card:#ffffff;--tm-chatbot-muted-foreground:#64748b;--tm-chatbot-border:rgba(15, 23, 42, 0.12);--tm-chatbot-input-bg:#ffffff;--tm-chatbot-radius:16px;--tm-chatbot-launcher-size:64px;--tm-chatbot-paybar-clearance:96px"><button type="button" class="tm-chatbot__toggle" aria-expanded="false" aria-controls="tm-chatbot-panel" aria-label="Ask Nora"><span class="tm-chatbot__toggle-pulse" aria-hidden="true"></span> <span class="tm-chatbot__toggle-icon tm-chatbot__toggle-icon--chat" aria-hidden="true"><svg class="tm-chatbot__icon-svg" viewBox="0 0 24 24" focusable="false">
5536<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> <span class="tm-chatbot__toggle-icon tm-chatbot__toggle-icon--close" aria-hidden="true"><svg class="tm-chatbot__icon-svg" viewBox="0 0 24 24" focusable="false"><path d="M18 6 6 18M6 6l12 12" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round"/></svg></span> <span class="tm-chatbot__toggle-label">Ask Nora</span> <span class="tm-chatbot__badge" hidden aria-live="polite"></span></button> <section id="tm-chatbot-panel" class="tm-chatbot__panel" role="dialog" aria-label="Nora AI" hidden><header class="tm-chatbot__header"><div class="tm-chatbot__header-brand"><span class="tm-chatbot__header-avatar tm-chatbot__header-avatar--bot" aria-hidden="true"></span> <div class="tm-chatbot__header-copy"><span class="tm-chatbot__title">Nora AI</span> <span class="tm-chatbot__subtitle">Your customer service AI assistant</span> <span class="tm-chatbot__status">Online</span></div></div><div class="tm-chatbot__header-actions"> <button type="button" class="tm-chatbot__reset" aria-label="Start a new chat">New chat</button> <button type="button" class="tm-chatbot__close" aria-label="Close chat"><span aria-hidden="true">×</span></button></div></header><div class="tm-chatbot__shopper-banner" hidden><span class="tm-chatbot__shopper-banner-label"></span></div><div class="tm-chatbot__messages" aria-live="polite"></div><section class="tm-chatbot__contact-capture" hidden><p class="tm-chatbot__contact-capture-title">Still waiting for an agent</p><p class="tm-chatbot__contact-capture-copy">Leave your name and email so the store can follow up.</p><label class="tm-chatbot__contact-label" for="tm-chatbot-contact-name">Name</label> <input id="tm-chatbot-contact-name" class="tm-chatbot__contact-input" type="text" name="contact_name" autocomplete="name" required><label class="tm-chatbot__contact-label" for="tm-chatbot-contact-email">Email</label> <input id="tm-chatbot-contact-email" class="tm-chatbot__contact-input" type="email" name="contact_email" autocomplete="email" required><button type="button" class="tm-chatbot__contact-submit">Send details</button> <p class="tm-chatbot__contact-status" hidden></p></section><section class="tm-chatbot__gap-email-capture" hidden><p class="tm-chatbot__contact-capture-title">How can the store reach you?</p><p class="tm-chatbot__contact-capture-copy" data-gap-copy="Nora will ask the store admin and get back to you. First name, last name, email, and phone are not sent as another chat question." data-lead-copy="Leave your first name, last name, email, and phone if you want the store to follow up. This is not sent as another chat question.">Nora will ask the store admin and get back to you. First name, last name, email, and phone are not sent as another chat question.</p><label class="tm-chatbot__contact-label" for="tm-chatbot-gap-first-name">First name</label> <input id="tm-chatbot-gap-first-name" class="tm-chatbot__contact-input" type="text" name="gap_first_name" autocomplete="given-name"><label class="tm-chatbot__contact-label" for="tm-chatbot-gap-last-name">Last name</label> <input id="tm-chatbot-gap-last-name" class="tm-chatbot__contact-input" type="text" name="gap_last_name" autocomplete="family-name"><label class="tm-chatbot__contact-label" for="tm-chatbot-gap-email">Email</label> <input id="tm-chatbot-gap-email" class="tm-chatbot__contact-input" type="email" name="gap_email" autocomplete="email"><label class="tm-chatbot__contact-label" for="tm-chatbot-gap-phone">Phone</label> <input id="tm-chatbot-gap-phone" class="tm-chatbot__contact-input" type="tel" name="gap_phone" autocomplete="tel"><button type="button" class="tm-chatbot__contact-submit tm-chatbot__gap-email-submit">Send details</button> <button type="button" class="tm-chatbot__gap-email-skip">Don't contact me</button></section><form class="tm-chatbot__form" novalidate><div class="tm-chatbot__composer"><label class="tm-chatbot__sr-only" for="tm-chatbot-input">Message</label> <div class="tm-chatbot__attachment-upload" hidden><button type="button" class="tm-chatbot__attachment-button" aria-label="Attach a file">Attach</button> <input id="tm-chatbot-attachment-input" class="tm-chatbot__attachment-input" type="file" accept=".pdf,.jpg,.jpeg,.png" hidden></div><button type="button" class="tm-chatbot__mic" hidden aria-label="Speak a message" title="Speak a message">
5536<svg class="tm-chatbot__mic-icon" viewBox="0 0 24 24" focusable="false" aria-hidden="true"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M19 10v2a7 7 0 0 1-14 0v-2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" x2="12" y1="19" y2="22" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button> <textarea id="tm-chatbot-input" 5537 class="tm-chatbot__input" 5538 rows="1" 5539 placeholder="Type a message..." 5540 aria-required="true"></textarea> <button type="submit" class="tm-chatbot__send" aria-label="Send message"><span class="tm-chatbot__send-icon" aria-hidden="true"></span></button></div><p class="tm-chatbot__voice-status" hidden aria-live="polite"></p> <p class="tm-chatbot__help">Guest chats are informational only. Sign in for order-specific help.</p></form></section></div>
5540<script defer src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_NoraAi/js/chatbot-message-format.js"></script>
5540
5540<script defer src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_NoraAi/js/checkout-login-modal.js?v=0.15.3-newchat"></script>
5540<script defer src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_NoraAi/js/checkout-assistant.js"></script>
5540
5540<script defer src="https://dessertlady.ca/static/version1791040052/frontend/Hyva/Dessertlady/en_CA/Toweringmedia_NoraAi/js/chatbot-widget.js?v=1.0.2"></script>
5540
5540<script type="text/json" id="default-section-data"> 5541 {"messages":[],"customer":[],"compare-products":[],"last-ordered-items":[],"cart":[],"directory-data":{"CA":{"name":"Canada","regions":{"66":{"code":"AB","name":"Alberta"},"67":{"code":"BC","name":"British Columbia"},"68":{"code":"MB","name":"Manitoba"},"70":{"code":"NB","name":"New Brunswick"},"69":{"code":"NL","name":"Newfoundland and Labrador"},"72":{"code":"NT","name":"Northwest Territories"},"71":{"code":"NS","name":"Nova Scotia"},"73":{"code":"NU","name":"Nunavut"},"74":{"code":"ON","name":"Ontario"},"75":{"code":"PE","name":"Prince Edward Island"},"76":{"code":"QC","name":"Quebec"},"77":{"code":"SK","name":"Saskatchewan"},"78":{"code":"YT","name":"Yukon Territory"}}},"US":{"name":"United States","regions":{"1":{"code":"AL","name":"Alabama"},"2":{"code":"AK","name":"Alaska"},"3":{"code":"AS","name":"American Samoa"},"4":{"code":"AZ","name":"Arizona"},"5":{"code":"AR","name":"Arkansas"},"6":{"code":"AE","name":"Armed Forces Africa"},"7":{"code":"AA","name":"Armed Forces Americas"},"8":{"code":"AE","name":"Armed Forces Canada"},"9":{"code":"AE","name":"Armed Forces Europe"},"10":{"code":"AE","name":"Armed Forces Middle East"},"11":{"code":"AP","name":"Armed Forces Pacific"},"12":{"code":"CA","name":"California"},"13":{"code":"CO","name":"Colorado"},"14":{"code":"CT","name":"Connecticut"},"15":{"code":"DE","name":"Delaware"},"16":{"code":"DC","name":"District of Columbia"},"17":{"code":"FM","name":"Federated States Of Micronesia"},"18":{"code":"FL","name":"Florida"},"19":{"code":"GA","name":"Georgia"},"20":{"code":"GU","name":"Guam"},"21":{"code":"HI","name":"Hawaii"},"22":{"code":"ID","name":"Idaho"},"23":{"code":"IL","name":"Illinois"},"24":{"code":"IN","name":"Indiana"},"25":{"code":"IA","name":"Iowa"},"26":{"code":"KS","name":"Kansas"},"27":{"code":"KY","name":"Kentucky"},"28":{"code":"LA","name":"Louisiana"},"29":{"code":"ME","name":"Maine"},"30":{"code":"MH","name":"Marshall Islands"},"31":{"code":"MD","name":"Maryland"},"32":{"code":"MA","name":"Massachusetts"},"33":{"code":"MI","name":"Michigan"},"34":{"code":"MN","name":"Minnesota"},"35":{"code":"MS","name":"Mississippi"},"36":{"code":"MO","name":"Missouri"},"37":{"code":"MT","name":"Montana"},"38":{"code":"NE","name":"Nebraska"},"39":{"code":"NV","name":"Nevada"},"40":{"code":"NH","name":"New Hampshire"},"41":{"code":"NJ","name":"New Jersey"},"42":{"code":"NM","name":"New Mexico"},"43":{"code":"NY","name":"New York"},"44":{"code":"NC","name":"North Carolina"},"45":{"code":"ND","name":"North Dakota"},"46":{"code":"MP","name":"Northern Mariana Islands"},"47":{"code":"OH","name":"Ohio"},"48":{"code":"OK","name":"Oklahoma"},"49":{"code":"OR","name":"Oregon"},"50":{"code":"PW","name":"Palau"},"51":{"code":"PA","name":"Pennsylvania"},"52":{"code":"PR","name":"Puerto Rico"},"53":{"code":"RI","name":"Rhode Island"},"54":{"code":"SC","name":"South Carolina"},"55":{"code":"SD","name":"South Dakota"},"56":{"code":"TN","name":"Tennessee"},"57":{"code":"TX","name":"Texas"},"58":{"code":"UT","name":"Utah"},"59":{"code":"VT","name":"Vermont"},"60":{"code":"VI","name":"Virgin Islands"},"61":{"code":"VA","name":"Virginia"},"62":{"code":"WA","name":"Washington"},"63":{"code":"WV","name":"West Virginia"},"64":{"code":"WI","name":"Wisconsin"},"65":{"code":"WY","name":"Wyoming"}}},"data_id":1791446173},"captcha":[],"instant-purchase":[],"loggedAsCustomer":[],"persistent":[],"review":[],"payments":[],"wishlist":{"items":[]},"amga4_event_pool":[],"rewards":[],"amga4_hyva_event_pool":[],"recently_viewed_product":[],"recently_compared_product":[],"product_data_storage":[],"paypal-billing-agreement":[]}</script>
5541</div></body> 5542</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.