diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ae400f0e4ff..e166efb37cb 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -63,16 +63,6 @@ We relentlessly and continuously optimize code within the constraint of being We Themes must be built with purpose. They shouldn’t support each and every feature in Shopify. -### JavaScript not required, fails gracefully - -_NoJS is our baseline._ - -We extract every bit of speed and functionality out of HTTP, semantic HTML, and CSS before writing our first line of JavaScript. - -JavaScript can only be used to progressively enhance features. JavaScript cannot be required to find or purchase products. And the little JavaScript that we use must always fail gracefully, such that every browser gets the most “enhanced” experience that it can within the capabilities that it has. - ->:information_source: We do so not because we expect buyers to experience our storefronts with JavaScript disabled, but because it keeps us aligned with the other principles: writing fast, server-rendered, Web-native code. - ### Server-rendered _Our main constraint._ diff --git a/README.md b/README.md index 72dcdc29aef..be05149e578 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,13 @@ Dawn represents a HTML-first, JavaScript-only-as-needed approach to theme develo * **Web-native in its purest form:** Themes run on the [evergreen web](https://www.w3.org/2001/tag/doc/evergreen-web/). We leverage the latest web browsers to their fullest, while maintaining support for the older ones through progressive enhancement—not polyfills. * **Lean, fast, and reliable:** Functionality and design defaults to “no” until it meets this requirement. Code ships on quality. Themes must be built with purpose. They shouldn’t support each and every feature in Shopify. -* **JavaScript not required, fails gracefully:** We extract every bit of speed and functionality out of HTTP, semantic HTML, and CSS before writing our first line of JavaScript. JavaScript can only be used to progressively enhance features. * **Server-rendered:** HTML must be rendered by Shopify servers using Liquid. Business logic and platform primitives such as translations and money formatting don’t belong on the client. Async and on-demand rendering of parts of the page is OK, but we do it sparingly as a progressive enhancement. * **Functional, not pixel-perfect:** The Web doesn’t require each page to be rendered pixel-perfect by each browser engine. Using semantic markup, progressive enhancement, and clever design, we ensure that themes remain functional regardless of the browser. You can find a more detailed version of our theme code principles in the [contribution guide](https://github.com/Shopify/dawn/blob/main/.github/CONTRIBUTING.md#theme-code-principles). ## Getting started -We recommend using Dawn as a starting point for theme development. [Learn more on Shopify.dev](https://shopify.dev/themes/getting-started/create). +We recommend using Dawn as a starting point for theme development. [Learn more on Shopify.dev](https://shopify.dev/themes/getting-started/create). > If you're building a theme for the Shopify Theme Store, then you can use Dawn as a starting point. However, the theme that you submit needs to be [substantively different from Dawn](https://shopify.dev/themes/store/requirements#uniqueness) so that it provides added value for merchants. Learn about the [ways that you can use Dawn](https://shopify.dev/themes/tools/dawn#ways-to-use-dawn). @@ -61,7 +60,7 @@ There are a number of really useful tools that the Shopify Themes team uses duri [Shopify CLI](https://github.com/Shopify/shopify-cli) helps you build Shopify themes faster and is used to automate and enhance your local development workflow. It comes bundled with a suite of commands for developing Shopify themes—everything from working with themes on a Shopify store (e.g. creating, publishing, deleting themes) or launching a development server for local theme development. -You can follow this [quick start guide for theme developers](https://github.com/Shopify/shopify-cli#quick-start-guide-for-theme-developers) to get started. +You can follow this [quick start guide for theme developers](https://shopify.dev/docs/themes/tools/cli) to get started. ### Theme Check diff --git a/assets/base.css b/assets/base.css index 4d1025b5542..55b64f0e9e1 100644 --- a/assets/base.css +++ b/assets/base.css @@ -73,26 +73,6 @@ /* base */ -.no-js:not(html) { - display: none !important; -} - -html.no-js .no-js:not(html) { - display: block !important; -} - -.no-js-inline { - display: none !important; -} - -html.no-js .no-js-inline { - display: inline-block !important; -} - -html.no-js .no-js-hidden { - display: none !important; -} - .page-width { max-width: var(--page-width); margin: 0 auto; @@ -594,6 +574,7 @@ details > * { --duration-medium: 300ms; --duration-long: 500ms; --duration-extra-long: 600ms; + --duration-extra-longer: 750ms; --duration-extended: 3s; --ease-out-slow: cubic-bezier(0, 0, 0.3, 1); --animation-slide-in: slideIn var(--duration-extra-long) var(--ease-out-slow) forwards; @@ -714,19 +695,12 @@ summary::-webkit-details-marker { } /* Fallback - for browsers that don't support :focus-visible, a fallback is set for :focus */ -.focused, -.no-js *:focus { +.focused { outline: 0.2rem solid rgba(var(--color-foreground), 0.5); outline-offset: 0.3rem; box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0.5rem 0.4rem rgba(var(--color-foreground), 0.3); } -/* Negate the fallback side-effect for browsers that support :focus-visible */ -.no-js *:focus:not(:focus-visible) { - outline: 0; - box-shadow: none; -} - /* Focus ring - inset */ @@ -737,18 +711,12 @@ summary::-webkit-details-marker { box-shadow: 0 0 0.2rem 0 rgba(var(--color-foreground), 0.3); } -.focused.focus-inset, -.no-js .focus-inset:focus { +.focused.focus-inset { outline: 0.2rem solid rgba(var(--color-foreground), 0.5); outline-offset: -0.2rem; box-shadow: 0 0 0.2rem 0 rgba(var(--color-foreground), 0.3); } -.no-js .focus-inset:focus:not(:focus-visible) { - outline: 0; - box-shadow: none; -} - /* Focus ring - none */ @@ -765,18 +733,12 @@ summary::-webkit-details-marker { box-shadow: 0 0 0 1rem rgb(var(--color-background)), 0 0 0.2rem 1.2rem rgba(var(--color-foreground), 0.3); } -.focus-offset.focused, -.no-js .focus-offset:focus { +.focus-offset.focused { outline: 0.2rem solid rgba(var(--color-foreground), 0.5); outline-offset: 1rem; box-shadow: 0 0 0 1rem rgb(var(--color-background)), 0 0 0.2rem 1.2rem rgba(var(--color-foreground), 0.3); } -.no-js .focus-offset:focus:not(:focus-visible) { - outline: 0; - box-shadow: none; -} - /* component-title */ .title, .title-wrapper-with-link { @@ -2037,34 +1999,14 @@ product-info .loading__spinner:not(.hidden) ~ *, list-style-type: none; } -.no-js details[open] .modal__toggle { - position: absolute; - z-index: 5; -} - .modal__toggle-close { display: none; } -.no-js details[open] svg.modal__toggle-close { - display: flex; - z-index: 1; - height: 1.7rem; - width: 1.7rem; -} - .modal__toggle-open { display: flex; } -.no-js details[open] .modal__toggle-open { - display: none; -} - -.no-js .modal__close-button.link { - display: none; -} - .modal__close-button.link { display: flex; justify-content: center; @@ -2331,13 +2273,25 @@ product-info .loading__spinner:not(.hidden) ~ *, } @keyframes translateAnnouncementSlideIn { - 0% {opacity: 0; transform: translateX(var(--announcement-translate-from))} - 100% {opacity: 1; transform: translateX(0)} + 0% { + opacity: 0; + transform: translateX(var(--announcement-translate-from)); + } + 100% { + opacity: 1; + transform: translateX(0); + } } @keyframes translateAnnouncementSlideOut { - 0% {opacity: 1; transform: translateX(0)} - 100% {opacity: 0; transform: translateX(var(--announcement-translate-to))} + 0% { + opacity: 1; + transform: translateX(0); + } + 100% { + opacity: 0; + transform: translateX(var(--announcement-translate-to)); + } } /* section-header */ @@ -2601,6 +2555,10 @@ product-info .loading__spinner:not(.hidden) ~ *, background: rgba(var(--color-foreground), 0.5); } +.header__icon--account shop-user-avatar { + --shop-avatar-size: 2.8rem; +} + /* Search */ menu-drawer + .header__search { display: none; @@ -2648,10 +2606,6 @@ menu-drawer + .header__search { } } -.no-js .predictive-search { - display: none; -} - details[open] > .search-modal { opacity: 1; animation: animateMenuOpen var(--duration-default) ease; @@ -2671,11 +2625,6 @@ details[open] .modal-overlay::after { height: 100vh; } -.no-js details[open] > .header__icon--search { - top: 1rem; - right: 0.5rem; -} - .search-modal { opacity: 0; border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.08); diff --git a/assets/cart.js b/assets/cart.js index 7727d7ec629..c9bcc076431 100644 --- a/assets/cart.js +++ b/assets/cart.js @@ -43,7 +43,12 @@ class CartItems extends HTMLElement { } onChange(event) { - this.updateQuantity(event.target.dataset.index, event.target.value, document.activeElement.getAttribute('name'), event.target.dataset.quantityVariantId); + this.updateQuantity( + event.target.dataset.index, + event.target.value, + document.activeElement.getAttribute('name'), + event.target.dataset.quantityVariantId + ); } onCartUpdate() { diff --git a/assets/component-card.css b/assets/component-card.css index bc141c85ded..5229713c3d8 100644 --- a/assets/component-card.css +++ b/assets/component-card.css @@ -58,7 +58,8 @@ top: calc(var(--border-width) * -1); left: calc(var(--border-width) * -1); border-radius: var(--border-radius); - box-shadow: var(--shadow-horizontal-offset) var(--shadow-vertical-offset) var(--shadow-blur-radius) rgba(var(--color-shadow), var(--shadow-opacity)); + box-shadow: var(--shadow-horizontal-offset) var(--shadow-vertical-offset) var(--shadow-blur-radius) + rgba(var(--color-shadow), var(--shadow-opacity)); } /* Needed for gradient continuity with or without animation, the transform scopes the gradient to its container which happens already when animation are turned on */ @@ -149,16 +150,130 @@ padding: 1.3rem 1rem; } -.card:not(.ratio)>.card__content { +.card:not(.ratio) > .card__content { grid-template-rows: max-content minmax(0, 1fr) max-content auto; } .card-information .card__information-volume-pricing-note { margin-top: 0.6rem; - line-height: calc(0.5 + .4 / var(--font-body-scale)); + line-height: calc(0.5 + 0.4 / var(--font-body-scale)); color: rgba(var(--color-foreground), 0.75); } +.card__information-volume-pricing-note--button, +.card__information-volume-pricing-note--button.quantity-popover__info-button--icon-with-label { + position: relative; + z-index: 1; + cursor: pointer; + padding: 0; + margin: 0; + text-align: var(--text-alignment); + min-width: auto; +} + +.card__information-volume-pricing-note--button:hover { + text-decoration: underline; +} + +.card__information-volume-pricing-note--button + .global-settings-popup.quantity-popover__info { + transform: initial; + top: auto; + bottom: 4rem; + max-width: 20rem; + width: calc(95% + 2rem); +} + +.card__information-volume-pricing-note--button + .global-settings-popup.quantity-popover__info span:first-of-type { + padding-right: 0.3rem; +} + +.card__information-volume-pricing-note--button-right + .global-settings-popup.quantity-popover__info { + right: 0; + left: auto; +} + +.card__information-volume-pricing-note--button-center + .global-settings-popup.quantity-popover__info { + left: 50%; + transform: translate(-50%); +} + +.card__information-volume-pricing-note--button + .global-settings-popup.quantity-popover__info .quantity__rules { + text-align: left; +} + +@media screen and (min-width: 990px) { + .grid--6-col-desktop .card__content quick-add-bulk .quantity { + width: auto; + } + + .grid--6-col-desktop .card__content quick-add-bulk .quantity__button { + width: calc(3rem / var(--font-body-scale)); + } + + .grid--6-col-desktop .card__information-volume-pricing-note--button + .global-settings-popup.quantity-popover__info { + left: 50%; + transform: translate(-50%); + width: calc(100% + var(--border-width) + 3.5rem); + } + + .grid--6-col-desktop + .card--standard + .card__information-volume-pricing-note--button + + .global-settings-popup.quantity-popover__info { + width: calc(100% + var(--border-width) + 1rem); + } +} + +@media screen and (max-width: 749px) { + .grid--2-col-tablet-down .card__content quick-add-bulk .quantity__button { + width: calc(3.5rem / var(--font-body-scale)); + } + + .grid--2-col-tablet-down + .card--card + .card__information-volume-pricing-note--button + + .global-settings-popup.quantity-popover__info, + .grid--2-col-tablet-down + .card--standard + .card__information-volume-pricing-note--button + + .global-settings-popup.quantity-popover__info { + left: 50%; + transform: translate(-50%); + } + + .grid--2-col-tablet-down + .card--standard + .card__information-volume-pricing-note--button + + .global-settings-popup.quantity-popover__info { + width: 100%; + } + + .grid--2-col-tablet-down + .card--card + .card__information-volume-pricing-note--button + + .global-settings-popup.quantity-popover__info { + width: calc(100% + var(--border-width) + 4rem); + } + + .grid--2-col-tablet-down .card__content quick-add-bulk .quantity { + width: auto; + } +} + +.card-information quantity-popover volume-pricing { + margin-top: 0; +} + +@media screen and (max-width: 989px) { + .card-information quantity-popover .quantity__rules ~ volume-pricing { + margin-top: 0; + } + + .card-information quantity-popover volume-pricing { + margin-top: 4.2rem; + } +} + @media screen and (min-width: 750px) { .card__information { padding-bottom: 1.7rem; @@ -181,7 +296,7 @@ justify-self: flex-end; } -.card:not(.card--horizontal)>.card__content>.card__badge { +.card:not(.card--horizontal) > .card__content > .card__badge { margin: 1.3rem; } @@ -192,7 +307,7 @@ width: 100%; } -.card__inner:not(.ratio)>.card__content { +.card__inner:not(.ratio) > .card__content { height: 100%; } @@ -211,7 +326,10 @@ font-size: calc(var(--font-heading-scale) * 1.2rem); } -.card--horizontal .card-information>*:not(.visually-hidden:first-child)+*:not(.rating):not(.card__information-volume-pricing-note) { +.card--horizontal + .card-information + > *:not(.visually-hidden:first-child) + + *:not(.rating):not(.card__information-volume-pricing-note) { margin-top: 0; } @@ -220,7 +338,6 @@ } @media only screen and (min-width: 750px) { - .card--horizontal .card__heading, .card--horizontal .price__container .price-item, .card--horizontal__quick-add { @@ -228,7 +345,7 @@ } } -.card--card.card--media>.card__content { +.card--card.card--media > .card__content { margin-top: calc(0rem - var(--image-padding)); } @@ -275,22 +392,21 @@ } @media screen and (min-width: 990px) { - - .card .media.media--hover-effect>img:only-child, - .card-wrapper .media.media--hover-effect>img:only-child { + .card .media.media--hover-effect > img:only-child, + .card-wrapper .media.media--hover-effect > img:only-child { transition: transform var(--duration-long) ease; } - .card:hover .media.media--hover-effect>img:first-child:only-child, - .card-wrapper:hover .media.media--hover-effect>img:first-child:only-child { + .card:hover .media.media--hover-effect > img:first-child:only-child, + .card-wrapper:hover .media.media--hover-effect > img:first-child:only-child { transform: scale(1.03); } - .card-wrapper:hover .media.media--hover-effect>img:first-child:not(:only-child) { + .card-wrapper:hover .media.media--hover-effect > img:first-child:not(:only-child) { opacity: 0; } - .card-wrapper:hover .media.media--hover-effect>img+img { + .card-wrapper:hover .media.media--hover-effect > img + img { opacity: 1; transition: transform var(--duration-long) ease; transform: scale(1.03); @@ -318,14 +434,14 @@ padding: 0; } -.card--standard>.card__content .card__information { +.card--standard > .card__content .card__information { padding-left: 0; padding-right: 0; } .card--card.card--media .card__inner .card__information, .card--card.card--text .card__inner, -.card--card.card--media>.card__content .card__badge { +.card--card.card--media > .card__content .card__badge { display: none; } @@ -356,7 +472,7 @@ overflow: hidden; } -.card-information>*+* { +.card-information > * + * { margin-top: 0.5rem; } @@ -364,24 +480,32 @@ width: 100%; } -.card-information>* { +.card-information > * { line-height: calc(1 + 0.4 / var(--font-body-scale)); color: rgb(var(--color-foreground)); } -.card-information>.price { +.card-information > .price { color: rgb(var(--color-foreground)); } -.card--horizontal .card-information>.price { +.card--horizontal .card-information > .price { color: rgba(var(--color-foreground), 0.75); } -.card-information>.rating { +.card-information > .rating { margin-top: 0.4rem; } -.card-information>*:not(.visually-hidden:first-child)+*:not(.rating):not(.card__information-volume-pricing-note) { +/* Specificity needed due to the changes below */ +.card-information + > *:not(.visually-hidden:first-child) + + quantity-popover:not(.rating):not(.card__information-volume-pricing-note), +.card-information .card__information-volume-pricing-note.card__information-volume-pricing-note--button { + margin-top: 0; +} + +.card-information > *:not(.visually-hidden:first-child) + *:not(.rating):not(.card__information-volume-pricing-note) { margin-top: 0.7rem; } @@ -403,7 +527,10 @@ border: 0; /* Border is not currently compatible with image shapes for standard cards. */ background-color: transparent; - filter: drop-shadow(var(--shadow-horizontal-offset) var(--shadow-vertical-offset) var(--shadow-blur-radius) rgba(var(--color-shadow), var(--shadow-opacity))); + filter: drop-shadow( + var(--shadow-horizontal-offset) var(--shadow-vertical-offset) var(--shadow-blur-radius) + rgba(var(--color-shadow), var(--shadow-opacity)) + ); } .card--shape.card--standard:not(.card--text) .card__inner:after { diff --git a/assets/component-cart-drawer.css b/assets/component-cart-drawer.css index c3d7629b8d9..ce318d53c95 100644 --- a/assets/component-cart-drawer.css +++ b/assets/component-cart-drawer.css @@ -100,8 +100,8 @@ cart-drawer:not(.is-empty) .cart-drawer__collection { } .drawer__close svg { - height: 2.4rem; - width: 2.4rem; + height: 2rem; + width: 2rem; } .drawer__contents { @@ -132,7 +132,7 @@ cart-drawer-items.is-empty + .drawer__footer { display: flex; position: relative; line-height: 1; - padding: 1.5rem 0; + padding: 1.5rem 2.8rem 1.5rem 0; } .drawer__footer > details + .cart-drawer__footer { @@ -182,9 +182,6 @@ cart-drawer { .cart-drawer thead { display: inline-table; width: 100%; - position: sticky; - top: 0; - z-index: 2; } cart-drawer-items { @@ -205,7 +202,7 @@ cart-drawer-items { .cart-drawer .cart-item { display: grid; grid-template: repeat(2, auto) / repeat(4, 1fr); - gap: 1.5rem; + gap: 1rem; margin-bottom: 0; } @@ -255,7 +252,7 @@ cart-drawer-items { } .cart-drawer .cart-items td { - padding-top: 2rem; + padding-top: 1.7rem; } .cart-drawer .cart-item > td + td { @@ -312,7 +309,7 @@ cart-drawer-items { } .cart-drawer .tax-note { - margin: 1.2rem 0 1rem auto; + margin: 1.2rem 0 2rem auto; text-align: left; } @@ -386,7 +383,7 @@ cart-drawer-items::-webkit-scrollbar-track-piece { .cart-drawer .quantity-popover__info.global-settings-popup { transform: translateY(0); - right: 0; + top: 100%; } .cart-drawer .cart-item__error { diff --git a/assets/component-cart-items.css b/assets/component-cart-items.css index fc1e7e1bb9a..d2294b4e961 100644 --- a/assets/component-cart-items.css +++ b/assets/component-cart-items.css @@ -337,8 +337,7 @@ cart-remove-button .icon-remove { @media screen and (min-width: 750px) { .cart-items .cart-items__heading--quantity, .cart-item .cart-item__quantity, - .cart-item__quantity--info quantity-popover > *, - .no-js .cart-item .cart-item__quantity--info { + .cart-item__quantity--info quantity-popover > * { padding-left: 5rem; } @@ -349,8 +348,8 @@ cart-remove-button .icon-remove { } } -@media screen and (min-width: 749px) and (max-width: 990px) { +@media screen and (max-width: 989px) { .cart-items .quantity-popover__info-button { - padding-left: 1.5rem; + padding-left: 0; } } diff --git a/assets/component-cart.css b/assets/component-cart.css index 8e107f60cd4..443bb2102b1 100644 --- a/assets/component-cart.css +++ b/assets/component-cart.css @@ -155,7 +155,7 @@ cart-items { width: 100%; } -.cart__ctas > *:not(noscript:first-child) + * { +.cart__ctas > * + * { margin-top: 1rem; } diff --git a/assets/component-facets.css b/assets/component-facets.css index ca766b95d91..25f8599f10a 100644 --- a/assets/component-facets.css +++ b/assets/component-facets.css @@ -134,8 +134,7 @@ box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0.5rem 0.4rem rgba(var(--color-foreground), 0.3); } -.mobile-facets__sort .select__select.focused, -.no-js .mobile-facets__sort .select__select:focus { +.mobile-facets__sort .select__select.focused { outline: 0.2rem solid rgba(var(--color-foreground), 0.5); outline-offset: 0.3rem; box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0.5rem 0.4rem rgba(var(--color-foreground), 0.3); @@ -147,19 +146,12 @@ box-shadow: 0 0 0 1rem rgb(var(--color-background)), 0 0 0.2rem 1.2rem rgba(var(--color-foreground), 0.3); } -.facet-filters__sort.focused, -.no-js .facet-filters__sort:focus { +.facet-filters__sort.focused { outline: 0.2rem solid rgba(var(--color-foreground), 0.5); outline-offset: 1rem; box-shadow: 0 0 0 1rem rgb(var(--color-background)), 0 0 0.2rem 1.2rem rgba(var(--color-foreground), 0.3); } -.no-js .facet-filters__sort:focus:not(:focus-visible), -.no-js .mobile-facets__sort .select__select:focus:not(:focus-visible) { - outline: 0; - box-shadow: none; -} - .facets { display: block; grid-column-start: span 2; @@ -301,10 +293,17 @@ .facets-layout-grid { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(3, minmax(0, 1fr)); text-align: center; padding: 2rem 2.4rem; - gap: 3rem 1rem; +} + +.facets-layout-list--swatch { + --swatch-input--size: 2.4rem; +} + +.facets-layout-grid.facets-layout-grid { + gap: 1rem; } .facets-layout-grid.facets__list--vertical { @@ -317,8 +316,8 @@ } /* Hover/focus state */ -.facets-layout-list .facets__label:hover .facet-checkbox__text, -.facets-layout-list input:focus ~ .facet-checkbox__text { +.facets-layout-list .facets__label:hover .facet-checkbox__text-label, +.facets-layout-list input:focus ~ .facet-checkbox__text-label { text-decoration: underline; } @@ -326,17 +325,110 @@ align-items: flex-start; } -.facets-layout-grid .visual-display-parent { +.facets-layout-grid .facets__label { display: flex; flex-direction: column; - gap: 0.8rem; padding: 0; height: 100%; font-size: 1.3rem; } -.facets__item label, -.facets__item input[type='checkbox'] { +/* Image filter specific styles */ +.facets-layout-grid .facets__image-wrapper { + display: block; + position: relative; + aspect-ratio: 1 / 1; + min-height: 0; + padding: 0.4rem; +} + +.facets__image { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* Default state */ +.facets-layout-grid--image .facets__label { + outline-style: solid; + outline-color: transparent; + transition-property: outline-color, outline-width, box-shadow; + transition-duration: var(--duration-short); + transition-timing-function: ease; +} + +.facets-layout-grid--image .facet-checkbox__text { + padding: 0.4rem; +} + +/* Active state */ +.facets-layout-grid--image .facets__label.active { + outline-color: rgb(var(--color-foreground)); + outline-width: 0.1rem; +} + +/* Hover state */ +.facets-layout-grid--image .facets__label:hover { + outline-color: rgba(var(--color-foreground), 0.4); + outline-width: 0.2rem; +} + +/* Focus visible */ +.facets-layout-grid--image .facets__label:has(:focus-visible) { + outline-color: rgba(var(--color-foreground), 0.5); + outline-width: 0.2rem; + box-shadow: 0px 0px 3px 1px rgba(var(--color-foreground), 0.25); +} + +/* Focused state */ +.facets-layout-grid--image .facets__label.active:has(:focus-visible) { + outline-color: rgb(var(--color-foreground)); + outline-width: 0.1rem; + box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0 0.5rem rgba(var(--color-foreground), 0.5), + 0 0 0.7rem 0.1rem rgba(var(--color-foreground), 0.25); +} + +/* Disabled state */ +.facets-layout-grid--image .facets__label.disabled { + /* Note: disabled gets its transparency set by a parent, so no need to set an alpha here. Also, we move the outline to the child when disabled */ + outline: none; +} + +.facets-layout-grid--image .facets__label.disabled .facets__image-wrapper { + outline: 0.1rem solid rgb(var(--color-foreground)); +} + +.facets-layout-grid--image .facets__label.disabled .facets__image-wrapper:before { + content: ''; + position: absolute; + bottom: 0; + left: 0; + /* 100% * square root of 2, this is not a magic number, it is math! */ + width: 141.4%; + height: 0.1rem; + background-color: rgb(var(--color-foreground)); + transform: rotate(-45deg); + transform-origin: left; +} + +.facets-layout-grid--image .facets__label.disabled .disabled-line { + position: absolute; + inset: 0; + height: 100%; + width: 100%; + background: transparent; + margin: 0; +} + +.facets-layout-grid--image .facets__label.disabled .disabled-line line { + stroke: rgb(var(--color-foreground)); + stroke-width: 1; +} + +/* End of image filter specific styles */ + +.list-menu__item label, +.list-menu__item input[type='checkbox'] { cursor: pointer; } @@ -350,18 +442,32 @@ } /* Hover, active, and focus states */ -:is(.facets__label:hover, .facets__label.active, .facets__label:has(:focus-visible)) { +.facets__label:hover, +.facets__label.active, +.facets__label:has(:focus-visible) { color: rgba(var(--color-foreground), 1); } -/* Focus states for older browsers */ -@supports not selector(:has(a, b)) { - .facets__label:focus-within { - color: rgba(var(--color-foreground), 1); - } +/* Disabled state */ +.facets-layout .facets__label.disabled { + pointer-events: none; +} + +.facets-layout:not(.facets-layout-list--swatch, .facets-layout-grid--image) .facets__label.disabled { + opacity: 0.4; } -.facet-checkbox input[type='checkbox'] { +.facets-layout-grid--image .facets__label.disabled .facets__image-wrapper { + opacity: 0.2; +} + +:is(.facets-layout-list--swatch, .facets-layout-grid--image) .facets__label.disabled .facet-checkbox__text { + opacity: 0.4; +} + +/* End disabled state */ + +.facets-layout-list--text input[type='checkbox'] { position: absolute; opacity: 1; width: 1.6rem; @@ -373,25 +479,23 @@ -webkit-appearance: none; } -.facets-layout-grid input[type='checkbox'] { +.facets-layout-grid input[type='checkbox'], +.facets-layout-list--swatch input[type='checkbox'] { position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; + inset: 0; + z-index: 1; + margin: 0; opacity: 0; } -.facets__visual-display-wrapper { +.facets-layout-list--swatch .facets__label { display: flex; - justify-content: center; - flex-shrink: 0; + align-items: center; + gap: 0.8rem; } -.no-js .facet-checkbox input[type='checkbox'] { - z-index: 0; +.swatch-input-wrapper { + display: flex; } .facet-checkbox > svg { @@ -423,12 +527,6 @@ } } -.facet-checkbox--disabled, -.mobile-facets__label--disabled { - opacity: 0.4; - pointer-events: none; -} - .facets__price { display: flex; padding: 2rem; @@ -459,10 +557,6 @@ button.facets__button { padding-bottom: 1.4rem; } -.facets__button-no-js { - transform: translateY(-0.6rem); -} - .active-facets { display: flex; flex-wrap: wrap; @@ -490,7 +584,7 @@ span.active-facets__button-inner { min-width: 0; padding: 0.5rem 1rem; display: flex; - align-items: stretch; + align-items: center; } span.active-facets__button-inner:before, @@ -565,8 +659,7 @@ a.active-facets__button:focus-visible { outline: transparent solid 1px; } } -a.active-facets__button.focused, -.no-js a.active-facets__button:focus { +a.active-facets__button.focused { outline: none; box-shadow: none; } @@ -577,8 +670,7 @@ a.active-facets__button:focus-visible .active-facets__button-inner { outline: none; } -a.active-facets__button.focused .active-facets__button-inner, -.no-js a.active-facets__button:focus .active-facets__button-inner { +a.active-facets__button.focused .active-facets__button-inner { box-shadow: 0 0 0 0.1rem rgba(var(--color-foreground), 0.2), 0 0 0 0.2rem rgb(var(--color-background)), 0 0 0 0.4rem rgb(var(--color-foreground)); outline: none; @@ -811,10 +903,6 @@ details.menu-opening .mobile-facets__close svg { grid-column-start: 2; } -.no-js .mobile-facets__close-button { - display: none; -} - .mobile-facets__close-button .icon-arrow { transform: rotate(180deg); margin-right: 1rem; @@ -834,10 +922,6 @@ details.menu-opening .mobile-facets__close svg { transform: rotate(180deg); } -.no-js .mobile-facets__details { - border-bottom: 1px solid rgba(var(--color-foreground), 0.04); -} - .mobile-facets__highlight { position: absolute; top: 0px; @@ -968,10 +1052,6 @@ input.mobile-facets__checkbox { width: 50%; } -.mobile-facets__footer noscript .button { - width: 100%; -} - .mobile-facets__sort { display: flex; justify-content: space-between; @@ -985,11 +1065,6 @@ input.mobile-facets__checkbox { width: auto; } -.no-js .mobile-facets__sort .select { - position: relative; - right: -1rem; -} - .mobile-facets__sort .select .icon-caret { right: 0; } @@ -1081,10 +1156,6 @@ input.mobile-facets__checkbox { display: none; } - .no-js .facets-vertical .facets-wrapper--no-filters { - display: block; - } - .facets-vertical .product-grid-container { width: 100%; } @@ -1103,17 +1174,6 @@ input.mobile-facets__checkbox { margin-bottom: 2rem; } - .facets-vertical .no-js .facets__button-no-js { - transform: none; - margin-left: 0; - } - - .facets-vertical .no-js .facet-filters__field { - justify-content: flex-start; - padding-bottom: 1rem; - padding-top: 2rem; - } - .facets-vertical .facets__price { padding: 0.5rem 0.5rem 0.5rem 0; } @@ -1126,20 +1186,10 @@ input.mobile-facets__checkbox { margin-bottom: 1.5rem; } - .no-js .facets-vertical .facet-filters.sorting { - padding-left: 0; - flex-direction: column; - } - .facets-vertical .facet-checkbox input[type='checkbox'] { z-index: 0; } - .no-js .facets-vertical .facets-container { - display: flex; - flex-direction: column; - } - .facets-vertical .active-facets facet-remove:last-of-type { margin-bottom: 1rem; } diff --git a/assets/component-localization-form.css b/assets/component-localization-form.css index e607b87bc3f..f2cfcd776e5 100644 --- a/assets/component-localization-form.css +++ b/assets/component-localization-form.css @@ -40,12 +40,6 @@ } } -@media screen and (max-width: 989px) { - noscript .localization-form:only-child { - width: 100%; - } -} - .localization-form .button { padding: 1rem; } @@ -82,16 +76,6 @@ background: rgb(var(--color-background)); } -noscript .localization-form__select { - padding-left: 0rem; -} - -@media screen and (min-width: 750px) { - noscript .localization-form__select { - min-width: 20rem; - } -} - .localization-form__select .icon-caret { position: absolute; content: ''; @@ -111,11 +95,6 @@ noscript .localization-form__select { padding-bottom: 1.5rem; } -noscript .localization-selector.link { - padding-top: 1.5rem; - padding-left: 1.5rem; -} - .disclosure .localization-form__select { padding-top: 1.5rem; } @@ -173,11 +152,14 @@ noscript .localization-selector.link { } .country-selector__list { - width: 25.5rem; padding-bottom: 0.95rem; padding-top: 0; } +.country-selector__list--with-multiple-currencies { + width: 25.5rem; +} + .country-selector__close-button { display: none; } @@ -459,7 +441,3 @@ noscript .localization-selector.link { .menu-drawer__localization + .list-social { margin-top: 1rem; } - -.menu-drawer__localization noscript .localization-form__select { - padding: initial; -} diff --git a/assets/component-menu-drawer.css b/assets/component-menu-drawer.css index a8a4fbcf658..2fc887dd153 100644 --- a/assets/component-menu-drawer.css +++ b/assets/component-menu-drawer.css @@ -52,7 +52,6 @@ menu-drawer > details[open] > summary::before { transition: transform var(--duration-default) ease, visibility var(--duration-default) ease; } -.no-js details[open] > .menu-drawer, .js details[open].menu-opening > .menu-drawer, details[open].menu-opening > .menu-drawer__submenu { transform: translateX(0); @@ -70,10 +69,6 @@ details[open].menu-opening > .menu-drawer__submenu { border-style: solid; border-color: rgba(var(--color-foreground), var(--drawer-border-opacity)); } - - .no-js .menu-drawer { - height: auto; - } } .menu-drawer__inner-container { @@ -99,18 +94,6 @@ details[open].menu-opening > .menu-drawer__submenu { overflow-y: auto; } -.no-js .menu-drawer__navigation { - padding: 0; -} - -.no-js .menu-drawer__navigation > ul > li { - border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.04); -} - -.no-js .menu-drawer__submenu ul > li { - border-top: 0.1rem solid rgba(var(--color-foreground), 0.04); -} - .js .menu-drawer__menu li { margin-bottom: 0.2rem; } @@ -121,26 +104,10 @@ details[open].menu-opening > .menu-drawer__submenu { font-size: 1.8rem; } -.no-js .menu-drawer__menu-item { - font-size: 1.6rem; -} - -.no-js .menu-drawer__submenu .menu-drawer__menu-item { - padding: 1.2rem 5.2rem 1.2rem 6rem; -} - -.no-js .menu-drawer__submenu .menu-drawer__submenu .menu-drawer__menu-item { - padding-left: 9rem; -} - .menu-drawer summary.menu-drawer__menu-item { padding-right: 5.2rem; } -.no-js .menu-drawer__menu-item .icon-caret { - right: 3rem; -} - .menu-drawer__menu-item--active, .menu-drawer__menu-item:focus, .menu-drawer__close-button:focus, @@ -154,8 +121,7 @@ details[open].menu-opening > .menu-drawer__submenu { background-color: rgba(var(--color-foreground), 0.08); } -.js .menu-drawer__menu-item .icon-caret, -.no-js .menu-drawer .icon-arrow { +.js .menu-drawer__menu-item .icon-caret { display: none; } @@ -196,10 +162,6 @@ details[open].menu-opening > .menu-drawer__submenu { text-align: left; } -.no-js .menu-drawer__close-button { - display: none; -} - .menu-drawer__close-button .icon-arrow { transform: rotate(180deg); margin-right: 1rem; @@ -247,13 +209,19 @@ details[open].menu-opening > .menu-drawer__submenu { margin-right: 1rem; } +.menu-drawer__account shop-user-avatar { + --shop-avatar-size: 2.4rem; + margin-right: 0.55rem; + margin-left: -0.45rem; +} + .menu-drawer__account:hover .icon-account { transform: scale(1.07); } .menu-drawer .list-social { justify-content: flex-start; - margin-left: -1.25rem + margin-left: -1.25rem; } .menu-drawer .list-social:empty { diff --git a/assets/component-product-variant-picker.css b/assets/component-product-variant-picker.css index f62ef38157f..b698ab80049 100644 --- a/assets/component-product-variant-picker.css +++ b/assets/component-product-variant-picker.css @@ -34,7 +34,7 @@ variant-selects { width: 1px; } -.product-form__input input[type='radio']:not(.disabled) + label > .label-unavailable { +.product-form__input input[type='radio']:not(.disabled):not(.visually-disabled) + label > .label-unavailable { display: none; } @@ -43,7 +43,6 @@ variant-selects { margin-bottom: 1.6rem; } - .product-form__input--dropdown .dropdown-swatch + select { padding-left: calc(2.4rem + var(--swatch-input--size)); } @@ -57,7 +56,6 @@ variant-selects { z-index: 1; } - /* Custom styles for Pill display type */ .product-form__input--pill input[type='radio'] + label { border: var(--variant-pills-border-width) solid rgba(var(--color-foreground), var(--variant-pills-border-opacity)); @@ -134,12 +132,6 @@ variant-selects { box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0 0.5rem rgba(var(--color-foreground), 0.55); } -/* No outline when focus-visible is available in the browser */ -.no-js .product-form__input--pill input[type='radio']:focus:not(:focus-visible) + label { - box-shadow: none; -} -/* End custom styles for Pill display type */ - /* Custom styles for Swatch display type */ .product-form__input--swatch { display: flex; @@ -147,6 +139,8 @@ variant-selects { } .product-form__input--swatch .swatch-input__input + .swatch-input__label { + --swatch-input--size: 3.6rem; + margin: 0.7rem 1.2rem 0.2rem 0; } diff --git a/assets/component-progress-bar.css b/assets/component-progress-bar.css new file mode 100644 index 00000000000..64c009dbcf1 --- /dev/null +++ b/assets/component-progress-bar.css @@ -0,0 +1,33 @@ +.progress-bar-container { + width: 100%; + margin: auto; +} + +.progress-bar { + height: 0.13rem; + width: 100%; +} + +.progress-bar-value { + width: 100%; + height: 100%; + background-color: rgb(var(--color-foreground)); + animation: indeterminateAnimation var(--duration-extra-longer) infinite ease-in-out; + transform-origin: 0; +} + +.progress-bar .progress-bar-value { + display: block; +} + +@keyframes indeterminateAnimation { + 0% { + transform: translateX(-20%) scaleX(0); + } + 40% { + transform: translateX(30%) scaleX(0.7); + } + 100% { + transform: translateX(100%) scaleX(0); + } +} diff --git a/assets/component-rating.css b/assets/component-rating.css index 2ab84ec7673..05bbcfc96b0 100644 --- a/assets/component-rating.css +++ b/assets/component-rating.css @@ -1,3 +1,7 @@ +.product--no-media .rating-wrapper { + text-align: center; +} + .rating { display: inline-block; margin: 0; diff --git a/assets/component-slider.css b/assets/component-slider.css index a9c911827fb..c0c197ecb64 100644 --- a/assets/component-slider.css +++ b/assets/component-slider.css @@ -23,12 +23,6 @@ slider-component.slider-component-full-width { } } -@media screen and (max-width: 989px) { - .no-js slider-component .slider { - padding-bottom: 3rem; - } -} - .slider__slide { --focus-outline-padding: 0.5rem; --shadow-padding-top: calc((var(--shadow-vertical-offset) * -1 + var(--shadow-blur-radius)) * var(--shadow-visible)); @@ -236,15 +230,6 @@ slider-component.slider-component-full-width { display: none; } -.no-js .slider { - -ms-overflow-style: auto; - scrollbar-width: auto; -} - -.no-js .slider::-webkit-scrollbar { - display: initial; -} - .slider::-webkit-scrollbar-thumb { background-color: rgb(var(--color-foreground)); border-radius: 0.4rem; diff --git a/assets/component-slideshow.css b/assets/component-slideshow.css index 2438c9d3aef..16af7857d8a 100644 --- a/assets/component-slideshow.css +++ b/assets/component-slideshow.css @@ -4,12 +4,6 @@ slideshow-component { flex-direction: column; } -@media screen and (max-width: 989px) { - .no-js slideshow-component .slider { - padding-bottom: 3rem; - } -} - slideshow-component .slideshow.banner { flex-direction: row; flex-wrap: nowrap; diff --git a/assets/component-swatch-input.css b/assets/component-swatch-input.css index 61a42b7277c..f81d93883d2 100644 --- a/assets/component-swatch-input.css +++ b/assets/component-swatch-input.css @@ -1,12 +1,16 @@ /* swatch-input lives in its own file for reusability of the swatch in other areas than the product form context */ .swatch-input__input + .swatch-input__label { - --swatch-input--size: 4.4rem; --swatch-input--border-radius: 50%; - display: inline-block; + max-width: 100%; border-radius: var(--swatch-input--border-radius); cursor: pointer; outline-offset: 0.2rem; + outline-color: transparent; + outline-style: solid; + transition-property: outline-color, outline-width, box-shadow; + transition-duration: var(--duration-short); + transition-timing-function: ease; forced-color-adjust: none; } @@ -14,56 +18,70 @@ --swatch-input--border-radius: 0.2rem; } -.swatch-input__input + .swatch-input__label:hover { +/* Active state */ +.swatch-input__input:active + .swatch-input__label, +.swatch-input__input:checked + .swatch-input__label { + outline: 0.1rem solid rgb(var(--color-foreground)); +} + +/* Hover state */ +.swatch-input__input + .swatch-input__label:hover, +.swatch-input__input:hover + .swatch-input__label { outline: 0.2rem solid rgba(var(--color-foreground), 0.4); } -.swatch-input__input:active + .swatch-input__label, -.swatch-input__input:checked + .swatch-input__label { +/* Focus visible */ +.swatch-input__input:focus-visible + .swatch-input__label { + outline: 0.2rem solid rgba(var(--color-foreground), 0.5); + box-shadow: 0 0 0 0.2rem rgb(var(--color-background)), 0 0 0.1rem 0.5rem rgba(var(--color-foreground), 0.25); +} + +/* Active and focused */ +.swatch-input__input:active:focus-visible + .swatch-input__label, +.swatch-input__input:checked:focus-visible + .swatch-input__label { outline: 0.1rem solid rgb(var(--color-foreground)); + box-shadow: 0 0 0 0.2rem rgb(var(--color-background)), 0 0 0.1rem 0.4rem rgba(var(--color-foreground), 0.25); } /* Visually disabled */ -.swatch-input__input.disabled:not(:active):not(:checked) + .swatch-input__label:hover { +.swatch-input__input.visually-disabled:not(:active):not(:checked) + .swatch-input__label { + transition: none; +} +.swatch-input__input.visually-disabled:not(:active):not(:checked) + .swatch-input__label:hover { outline: none; } -/* Focus visible */ -.swatch-input__input:focus-visible + .swatch-input__label { - box-shadow: 0 0 0 0.5rem rgb(var(--color-background)), 0 0 0 0.7rem rgba(var(--color-foreground), 0.55); +/* Actually disabled */ +.swatch-input__input:disabled + .swatch-input__label { + pointer-events: none; } /* Overrides for swatch snippet when used inside disabled swatch-input */ .swatch-input__input:disabled + .swatch-input__label > .swatch, -.swatch-input__input.disabled + .swatch-input__label > .swatch { +.swatch-input__input.visually-disabled + .swatch-input__label > .swatch { position: relative; overflow: hidden; } -/* Display white semi-transparent overlay over swatch when input is disabled */ -.swatch-input__input:disabled + .swatch-input__label > .swatch::before, -.swatch-input__input.disabled + .swatch-input__label > .swatch::before { - content: ''; - position: absolute; - inset: 0; - background: rgba(250, 250, 250, 0.5); +/* Disabled styles */ +.swatch-input__input:disabled + .swatch-input__label > .swatch, +.swatch-input__input.visually-disabled + .swatch-input__label > .swatch { + opacity: 0.4; } /* Display crossed out line over swatch when input is disabled */ .swatch-input__input:disabled + .swatch-input__label > .swatch::after, -.swatch-input__input.disabled + .swatch-input__label > .swatch::after { +.swatch-input__input.visually-disabled + .swatch-input__label > .swatch::after { /* Diagonal of a square = length of the side * sqrt(2) */ --diagonal--size: calc(var(--swatch-input--size) * 1.414); --crossed-line--size: 0.1rem; - --crossed-line--color: rgb(0, 0, 0); - content: ''; position: absolute; bottom: calc(var(--crossed-line--size) * -0.5); left: 0; width: var(--diagonal--size); height: var(--crossed-line--size); - background-color: var(--crossed-line--color); + background-color: rgb(var(--color-foreground)); transform: rotate(-45deg); transform-origin: left; } diff --git a/assets/component-swatch.css b/assets/component-swatch.css index 75d91b50f0e..ab4a83c23aa 100644 --- a/assets/component-swatch.css +++ b/assets/component-swatch.css @@ -5,11 +5,13 @@ display: block; width: var(--swatch--size); + max-width: 100%; aspect-ratio: 1 / 1; background: var(--swatch--background); + background-position: var(--swatch-focal-point, initial); background-size: cover; background-origin: border-box; - border: 0.1rem solid rgba(var(--color-foreground), 0.45); + border: 0.1rem solid rgba(var(--color-foreground), 0.15); border-radius: var(--swatch--border-radius); } diff --git a/assets/component-visual-display.css b/assets/component-visual-display.css deleted file mode 100644 index f683b455d7e..00000000000 --- a/assets/component-visual-display.css +++ /dev/null @@ -1,90 +0,0 @@ -.visual-display { - --visual-display__size: min(2.4rem, 100%); - position: relative; - width: var(--visual-display__size); - max-width: 100%; - border: 0.1rem solid rgba(var(--color-foreground), 0.2); - aspect-ratio: 1/1; -} - -.visual-display.empty { - border-style: dashed; -} - -.visual-display--presentation-swatch { - --visual-display__size: min(2.4rem, 100%); - - border-radius: 100%; - overflow: hidden; -} - -.visual-display-parent .visual-display--presentation-swatch { - outline-offset: 0.2rem; -} - -/* Hover, active, and focus states */ -:is( - .visual-display-parent:hover .visual-display--presentation-swatch, - .visual-display-parent.active .visual-display--presentation-swatch, - .visual-display-parent:has(:focus-visible) .visual-display--presentation-swatch - ) { - outline-style: solid; -} - -/* Active state */ -.visual-display-parent.active .visual-display--presentation-swatch { - outline-width: 0.2rem; - outline-color: rgb(var(--color-foreground), 1); -} - -/* Hover state */ -.visual-display-parent:hover .visual-display--presentation-swatch { - outline-width: 0.2rem; - outline-color: rgb(var(--color-foreground), 0.4); -} - -/* Focus state */ -.visual-display-parent:has(:focus-visible) .visual-display--presentation-swatch { - outline-width: 0.2rem; - outline-color: rgb(var(--color-foreground), 0.4); - box-shadow: 0 0 0 0.6rem rgb(var(--color-background)), 0 0 0 0.8rem rgba(var(--color-foreground), 0.5), - 0 0 1.2rem 0.4rem rgba(var(--color-foreground), 0.3); -} - -/* Focus state for older browsers */ -@supports not selector(:has(a, b)) { - .visual-display-parent:focus-within .visual-display--presentation-swatch { - outline-offset: 0.2rem; - outline: 0.2rem solid rgb(var(--color-foreground), 0.4); - box-shadow: 0 0 0 0.6rem rgb(var(--color-background)), 0 0 0 0.8rem rgba(var(--color-foreground), 0.5), - 0 0 1.2rem 0.4rem rgba(var(--color-foreground), 0.3); - } -} - -.visual-display-parent.disabled { - opacity: 0.4; - pointer-events: none; -} - -/* Used to display the disabled dash */ -.visual-display-parent.disabled .visual-display::after { - display: block; - content: ''; - - /* 1.414 is not a magic number, it's the square root of 2, or the length of the diagonal */ - width: calc(var(--visual-display__size) * 1.414); - border-bottom: 0.1rem solid rgb(var(--color-background-contrast)); - transform: rotate(-45deg); - transform-origin: left; -} - -.visual-display .visual-display__child { - display: block; - height: 100%; - width: 100%; - forced-color-adjust: none; -} - -.visual-display--presentation-swatch .visual-display__image { - object-fit: cover; -} diff --git a/assets/facets.js b/assets/facets.js index f41ab17a8db..9305e5f3c69 100644 --- a/assets/facets.js +++ b/assets/facets.js @@ -5,7 +5,7 @@ class FacetFiltersForm extends HTMLElement { this.debouncedOnSubmit = debounce((event) => { this.onSubmitHandler(event); - }, 500); + }, 800); const facetForm = this.querySelector('form'); facetForm.addEventListener('input', this.debouncedOnSubmit.bind(this)); @@ -128,6 +128,7 @@ class FacetFiltersForm extends HTMLElement { const jsFilter = event ? event.target.closest('.js-filter') : undefined; return jsFilter ? element.id === jsFilter.id : false; }; + const facetsToRender = Array.from(facetDetailsElementsFromFetch).filter((element) => !matchesId(element)); const countsToRender = Array.from(facetDetailsElementsFromFetch).find(matchesId); @@ -167,7 +168,10 @@ class FacetFiltersForm extends HTMLElement { ? `.mobile-facets__close-button` : `.facets__summary`; const newElementToActivate = newFacetDetailsElement.querySelector(newElementSelector); - if (newElementToActivate) newElementToActivate.focus(); + + const isTextInput = event.target.getAttribute('type') === 'text'; + + if (newElementToActivate && !isTextInput) newElementToActivate.focus(); } } } @@ -268,8 +272,6 @@ class FacetFiltersForm extends HTMLElement { sortFilterForms.forEach((form) => { if (!isMobile) { if (form.id === 'FacetSortForm' || form.id === 'FacetFiltersForm' || form.id === 'FacetSortDrawerForm') { - const noJsElements = document.querySelectorAll('.no-js-list'); - noJsElements.forEach((el) => el.remove()); forms.push(this.createSearchParams(form)); } } else if (form.id === 'FacetFiltersFormMobile') { @@ -300,9 +302,10 @@ FacetFiltersForm.setListeners(); class PriceRange extends HTMLElement { constructor() { super(); - this.querySelectorAll('input').forEach((element) => - element.addEventListener('change', this.onRangeChange.bind(this)) - ); + this.querySelectorAll('input').forEach((element) => { + element.addEventListener('change', this.onRangeChange.bind(this)); + element.addEventListener('keydown', this.onKeyDown.bind(this)); + }); this.setMinAndMaxValues(); } @@ -311,20 +314,27 @@ class PriceRange extends HTMLElement { this.setMinAndMaxValues(); } + onKeyDown(event) { + if (event.metaKey) return; + + const pattern = /[0-9]|\.|,|'| |Tab|Backspace|Enter|ArrowUp|ArrowDown|ArrowLeft|ArrowRight|Delete|Escape/; + if (!event.key.match(pattern)) event.preventDefault(); + } + setMinAndMaxValues() { const inputs = this.querySelectorAll('input'); const minInput = inputs[0]; const maxInput = inputs[1]; - if (maxInput.value) minInput.setAttribute('max', maxInput.value); - if (minInput.value) maxInput.setAttribute('min', minInput.value); - if (minInput.value === '') maxInput.setAttribute('min', 0); - if (maxInput.value === '') minInput.setAttribute('max', maxInput.getAttribute('max')); + if (maxInput.value) minInput.setAttribute('data-max', maxInput.value); + if (minInput.value) maxInput.setAttribute('data-min', minInput.value); + if (minInput.value === '') maxInput.setAttribute('data-min', 0); + if (maxInput.value === '') minInput.setAttribute('data-max', maxInput.getAttribute('data-max')); } adjustToValidValues(input) { const value = Number(input.value); - const min = Number(input.getAttribute('min')); - const max = Number(input.getAttribute('max')); + const min = Number(input.getAttribute('data-min')); + const max = Number(input.getAttribute('data-max')); if (value < min) input.value = min; if (value > max) input.value = max; diff --git a/assets/global.js b/assets/global.js index c541c6fb82e..e4324460f8c 100644 --- a/assets/global.js +++ b/assets/global.js @@ -184,16 +184,28 @@ class QuantityInput extends HTMLElement { event.preventDefault(); const previousValue = this.input.value; - event.target.name === 'plus' ? this.input.stepUp() : this.input.stepDown(); + if (event.target.name === 'plus') { + if (parseInt(this.input.dataset.min) > parseInt(this.input.step) && this.input.value == 0) { + this.input.value = this.input.dataset.min; + } else { + this.input.stepUp(); + } + } else { + this.input.stepDown(); + } + if (previousValue !== this.input.value) this.input.dispatchEvent(this.changeEvent); + + if (this.input.dataset.min === previousValue && event.target.name === 'minus') { + this.input.value = parseInt(this.input.min); + } } validateQtyRules() { const value = parseInt(this.input.value); if (this.input.min) { - const min = parseInt(this.input.min); const buttonMinus = this.querySelector(".quantity__button[name='minus']"); - buttonMinus.classList.toggle('disabled', value <= min); + buttonMinus.classList.toggle('disabled', parseInt(value) <= parseInt(this.input.min)); } if (this.input.max) { const max = parseInt(this.input.max); @@ -361,8 +373,7 @@ class MenuDrawer extends HTMLElement { ); this.querySelectorAll( 'button:not(.localization-selector):not(.country-selector__close-button):not(.country-filter__reset-button)' - ).forEach((button) => button.addEventListener('click', this.onCloseButtonClick.bind(this)) - ); + ).forEach((button) => button.addEventListener('click', this.onCloseButtonClick.bind(this))); } onKeyUp(event) { @@ -767,9 +778,7 @@ class SlideshowComponent extends SliderComponent { this.autoplayButtonIsSetToPlay = true; this.play(); } else { - this.reducedMotion.matches || this.announcementBarArrowButtonWasClicked - ? this.pause() - : this.play(); + this.reducedMotion.matches || this.announcementBarArrowButtonWasClicked ? this.pause() : this.play(); } } @@ -833,10 +842,7 @@ class SlideshowComponent extends SliderComponent { event.target === this.sliderAutoplayButton || this.sliderAutoplayButton.contains(event.target); if (!this.autoplayButtonIsSetToPlay || focusedOnAutoplayButton) return; this.play(); - } else if ( - !this.reducedMotion.matches && - !this.announcementBarArrowButtonWasClicked - ) { + } else if (!this.reducedMotion.matches && !this.announcementBarArrowButtonWasClicked) { this.play(); } } @@ -878,9 +884,7 @@ class SlideshowComponent extends SliderComponent { autoRotateSlides() { const slideScrollPosition = - this.currentPage === this.sliderItems.length - ? 0 - : this.slider.scrollLeft + this.sliderItemOffset; + this.currentPage === this.sliderItems.length ? 0 : this.slider.scrollLeft + this.sliderItemOffset; this.setSlidePosition(slideScrollPosition); this.applyAnimationToAnnouncementBar(); @@ -944,7 +948,7 @@ class SlideshowComponent extends SliderComponent { const slideScrollPosition = this.slider.scrollLeft + this.sliderFirstItemNode.clientWidth * - (this.sliderControlLinksArray.indexOf(event.currentTarget) + 1 - this.currentPage); + (this.sliderControlLinksArray.indexOf(event.currentTarget) + 1 - this.currentPage); this.slider.scrollTo({ left: slideScrollPosition, }); @@ -972,7 +976,6 @@ class VariantSelects extends HTMLElement { this.toggleAddButton(true, '', true); this.setUnavailable(); } else { - this.updateMedia(); this.updateURL(); this.updateVariantInput(); this.renderProductInfo(); @@ -1015,27 +1018,17 @@ class VariantSelects extends HTMLElement { selectedDropdownSwatchValue.style.setProperty('--swatch--background', 'unset'); selectedDropdownSwatchValue.classList.add('swatch--unavailable'); } + + selectedDropdownSwatchValue.style.setProperty( + '--swatch-focal-point', + target.selectedOptions[0].dataset.optionSwatchFocalPoint || 'unset' + ); } else if (tagName === 'INPUT' && target.type === 'radio') { const selectedSwatchValue = this.querySelector(`[data-selected-swatch-value="${name}"]`); if (selectedSwatchValue) selectedSwatchValue.innerHTML = value; } } - updateMedia() { - if (!this.currentVariant) return; - if (!this.currentVariant.featured_media) return; - - const mediaGalleries = document.querySelectorAll(`[id^="MediaGallery-${this.dataset.section}"]`); - mediaGalleries.forEach((mediaGallery) => - mediaGallery.setActiveMedia(`${this.dataset.section}-${this.currentVariant.featured_media.id}`, true) - ); - - const modalContent = document.querySelector(`#ProductModal-${this.dataset.section} .product-media-modal__content`); - if (!modalContent) return; - const newMediaModal = modalContent.querySelector(`[data-media-id="${this.currentVariant.featured_media.id}"]`); - modalContent.prepend(newMediaModal); - } - updateURL() { if (!this.currentVariant || this.dataset.updateUrl === 'false') return; window.history.replaceState({}, '', `${this.dataset.url}?variant=${this.currentVariant.id}`); @@ -1109,12 +1102,77 @@ class VariantSelects extends HTMLElement { if (productForm) productForm.handleErrorMessage(); } + updateMedia(html) { + const mediaGallerySource = document.querySelector(`[id^="MediaGallery-${this.dataset.section}"] ul`); + const mediaGalleryDestination = html.querySelector(`[id^="MediaGallery-${this.dataset.section}"] ul`); + + const refreshSourceData = () => { + const mediaGallerySourceItems = Array.from(mediaGallerySource.querySelectorAll('li[data-media-id]')); + const sourceSet = new Set(mediaGallerySourceItems.map((item) => item.dataset.mediaId)); + const sourceMap = new Map(mediaGallerySourceItems.map((item, index) => [item.dataset.mediaId, { item, index }])); + return [mediaGallerySourceItems, sourceSet, sourceMap]; + }; + + if (mediaGallerySource && mediaGalleryDestination) { + let [mediaGallerySourceItems, sourceSet, sourceMap] = refreshSourceData(); + const mediaGalleryDestinationItems = Array.from(mediaGalleryDestination.querySelectorAll('li[data-media-id]')); + const destinationSet = new Set(mediaGalleryDestinationItems.map(({ dataset }) => dataset.mediaId)); + let shouldRefresh = false; + + // add items from new data not present in DOM + for (let i = mediaGalleryDestinationItems.length - 1; i >= 0; i--) { + if (!sourceSet.has(mediaGalleryDestinationItems[i].dataset.mediaId)) { + mediaGallerySource.prepend(mediaGalleryDestinationItems[i]); + shouldRefresh = true; + } + } + + // remove items from DOM not present in new data + for (let i = 0; i < mediaGallerySourceItems.length; i++) { + if (!destinationSet.has(mediaGallerySourceItems[i].dataset.mediaId)) { + mediaGallerySourceItems[i].remove(); + shouldRefresh = true; + } + } + + // refresh + if (shouldRefresh) [mediaGallerySourceItems, sourceSet, sourceMap] = refreshSourceData(); + + // if media galleries don't match, sort to match new data order + mediaGalleryDestinationItems.forEach((destinationItem, destinationIndex) => { + const sourceData = sourceMap.get(destinationItem.dataset.mediaId); + + if (sourceData && sourceData.index !== destinationIndex) { + mediaGallerySource.insertBefore( + sourceData.item, + mediaGallerySource.querySelector(`li:nth-of-type(${destinationIndex + 1})`) + ); + + // refresh source now that it has been modified + [mediaGallerySourceItems, sourceSet, sourceMap] = refreshSourceData(); + } + }); + } + + if (this.currentVariant.featured_media) { + document + .querySelector(`[id^="MediaGallery-${this.dataset.section}"]`) + ?.setActiveMedia?.(`${this.dataset.section}-${this.currentVariant.featured_media.id}`); + } + + // update media modal + const modalContent = document.querySelector(`#ProductModal-${this.dataset.section} .product-media-modal__content`); + const newModalContent = html.querySelector(`product-modal`); + if (modalContent && newModalContent) modalContent.innerHTML = newModalContent.innerHTML; + } + renderProductInfo() { const requestedVariantId = this.currentVariant.id; const sectionId = this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section; fetch( - `${this.dataset.url}?variant=${requestedVariantId}§ion_id=${this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section + `${this.dataset.url}?variant=${requestedVariantId}§ion_id=${ + this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section }` ) .then((response) => response.text()) @@ -1140,8 +1198,12 @@ class VariantSelects extends HTMLElement { `Volume-${this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section}` ); + this.updateMedia(html); + const pricePerItemDestination = document.getElementById(`Price-Per-Item-${this.dataset.section}`); - const pricePerItemSource = html.getElementById(`Price-Per-Item-${this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section}`); + const pricePerItemSource = html.getElementById( + `Price-Per-Item-${this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section}` + ); const volumePricingDestination = document.getElementById(`Volume-${this.dataset.section}`); const qtyRules = document.getElementById(`Quantity-Rules-${this.dataset.section}`); @@ -1171,8 +1233,7 @@ class VariantSelects extends HTMLElement { if (price) price.classList.remove('hidden'); - if (inventoryDestination) - inventoryDestination.classList.toggle('hidden', inventorySource.innerText === ''); + if (inventoryDestination) inventoryDestination.classList.toggle('hidden', inventorySource.innerText === ''); const addButtonUpdated = html.getElementById(`ProductSubmitButton-${sectionId}`); this.toggleAddButton( @@ -1278,3 +1339,23 @@ class ProductRecommendations extends HTMLElement { } customElements.define('product-recommendations', ProductRecommendations); + +class AccountIcon extends HTMLElement { + constructor() { + super(); + + this.icon = this.querySelector('.icon'); + } + + connectedCallback() { + document.addEventListener('storefront:signincompleted', this.handleStorefrontSignInCompleted.bind(this)); + } + + handleStorefrontSignInCompleted(event) { + if (event?.detail?.avatar) { + this.icon?.replaceWith(event.detail.avatar.cloneNode()); + } + } +} + +customElements.define('account-icon', AccountIcon); diff --git a/assets/media-gallery.js b/assets/media-gallery.js index 5b2520fd591..a7cb8f7c3e5 100644 --- a/assets/media-gallery.js +++ b/assets/media-gallery.js @@ -16,7 +16,7 @@ if (!customElements.get('media-gallery')) { this.elements.thumbnails.querySelectorAll('[data-target]').forEach((mediaToSwitch) => { mediaToSwitch .querySelector('button') - .addEventListener('click', this.setActiveMedia.bind(this, mediaToSwitch.dataset.target, false)); + .addEventListener('click', this.setActiveMedia.bind(this, mediaToSwitch.dataset.target)); }); if (this.dataset.desktopLayout.includes('thumbnail') && this.mql.matches) this.removeListSemantic(); } @@ -28,30 +28,28 @@ if (!customElements.get('media-gallery')) { this.setActiveThumbnail(thumbnail); } - setActiveMedia(mediaId, prepend) { - const activeMedia = this.elements.viewer.querySelector(`[data-media-id="${mediaId}"]`); + setActiveMedia(mediaId) { + const activeMedia = + this.elements.viewer.querySelector(`[data-media-id="${mediaId}"]`) || + this.elements.viewer.querySelector('[data-media-id]'); + if (!activeMedia) { + return; + } this.elements.viewer.querySelectorAll('[data-media-id]').forEach((element) => { element.classList.remove('is-active'); }); - activeMedia.classList.add('is-active'); - - if (prepend) { - activeMedia.parentElement.prepend(activeMedia); - if (this.elements.thumbnails) { - const activeThumbnail = this.elements.thumbnails.querySelector(`[data-target="${mediaId}"]`); - activeThumbnail.parentElement.prepend(activeThumbnail); - } - if (this.elements.viewer.slider) this.elements.viewer.resetPages(); - } + activeMedia?.classList?.add('is-active'); this.preventStickyHeader(); window.setTimeout(() => { - if (this.elements.thumbnails) { + if (!this.mql.matches || this.elements.thumbnails) { activeMedia.parentElement.scrollTo({ left: activeMedia.offsetLeft }); } - if (!this.elements.thumbnails || this.dataset.desktopLayout === 'stacked') { - activeMedia.scrollIntoView({ behavior: 'smooth' }); - } + const activeMediaRect = activeMedia.getBoundingClientRect(); + // Don't scroll if the image is already in view + if (activeMediaRect.top > -0.5) return; + const top = activeMediaRect.top + window.scrollY; + window.scrollTo({ top: top, behavior: 'smooth' }); }); this.playActiveMedia(activeMedia); diff --git a/assets/product-info.js b/assets/product-info.js index cbdeb9e7d57..1fa35239125 100644 --- a/assets/product-info.js +++ b/assets/product-info.js @@ -49,9 +49,13 @@ if (!customElements.get('product-info')) { const max = data.max === null ? data.max : data.max - data.cartQuantity; if (max !== null) min = Math.min(min, max); if (data.cartQuantity >= data.min) min = Math.min(min, data.step); - this.input.min = min; - this.input.max = max; + + if (max) { + this.input.max = max; + } else { + this.input.removeAttribute('max'); + } this.input.value = min; publish(PUB_SUB_EVENTS.quantityUpdate, undefined); } @@ -87,7 +91,11 @@ if (!customElements.get('product-info')) { const attributes = ['data-cart-quantity', 'data-min', 'data-max', 'step']; for (let attribute of attributes) { const valueUpdated = updated.getAttribute(attribute); - if (valueUpdated !== null) current.setAttribute(attribute, valueUpdated); + if (valueUpdated !== null) { + current.setAttribute(attribute, valueUpdated); + } else { + current.removeAttribute(attribute); + } } } else { current.innerHTML = updated.innerHTML; diff --git a/assets/quantity-popover.css b/assets/quantity-popover.css index f7b0c87c987..617923a2d22 100644 --- a/assets/quantity-popover.css +++ b/assets/quantity-popover.css @@ -8,7 +8,7 @@ quantity-popover volume-pricing li:nth-child(odd) { } quantity-popover volume-pricing li { - font-size: 1.4rem; + font-size: 1.2rem; letter-spacing: 0.06rem; padding: 0.6rem 0.8rem; display: flex; @@ -17,7 +17,7 @@ quantity-popover volume-pricing li { .quantity-popover__info.global-settings-popup { width: 100%; - z-index: 2; + z-index: 3; position: absolute; background-color: rgb(var(--color-background)); max-width: 36rem; @@ -58,9 +58,18 @@ quantity-popover quick-order-list-remove-button .button { padding-left: 1rem; } +.quantity-popover__info-button--icon-only--animation svg { + transform: scale(1.25); +} + +.quantity-popover__info-button--icon-only svg { + transition: transform var(--duration-default) ease; +} + @media screen and (max-width: 989px) { .quantity-popover__info.global-settings-popup { left: 0; + top: 100%; } .quantity-popover__info-button { @@ -91,7 +100,7 @@ quantity-popover quick-order-list-remove-button .button { justify-content: flex-end; } -.quantity-popover__info .volume-pricing-label~.button { +.quantity-popover__info .volume-pricing-label ~ .button { top: -0.2rem; } @@ -119,17 +128,12 @@ quantity-popover .quantity__rules span:first-of-type { } @media screen and (min-width: 990px) { - .quantity-popover-container--hover:hover { - background-color: rgba(var(--color-foreground), 0.03); - border-radius: var(--inputs-radius-outset); - } - .quantity-popover-container--empty { margin-right: 2.7rem; } .quantity-popover__info.global-settings-popup { - width: 27rem; + width: 20rem; } .quantity-popover-container { @@ -138,14 +142,11 @@ quantity-popover .quantity__rules span:first-of-type { } .quantity-popover__info.global-settings-popup { - transform: translateY(1rem); + transform: translateX(-100%); + top: 0.5rem; } } -quantity-popover:has(.quantity__input:focus-visible) .quantity-popover__info { - display: block; -} - quantity-popover .quantity { background: rgb(var(--color-background)); } @@ -161,4 +162,4 @@ quantity-popover .quantity__rules .divider:nth-child(2)::before { quantity-popover .quantity__button:not(:focus-visible):not(.focused), quantity-popover .quantity__input:not(:focus-visible):not(.focused) { background-color: initial; -} \ No newline at end of file +} diff --git a/assets/quantity-popover.js b/assets/quantity-popover.js index 3827a31db6d..af45660bbfa 100644 --- a/assets/quantity-popover.js +++ b/assets/quantity-popover.js @@ -10,15 +10,14 @@ if (!customElements.get('quantity-popover')) { this.infoButtonMobile = this.querySelector('.quantity-popover__info-button--icon-with-label'); this.popoverInfo = this.querySelector('.quantity-popover__info'); this.closeButton = this.querySelector('.button-close'); - this.variantInfo = this.querySelector('.quantity-popover-container'); this.eventMouseEnterHappened = false; if (this.closeButton) { this.closeButton.addEventListener('click', this.closePopover.bind(this)); } - if (this.popoverInfo && this.infoButtonDesktop && this.mql.matches) { - this.popoverInfo.addEventListener('mouseenter', this.closePopover.bind(this)); + if (this.popoverInfo && this.infoButtonDesktop && this.mqlTablet.matches) { + this.popoverInfo.addEventListener('mouseleave', this.closePopover.bind(this)); } if (this.infoButtonDesktop) { @@ -28,12 +27,11 @@ if (!customElements.get('quantity-popover')) { if (this.infoButtonMobile) { this.infoButtonMobile.addEventListener('click', this.togglePopover.bind(this)); - this.infoButtonMobile.addEventListener('focusout', this.closePopover.bind(this)); } if (this.infoButtonDesktop && this.mqlTablet.matches) { - this.variantInfo.addEventListener('mouseenter', this.togglePopover.bind(this)); - this.variantInfo.addEventListener('mouseleave', this.closePopover.bind(this)); + this.infoButtonDesktop.addEventListener('mouseenter', this.togglePopover.bind(this)); + this.infoButtonDesktop.addEventListener('mouseleave', this.closePopover.bind(this)); } } @@ -48,29 +46,40 @@ if (!customElements.get('quantity-popover')) { const button = this.infoButtonDesktop && this.mql.matches ? this.infoButtonDesktop : this.infoButtonMobile; const isExpanded = button.getAttribute('aria-expanded') === 'true'; - button.setAttribute('aria-expanded', !isExpanded); + if ((this.mql.matches && !isExpanded) || event.type === 'click') { + button.setAttribute('aria-expanded', !isExpanded); - this.popoverInfo.toggleAttribute('hidden'); + this.popoverInfo.toggleAttribute('hidden'); - const isOpen = button.getAttribute('aria-expanded') === 'true'; + button.classList.toggle('quantity-popover__info-button--open'); + + this.infoButtonDesktop.classList.add('quantity-popover__info-button--icon-only--animation'); + } - button.classList.toggle('quantity-popover__info-button--open'); + const isOpen = button.getAttribute('aria-expanded') === 'true'; if (isOpen && event.type !== 'mouseenter') { button.focus(); + button.addEventListener('keyup', (e) => { + if (e.key === 'Escape') { + this.closePopover(e); + } + }); } } closePopover(event) { event.preventDefault(); - const isChild = this.variantInfo.contains(event.relatedTarget); + const isButtonChild = this.infoButtonDesktop.contains(event.relatedTarget); + const isPopoverChild = this.popoverInfo.contains(event.relatedTarget); const button = this.infoButtonDesktop && this.mql.matches ? this.infoButtonDesktop : this.infoButtonMobile; - if (!event.relatedTarget || !isChild) { + if (!isButtonChild && !isPopoverChild) { button.setAttribute('aria-expanded', 'false'); button.classList.remove('quantity-popover__info-button--open'); this.popoverInfo.setAttribute('hidden', ''); + this.infoButtonDesktop.classList.remove('quantity-popover__info-button--icon-only--animation'); } this.eventMouseEnterHappened = false; diff --git a/assets/quick-add-bulk.js b/assets/quick-add-bulk.js new file mode 100644 index 00000000000..935d6f6a61d --- /dev/null +++ b/assets/quick-add-bulk.js @@ -0,0 +1,252 @@ +if (!customElements.get('quick-add-bulk')) { + customElements.define( + 'quick-add-bulk', + class QuickAddBulk extends HTMLElement { + constructor() { + super(); + this.quantity = this.querySelector('quantity-input'); + + const debouncedOnChange = debounce((event) => { + if (parseInt(event.target.dataset.cartQuantity) === 0) { + this.addToCart(event); + } else { + this.updateCart(event); + } + }, ON_CHANGE_DEBOUNCE_TIMER); + + this.addEventListener('change', debouncedOnChange.bind(this)); + this.listenForActiveInput(); + this.listenForKeydown(); + this.lastActiveInputId = null; + const pageParams = new URLSearchParams(window.location.search); + window.pageNumber = decodeURIComponent(pageParams.get('page') || ''); + } + + connectedCallback() { + this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => { + if (event.source === 'quick-add') { + return; + } + // If its another section that made the update + this.onCartUpdate().then(() => { + this.listenForActiveInput(); + this.listenForKeydown(); + }); + }); + } + + disconnectedCallback() { + if (this.cartUpdateUnsubscriber) { + this.cartUpdateUnsubscriber(); + } + } + + getInput() { + return this.querySelector('quantity-input input'); + } + + selectProgressBar() { + return this.querySelector('.progress-bar-container'); + } + + listenForActiveInput() { + if (!this.classList.contains('hidden')) { + this.getInput().addEventListener('focusin', (event) => event.target.select()); + } + this.isEnterPressed = false; + } + + listenForKeydown() { + this.getInput().addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + this.getInput().blur(); + this.isEnterPressed = true; + } + }); + } + + resetQuantityInput(id) { + const input = document.getElementById(id); + input.value = input.getAttribute('value'); + this.isEnterPressed = false; + } + + cleanErrorMessageOnType(event) { + event.target.addEventListener( + 'keypress', + () => { + event.target.setCustomValidity(''); + }, + { once: true } + ); + } + + onCartUpdate() { + return new Promise((resolve, reject) => { + fetch(`${this.getSectionsUrl()}?section_id=${this.closest('.collection').dataset.id}`) + .then((response) => response.text()) + .then((responseText) => { + const html = new DOMParser().parseFromString(responseText, 'text/html'); + const sourceQty = html.querySelector( + `#quick-add-bulk-${this.dataset.id}-${this.closest('.collection').dataset.id}` + ); + if (sourceQty) { + this.innerHTML = sourceQty.innerHTML; + } + resolve(); + }) + .catch((e) => { + console.error(e); + reject(e); + }); + }); + } + + updateCart(event) { + this.lastActiveInputId = event.target.getAttribute('data-index'); + this.quantity.classList.add('quantity__input-disabled'); + this.selectProgressBar().classList.remove('hidden'); + const body = JSON.stringify({ + quantity: event.target.value, + id: event.target.getAttribute('data-index'), + sections: this.getSectionsToRender().map((section) => section.section), + sections_url: this.getSectionsUrl(), + }); + + fetch(`${routes.cart_change_url}`, { ...fetchConfig('javascript'), ...{ body } }) + .then((response) => { + return response.text(); + }) + .then((state) => { + const parsedState = JSON.parse(state); + this.quantity.classList.remove('quantity__input-disabled'); + if (parsedState.description || parsedState.errors) { + event.target.setCustomValidity(parsedState.description); + event.target.reportValidity(); + this.resetQuantityInput(event.target.id); + this.selectProgressBar().classList.add('hidden'); + event.target.select(); + this.cleanErrorMessageOnType(event); + return; + } + + this.renderSections(parsedState); + + publish(PUB_SUB_EVENTS.cartUpdate, { source: 'quick-add', cartData: parsedState }); + }) + .catch((error) => { + console.log(error, 'error'); + }); + } + + addToCart(event) { + this.quantity.classList.add('quantity__input-disabled'); + this.selectProgressBar().classList.remove('hidden'); + this.lastActiveInputId = event.target.getAttribute('data-index'); + const body = JSON.stringify({ + items: [ + { + quantity: parseInt(event.target.value), + id: parseInt(this.dataset.id), + }, + ], + sections: this.getSectionsToRender().map((section) => section.section), + }); + + fetch(`${routes.cart_add_url}`, { ...fetchConfig('javascript'), ...{ body } }) + .then((response) => { + return response.text(); + }) + .then((state) => { + const parsedState = JSON.parse(state); + this.quantity.classList.remove('quantity__input-disabled'); + if (parsedState.description || parsedState.errors) { + event.target.setCustomValidity(parsedState.description); + event.target.reportValidity(); + this.resetQuantityInput(event.target.id); + this.selectProgressBar().classList.add('hidden'); + event.target.select(); + this.cleanErrorMessageOnType(event); + // Error handling + return; + } + + this.renderSections(parsedState); + + publish(PUB_SUB_EVENTS.cartUpdate, { source: 'quick-add', cartData: parsedState }); + }) + .catch((error) => { + console.error(error); + }); + } + + getSectionsToRender() { + return [ + { + id: `quick-add-bulk-${this.dataset.id}-${this.closest('.collection-quick-add-bulk').dataset.id}`, + section: this.closest('.collection-quick-add-bulk').dataset.id, + selector: `#quick-add-bulk-${this.dataset.id}-${this.closest('.collection-quick-add-bulk').dataset.id}`, + }, + { + id: 'cart-icon-bubble', + section: 'cart-icon-bubble', + selector: '.shopify-section', + }, + { + id: 'CartDrawer', + selector: '#CartDrawer', + section: 'cart-drawer', + }, + ]; + } + + getSectionsUrl() { + if (window.pageNumber) { + return `${window.location.pathname}?page=${window.pageNumber}`; + } else { + return `${window.location.pathname}`; + } + } + + getSectionInnerHTML(html, selector) { + return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML; + } + + renderSections(parsedState) { + this.getSectionsToRender().forEach((section) => { + const sectionElement = document.getElementById(section.id); + if ( + sectionElement && + sectionElement.parentElement && + sectionElement.parentElement.classList.contains('drawer') + ) { + parsedState.items.length > 0 + ? sectionElement.parentElement.classList.remove('is-empty') + : sectionElement.parentElement.classList.add('is-empty'); + + setTimeout(() => { + document.querySelector('#CartDrawer-Overlay').addEventListener('click', this.cart.close.bind(this.cart)); + }); + } + const elementToReplace = + sectionElement && sectionElement.querySelector(section.selector) + ? sectionElement.querySelector(section.selector) + : sectionElement; + if (elementToReplace) { + elementToReplace.innerHTML = this.getSectionInnerHTML( + parsedState.sections[section.section], + section.selector + ); + } + }); + + if (this.isEnterPressed) { + this.querySelector(`#Quantity-${this.lastActiveInputId}`).select(); + } + + this.listenForActiveInput(); + this.listenForKeydown(); + } + } + ); +} diff --git a/assets/quick-add.css b/assets/quick-add.css index 785e1020a3c..b9d889ad5a9 100644 --- a/assets/quick-add.css +++ b/assets/quick-add.css @@ -34,6 +34,10 @@ opacity: 1; } +.quick-add-modal__content.quick-add-modal__content--bulk { + width: 90%; +} + .quick-add-modal__content { --modal-height-offset: 3.2rem; position: absolute; @@ -73,6 +77,114 @@ max-width: 100%; } +.quick-add-modal__content-info--bulk h3 { + margin-bottom: 0.5rem; + margin-top: 0; +} + +.quick-add-modal__content-info--bulk .price, +.quick-add-modal__content-info--bulk .card__information-volume-pricing-note { + display: inline-block; +} + +@media screen and (min-width: 750px) { + .quick-add-modal__content-info--bulk .card__information-volume-pricing-note { + padding-left: 1.6rem; + } +} + +@media screen and (min-width: 990px) { + .quick-add-modal__content-info.quick-add-modal__content-info--bulk { + overflow-y: initial; + } + + .quick-add-modal__content-info--bulk .quick-order-list__table th { + padding-top: 2.5rem; + } + + .quick-add-modal__content-info--bulk thead { + position: sticky; + z-index: 3; + top: 0; + background-color: rgb(var(--color-background)); + } +} + +.quick-add-modal__content-info--bulk .quick-add__product-media, +.quick-add-modal__content-info--bulk .quick-add__product-container, +.quick-add-modal__content-info--bulk .quick-add__info { + width: 4.8rem; + height: 7rem; +} + +.quick-add-modal__content-info--bulk-details { + padding-left: 1rem; +} + +@media screen and (min-width: 990px) { + .quick-add-modal__content-info--bulk .quick-add__product-media, + .quick-add-modal__content-info--bulk .quick-add__product-container, + .quick-add-modal__content-info--bulk .quick-add__info { + width: 17rem; + height: 22rem; + } + + .quick-add-modal__content-info--bulk-details, + .quick-add-modal__content-info--bulk quick-order-list { + padding: 0 2.5rem; + } +} + +.quick-add__product-media { + margin-bottom: 1rem; +} + +.quick-add-modal__content-info--bulk .quick-add__product-container img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.quick-add-modal__content-info--bulk .quick-add__info { + display: flex; +} + +.quick-add-modal__content-info--bulk .quick-add__content-info__media { + width: auto; +} + +@media screen and (max-width: 990px) { + .quick-add-modal__content-info--bulk .quick-add__content-info__media { + display: flex; + margin: 0; + } + + .quick-add-modal__content-info--bulk quick-order-list { + padding-left: 0; + padding-right: 0; + } + + .quick-add-modal__content-info.quick-add-modal__content-info--bulk { + --modal-padding: 1.5rem; + } +} + +@media screen and (min-width: 989px) { + .quick-add-modal__content-info--bulk .quick-add__info { + flex-direction: column; + position: sticky; + top: 0; + margin-top: -2.5rem; + padding-top: 2.5rem; + } +} + +@media screen and (max-width: 990px) { + .quick-add-modal__content-info--bulk { + flex-direction: column; + } +} + .quick-add-modal__content-info { --modal-padding: 2.5rem; padding-right: 4.4rem; @@ -224,3 +336,36 @@ quick-add-modal .product-media-container.constrain-height { --constrained-min-height: 400px; } } + +quick-add-bulk { + position: relative; + grid-row-start: 4; + margin: 0 0 1rem; + z-index: 1; +} + +.card__content quick-add-bulk .quantity { + width: 100%; +} + +quick-add-bulk .progress-bar-container { + position: absolute; + height: 100%; + display: flex; + overflow: hidden; + border-radius: var(--inputs-radius-outset); + border: var(--inputs-border-width) solid transparent; +} + +quick-add-bulk quantity-input { + justify-content: space-between; +} + +quick-add-bulk .quantity__input { + max-width: calc(6.5rem / var(--font-body-scale)); + flex-grow: 0; +} + +.quantity__input-disabled { + pointer-events: none; +} diff --git a/assets/quick-order-list.css b/assets/quick-order-list.css index a733c922ac4..e2a3c010ab9 100644 --- a/assets/quick-order-list.css +++ b/assets/quick-order-list.css @@ -11,6 +11,10 @@ quick-order-list .quantity__button { width: calc(3.5rem / var(--font-body-scale)); } +.quick-order-list__contents { + position: relative; +} + .quick-order-list__container { padding-bottom: 4rem; } @@ -21,7 +25,7 @@ quick-order-list .quantity__button { .quick-order-list__total { padding-top: 2rem; - border-top: 0.1rem solid rgba(var(--color-foreground), .08); + border-top: 0.1rem solid rgba(var(--color-foreground), 0.08); } .variant-item__quantity .quantity:before { @@ -36,7 +40,7 @@ quick-order-list .quantity__button { .quick-order-list__total { position: sticky; bottom: 0; - z-index: 1; + z-index: 2; background-color: rgb(var(--color-background)); } @@ -135,7 +139,7 @@ quick-order-list .quantity__button { line-height: calc(1 + 0.4 / var(--font-body-scale)); } -.variant-item__details>* { +.variant-item__details > * { margin: 0; max-width: 30rem; } @@ -149,7 +153,7 @@ quick-order-list .quantity__button { display: block; font-size: 1.6rem; letter-spacing: 0.06rem; - line-height: calc(1 + .5 / var(--font-body-scale)); + line-height: calc(1 + 0.5 / var(--font-body-scale)); } .variant-item__sku { @@ -317,8 +321,8 @@ quick-order-list-remove-button:hover .icon-remove { align-self: center; } -.variant-item .loading__spinner:not(.hidden)~*, -.variant-remove-total .loading__spinner:not(.hidden)~* { +.variant-item .loading__spinner:not(.hidden) ~ *, +.variant-remove-total .loading__spinner:not(.hidden) ~ * { visibility: hidden; } @@ -336,14 +340,14 @@ quick-order-list-remove-button:hover .icon-remove { order: 1; } -.variant-item__error-text+svg { +.variant-item__error-text + svg { flex-shrink: 0; width: 1.2rem; margin-right: 0.5rem; margin-top: 0.1rem; } -.variant-item__error-text:empty+svg { +.variant-item__error-text:empty + svg { display: none; } @@ -400,7 +404,7 @@ quick-order-list-remove-button:hover .icon-remove { grid-column: 5 / 3; } - .variant-item--no-media .variant-item__inner~.variant-item__quantity { + .variant-item--no-media .variant-item__inner ~ .variant-item__quantity { grid-column: 1 / 5; } @@ -458,11 +462,11 @@ quick-order-list-remove-button:hover .icon-remove { border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.08); } - .quick-order-list__table th+th { + .quick-order-list__table th + th { padding-left: 5.4rem; } - .quick-order-list__table .quick-order-list__table-heading--wide+.quick-order-list__table-heading--wide { + .quick-order-list__table .quick-order-list__table-heading--wide + .quick-order-list__table-heading--wide { padding-left: 10rem; text-align: right; } diff --git a/assets/quick-order-list.js b/assets/quick-order-list.js index 68171d17df8..bc4d6d0843f 100644 --- a/assets/quick-order-list.js +++ b/assets/quick-order-list.js @@ -1,392 +1,639 @@ -class QuickOrderListRemoveButton extends HTMLElement { - constructor() { - super(); - this.addEventListener('click', (event) => { - event.preventDefault(); - const quickOrderList = this.closest('quick-order-list'); - quickOrderList.updateQuantity(this.dataset.index, 0); - }); - } +if (!customElements.get('quick-order-list-remove-button')) { + customElements.define( + 'quick-order-list-remove-button', + class QuickOrderListRemoveButton extends HTMLElement { + constructor() { + super(); + this.addEventListener('click', (event) => { + event.preventDefault(); + const quickOrderList = this.closest('quick-order-list'); + quickOrderList.updateQuantity(this.dataset.index, 0); + }); + } + } + ); } -customElements.define('quick-order-list-remove-button', QuickOrderListRemoveButton); +if (!customElements.get('quick-order-list-remove-all-button')) { + customElements.define( + 'quick-order-list-remove-all-button', + class QuickOrderListRemoveAllButton extends HTMLElement { + constructor() { + super(); + this.quickOrderList = this.closest('quick-order-list'); + const allVariants = this.quickOrderList.querySelectorAll('[data-quantity-variant-id]'); + const items = {}; + let hasVariantsInCart = false; + + allVariants.forEach((variant) => { + const cartQty = parseInt(variant.dataset.cartQuantity); + if (cartQty > 0) { + hasVariantsInCart = true; + items[parseInt(variant.dataset.quantityVariantId)] = 0; + } + }); -class QuickOrderListRemoveAllButton extends HTMLElement { - constructor() { - super(); - const allVariants = Array.from(document.querySelectorAll('[data-variant-id]')); - const items = {} - let hasVariantsInCart = false; - this.quickOrderList = this.closest('quick-order-list'); + if (!hasVariantsInCart) { + this.classList.add('hidden'); + } - allVariants.forEach((variant) => { - const cartQty = parseInt(variant.dataset.cartQty); - if (cartQty > 0) { - hasVariantsInCart = true; - items[parseInt(variant.dataset.variantId)] = 0; + this.actions = { + confirm: 'confirm', + remove: 'remove', + cancel: 'cancel', + }; + + this.addEventListener('click', (event) => { + event.preventDefault(); + if (this.dataset.action === this.actions.confirm) { + this.toggleConfirmation(false, true); + } else if (this.dataset.action === this.actions.remove) { + this.quickOrderList.updateMultipleQty(items); + this.toggleConfirmation(true, false); + } else if (this.dataset.action === this.actions.cancel) { + this.toggleConfirmation(true, false); + } + }); } - }); - if (!hasVariantsInCart) { - this.classList.add('hidden'); + toggleConfirmation(showConfirmation, showInfo) { + this.quickOrderList + .querySelector('.quick-order-list-total__confirmation') + .classList.toggle('hidden', showConfirmation); + this.quickOrderList.querySelector('.quick-order-list-total__info').classList.toggle('hidden', showInfo); + } } + ); +} - this.actions = { - confirm: 'confirm', - remove: 'remove', - cancel: 'cancel' - } +if (!customElements.get('quick-order-list')) { + customElements.define( + 'quick-order-list', + class QuickOrderList extends HTMLElement { + constructor() { + super(); + this.cart = document.querySelector('cart-drawer'); + this.actions = { + add: 'ADD', + update: 'UPDATE', + }; + + this.quickOrderListId = `quick-order-list-${this.dataset.productId}`; + this.defineInputsAndQuickOrderTable(); + + this.variantItemStatusElement = document.getElementById('shopping-cart-variant-item-status'); + const form = this.querySelector('form'); + this.inputFieldHeight = this.querySelector('.variant-item__quantity-wrapper').offsetHeight; + this.isListInsideModal = document.querySelector('.quick-add-bulk'); + this.stickyHeaderElement = document.querySelector('sticky-header'); + this.getTableHead(); + this.getTotalBar(); + + if (this.stickyHeaderElement) { + this.stickyHeader = { + height: this.stickyHeaderElement.offsetHeight, + type: `${this.stickyHeaderElement.getAttribute('data-sticky-type')}`, + }; + } - this.addEventListener('click', (event) => { - event.preventDefault(); - if (this.dataset.action === this.actions.confirm) { - this.toggleConfirmation(false, true); - } else if (this.dataset.action === this.actions.remove) { - this.quickOrderList.updateMultipleQty(items); - this.toggleConfirmation(true, false); - } else if (this.dataset.action === this.actions.cancel) { - this.toggleConfirmation(true, false); - } - }); - } - - toggleConfirmation(showConfirmation, showInfo) { - this.quickOrderList.querySelector('.quick-order-list-total__confirmation').classList.toggle('hidden', showConfirmation); - this.quickOrderList.querySelector('.quick-order-list-total__info').classList.toggle('hidden', showInfo) - } -} + if (this.getTotalBar()) { + this.totalBarPosition = window.innerHeight - this.getTotalBar().offsetHeight; -customElements.define('quick-order-list-remove-all-button', QuickOrderListRemoveAllButton); + window.addEventListener('resize', () => { + this.totalBarPosition = window.innerHeight - this.getTotalBar().offsetHeight; + this.stickyHeader.height = this.stickyHeaderElement ? this.stickyHeaderElement.offsetHeight : 0; + }); + } + const pageParams = new URLSearchParams(window.location.search); + window.pageNumber = decodeURIComponent(pageParams.get('page') || ''); + form.addEventListener('submit', this.onSubmit.bind(this)); + this.addMultipleDebounce(); + } -class QuickOrderList extends HTMLElement { - constructor() { - super(); - this.cart = document.querySelector('cart-drawer'); - this.actions = { - add: 'ADD', - update: 'UPDATE' - } - this.quickOrderListId = 'quick-order-list' - this.variantItemStatusElement = document.getElementById('shopping-cart-variant-item-status'); - const form = this.querySelector('form'); - - form.addEventListener('submit', this.onSubmit.bind(this)); - - const debouncedOnChange = debounce((event) => { - this.onChange(event); - }, ON_CHANGE_DEBOUNCE_TIMER); - this.addEventListener('change', debouncedOnChange.bind(this)); - } - - cartUpdateUnsubscriber = undefined; - - onSubmit(event) { - event.preventDefault(); - } - - connectedCallback() { - this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => { - if (event.source === this.quickOrderListId) { - return; - } - // If its another section that made the update - this.onCartUpdate(); - }); - this.sectionId = this.dataset.id; - } - - disconnectedCallback() { - if (this.cartUpdateUnsubscriber) { - this.cartUpdateUnsubscriber(); - } - } + cartUpdateUnsubscriber = undefined; - onChange(event) { - const inputValue = parseInt(event.target.value); - const cartQuantity = parseInt(event.target.dataset.cartQuantity); - const index = event.target.dataset.index; - const name = document.activeElement.getAttribute('name'); + onSubmit(event) { + event.preventDefault(); + } - const quantity = inputValue - cartQuantity; + connectedCallback() { + this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => { + if (event.source === this.quickOrderListId) { + return; + } + // If its another section that made the update + this.refresh().then(() => { + this.defineInputsAndQuickOrderTable(); + this.addMultipleDebounce(); + }); + }); + this.sectionId = this.dataset.id; + } - if (cartQuantity > 0) { - this.updateQuantity(index, inputValue, name, this.actions.update); - } else { - this.updateQuantity(index, quantity, name, this.actions.add); - } - } - - onCartUpdate() { - fetch(`${window.location.pathname}?section_id=${this.sectionId}`) - .then((response) => response.text()) - .then((responseText) => { - const html = new DOMParser().parseFromString(responseText, 'text/html'); - const sourceQty = html.querySelector(this.quickOrderListId); - this.innerHTML = sourceQty.innerHTML; - }) - .catch(e => { - console.error(e); - }); - } - - getSectionsToRender() { - return [ - { - id: this.quickOrderListId, - section: document.getElementById(this.quickOrderListId).dataset.id, - selector: '.js-contents' - }, - { - id: 'cart-icon-bubble', - section: 'cart-icon-bubble', - selector: '.shopify-section' - }, - { - id: 'quick-order-list-live-region-text', - section: 'cart-live-region-text', - selector: '.shopify-section' - }, - { - id: 'quick-order-list-total', - section: document.getElementById(this.quickOrderListId).dataset.id, - selector: '.quick-order-list__total' - }, - { - id: 'CartDrawer', - selector: '#CartDrawer', - section: 'cart-drawer' - } - ]; - } - - renderSections(parsedState) { - this.getSectionsToRender().forEach((section => { - const sectionElement = document.getElementById(section.id); - if (sectionElement && sectionElement.parentElement && sectionElement.parentElement.classList.contains('drawer')) { - parsedState.items.length > 0 ? sectionElement.parentElement.classList.remove('is-empty') : sectionElement.parentElement.classList.add('is-empty'); + disconnectedCallback() { + this.cartUpdateUnsubscriber?.(); + } - setTimeout(() => { - document.querySelector('#CartDrawer-Overlay').addEventListener('click', this.cart.close.bind(this.cart)); + defineInputsAndQuickOrderTable() { + this.allInputsArray = Array.from(this.querySelectorAll('input[type="number"]')); + this.quickOrderListTable = this.querySelector('.quick-order-list__table'); + this.quickOrderListTable.addEventListener('focusin', this.switchVariants.bind(this)); + } + + onChange(event) { + const inputValue = parseInt(event.target.value); + const cartQuantity = parseInt(event.target.dataset.cartQuantity); + const index = event.target.dataset.index; + const name = document.activeElement.getAttribute('name'); + + const quantity = inputValue - cartQuantity; + this.cleanErrorMessageOnType(event); + if (inputValue == 0) { + this.updateQuantity(index, inputValue, name, this.actions.update); + } else { + this.validateQuantity(event, name, index, inputValue, cartQuantity, quantity); + } + } + + cleanErrorMessageOnType(event) { + event.target.addEventListener('keydown', () => { + event.target.setCustomValidity(' '); + event.target.reportValidity(); }); } - const elementToReplace = sectionElement && sectionElement.querySelector(section.selector) ? sectionElement.querySelector(section.selector) : sectionElement; - if (elementToReplace) { - elementToReplace.innerHTML = - this.getSectionInnerHTML(parsedState.sections[section.section], section.selector); - } - })); - - } - - updateMultipleQty(items) { - this.querySelector('.variant-remove-total .loading__spinner').classList.remove('hidden'); - - const body = JSON.stringify({ - updates: items, - sections: this.getSectionsToRender().map((section) => section.section), - sections_url: window.location.pathname - }); - - this.updateMessage(); - this.setErrorMessage(); - - fetch(`${routes.cart_update_url}`, { ...fetchConfig(), ...{ body } }) - .then((response) => { - return response.text(); - }) - .then((state) => { - const parsedState = JSON.parse(state); - this.renderSections(parsedState); - }).catch(() => { - this.setErrorMessage(window.cartStrings.error); - }) - .finally(() => { - this.querySelector('.variant-remove-total .loading__spinner').classList.add('hidden'); - }); - } - - updateQuantity(id, quantity, name, action) { - this.toggleLoading(id, true); - - let routeUrl = routes.cart_change_url; - let body = JSON.stringify({ - quantity, - id, - sections: this.getSectionsToRender().map((section) => section.section), - sections_url: window.location.pathname - }); - let fetchConfigType; - if (action === this.actions.add) { - fetchConfigType = 'javascript'; - routeUrl = routes.cart_add_url; - body = JSON.stringify({ - items: [ + + validateQuantity(event, name, index, inputValue, cartQuantity, quantity) { + if (inputValue < event.target.dataset.min) { + this.setValidity( + event, + index, + window.quickOrderListStrings.min_error.replace('[min]', event.target.dataset.min) + ); + } else if (inputValue > parseInt(event.target.max)) { + this.setValidity(event, index, window.quickOrderListStrings.max_error.replace('[max]', event.target.max)); + } else if (inputValue % parseInt(event.target.step) != 0) { + this.setValidity(event, index, window.quickOrderListStrings.step_error.replace('[step]', event.target.step)); + } else { + event.target.setCustomValidity(''); + event.target.reportValidity(); + if (cartQuantity > 0) { + this.updateQuantity(index, inputValue, name, this.actions.update); + } else { + this.updateQuantity(index, quantity, name, this.actions.add); + } + } + } + + setValidity(event, index, message) { + event.target.setCustomValidity(message); + event.target.reportValidity(); + this.resetQuantityInput(index); + event.target.select(); + } + + validateInput(target) { + if (target.max) { + return ( + parseInt(target.value) == 0 || + (parseInt(target.value) >= parseInt(target.dataset.min) && + parseInt(target.value) <= parseInt(target.max) && + parseInt(target.value) % parseInt(target.step) == 0) + ); + } else { + return ( + parseInt(target.value) == 0 || + (parseInt(target.value) >= parseInt(target.dataset.min) && + parseInt(target.value) % parseInt(target.step) == 0) + ); + } + } + + refresh() { + return new Promise((resolve, reject) => { + fetch(`${this.getSectionsUrl()}?section_id=${this.sectionId}`) + .then((response) => response.text()) + .then((responseText) => { + const html = new DOMParser().parseFromString(responseText, 'text/html'); + const sourceQty = html.querySelector(`#${this.quickOrderListId}`); + if (sourceQty) { + this.innerHTML = sourceQty.innerHTML; + } + resolve(); + }) + .catch((e) => { + console.error(e); + reject(e); + }); + }); + } + + getSectionsToRender() { + return [ + { + id: this.quickOrderListId, + section: document.getElementById(this.quickOrderListId).dataset.id, + selector: `#${this.quickOrderListId} .js-contents`, + }, + { + id: 'cart-icon-bubble', + section: 'cart-icon-bubble', + selector: '.shopify-section', + }, + { + id: `quick-order-list-live-region-text-${this.dataset.productId}`, + section: 'cart-live-region-text', + selector: '.shopify-section', + }, { - quantity: parseInt(quantity), - id: parseInt(id) + id: `quick-order-list-total-${this.dataset.productId}`, + section: document.getElementById(this.quickOrderListId).dataset.id, + selector: `#${this.quickOrderListId} .quick-order-list__total`, + }, + { + id: 'CartDrawer', + selector: '#CartDrawer', + section: 'cart-drawer', + }, + ]; + } + + addMultipleDebounce() { + this.querySelectorAll('quantity-input').forEach((qty) => { + const debouncedOnChange = debounce((event) => { + this.onChange(event); + }, ON_CHANGE_DEBOUNCE_TIMER); + qty.addEventListener('change', debouncedOnChange.bind(this)); + }); + } + + addDebounce(id) { + const element = this.querySelector(`#Variant-${id} quantity-input`); + const debouncedOnChange = debounce((event) => { + this.onChange(event); + }, ON_CHANGE_DEBOUNCE_TIMER); + element.addEventListener('change', debouncedOnChange.bind(this)); + } + + renderSections(parsedState, id) { + this.getSectionsToRender().forEach((section) => { + const sectionElement = document.getElementById(section.id); + if ( + sectionElement && + sectionElement.parentElement && + sectionElement.parentElement.classList.contains('drawer') + ) { + parsedState.items.length > 0 + ? sectionElement.parentElement.classList.remove('is-empty') + : sectionElement.parentElement.classList.add('is-empty'); + setTimeout(() => { + document.querySelector('#CartDrawer-Overlay').addEventListener('click', this.cart.close.bind(this.cart)); + }); } - ], - sections: this.getSectionsToRender().map((section) => section.section), - sections_url: window.location.pathname - }); - } + const elementToReplace = + sectionElement && sectionElement.querySelector(section.selector) + ? sectionElement.querySelector(section.selector) + : sectionElement; + if (elementToReplace) { + if (section.selector === `#${this.quickOrderListId} .js-contents` && id !== undefined) { + elementToReplace.querySelector(`#Variant-${id}`).innerHTML = this.getSectionInnerHTML( + parsedState.sections[section.section], + `#Variant-${id}` + ); + } else { + elementToReplace.innerHTML = this.getSectionInnerHTML( + parsedState.sections[section.section], + section.selector + ); + } + } + }); + this.defineInputsAndQuickOrderTable(); + if (id) { + this.addDebounce(id); + } else { + this.addMultipleDebounce(); + } + } - this.updateMessage(); - this.setErrorMessage(); - - fetch(`${routeUrl}`, { ...fetchConfig(fetchConfigType), ...{ body } }) - .then((response) => { - return response.text(); - }) - .then((state) => { - const parsedState = JSON.parse(state); - const quantityElement = document.getElementById(`Quantity-${id}`); - const items = document.querySelectorAll('.variant-item'); - - if (parsedState.description || parsedState.errors) { - const variantItem = document.querySelector(`[id^="Variant-${id}"] .variant-item__totals.small-hide .loading__spinner`); - variantItem.classList.add('loading__spinner--error'); - this.resetQuantityInput(id, quantityElement); - if (parsedState.errors) { - this.updateLiveRegions(id, parsedState.errors); - } else { - this.updateLiveRegions(id, parsedState.description); + getTableHead() { + return document.querySelector('.quick-order-list__table thead'); + } + + getTotalBar() { + return this.querySelector('.quick-order-list__total'); + } + + scrollQuickOrderListTable() { + const inputTopBorder = this.variantListInput.getBoundingClientRect().top; + const inputBottomBorder = this.variantListInput.getBoundingClientRect().bottom; + + if (this.isListInsideModal) { + const totalBarCrossesInput = inputBottomBorder > this.getTotalBar().getBoundingClientRect().top; + const tableHeadCrossesInput = inputTopBorder < this.getTableHead().getBoundingClientRect().bottom; + + if (totalBarCrossesInput || tableHeadCrossesInput) { + this.scrollToCenter(); + } + } else { + const stickyHeaderBottomBorder = + this.stickyHeaderElement && this.stickyHeaderElement.getBoundingClientRect().bottom; + const totalBarCrossesInput = inputBottomBorder > this.totalBarPosition; + const inputOutsideOfViewPort = inputBottomBorder < this.inputFieldHeight; + const stickyHeaderCrossesInput = + this.stickyHeaderElement && + this.stickyHeader.type !== 'on-scroll-up' && + this.stickyHeader.height > inputTopBorder; + const stickyHeaderScrollupCrossesInput = + this.stickyHeaderElement && + this.stickyHeader.type === 'on-scroll-up' && + this.stickyHeader.height > inputTopBorder && + stickyHeaderBottomBorder > 0; + + if ( + totalBarCrossesInput || + inputOutsideOfViewPort || + stickyHeaderCrossesInput || + stickyHeaderScrollupCrossesInput + ) { + this.scrollToCenter(); } + } + } + + scrollToCenter() { + this.variantListInput.scrollIntoView({ block: 'center', behavior: 'smooth' }); + } + + switchVariants(event) { + if (event.target.tagName !== 'INPUT') { return; } - this.classList.toggle('is-empty', parsedState.item_count === 0); + this.variantListInput = event.target; + this.variantListInput.select(); + if (this.allInputsArray.length !== 1) { + this.variantListInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + e.target.blur(); + if (this.validateInput(e.target)) { + const currentIndex = this.allInputsArray.indexOf(e.target); + this.lastKey = e.shiftKey; + if (!e.shiftKey) { + const nextIndex = currentIndex + 1; + const nextVariant = this.allInputsArray[nextIndex] || this.allInputsArray[0]; + nextVariant.select(); + } else { + const previousIndex = currentIndex - 1; + const previousVariant = + this.allInputsArray[previousIndex] || this.allInputsArray[this.allInputsArray.length - 1]; + this.lastElement = previousVariant.dataset.index; + previousVariant.select(); + } + } + } + }); + + this.scrollQuickOrderListTable(); + } else { + this.variantListInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + e.target.blur(); + } + }); + } + } + + updateMultipleQty(items) { + this.querySelector('.variant-remove-total .loading__spinner').classList.remove('hidden'); - this.renderSections(parsedState); + const body = JSON.stringify({ + updates: items, + sections: this.getSectionsToRender().map((section) => section.section), + sections_url: this.getSectionsUrl(), + }); - let hasError = false; + this.updateMessage(); + this.setErrorMessage(); + + fetch(`${routes.cart_update_url}`, { ...fetchConfig(), ...{ body } }) + .then((response) => { + return response.text(); + }) + .then((state) => { + const parsedState = JSON.parse(state); + this.renderSections(parsedState); + }) + .catch(() => { + this.setErrorMessage(window.cartStrings.error); + }) + .finally(() => { + this.querySelector('.variant-remove-total .loading__spinner').classList.add('hidden'); + }); + } - const currentItem = parsedState.items.find((item) => item.variant_id === parseInt(id)); - const updatedValue = currentItem ? currentItem.quantity : undefined; - if (updatedValue && updatedValue !== quantity) { - this.updateError(updatedValue, id); - hasError = true; + getSectionsUrl() { + if (window.pageNumber) { + return `${window.location.pathname}?page=${window.pageNumber}`; + } else { + return `${window.location.pathname}`; } + } + + updateQuantity(id, quantity, name, action) { + this.toggleLoading(id, true); + this.cleanErrors(); - const variantItem = document.getElementById(`Variant-${id}`); - if (variantItem && variantItem.querySelector(`[name="${name}"]`)) { - variantItem.querySelector(`[name="${name}"]`).focus(); + let routeUrl = routes.cart_change_url; + let body = JSON.stringify({ + quantity, + id, + sections: this.getSectionsToRender().map((section) => section.section), + sections_url: this.getSectionsUrl(), + }); + let fetchConfigType; + if (action === this.actions.add) { + fetchConfigType = 'javascript'; + routeUrl = routes.cart_add_url; + body = JSON.stringify({ + items: [ + { + quantity: parseInt(quantity), + id: parseInt(id), + }, + ], + sections: this.getSectionsToRender().map((section) => section.section), + sections_url: this.getSectionsUrl(), + }); } - publish(PUB_SUB_EVENTS.cartUpdate, { source: this.quickOrderListId, cartData: parsedState }); - - if (hasError) { - this.updateMessage(); - } else if (action === this.actions.add) { - this.updateMessage(parseInt(quantity)) - } else if (action === this.actions.update) { - this.updateMessage(parseInt(quantity - quantityElement.dataset.cartQuantity)) + + this.updateMessage(); + this.setErrorMessage(); + + fetch(`${routeUrl}`, { ...fetchConfig(fetchConfigType), ...{ body } }) + .then((response) => { + return response.text(); + }) + .then((state) => { + const parsedState = JSON.parse(state); + const quantityElement = document.getElementById(`Quantity-${id}`); + const items = document.querySelectorAll('.variant-item'); + + if (parsedState.description || parsedState.errors) { + const variantItem = document.querySelector( + `[id^="Variant-${id}"] .variant-item__totals.small-hide .loading__spinner` + ); + variantItem.classList.add('loading__spinner--error'); + this.resetQuantityInput(id, quantityElement); + if (parsedState.errors) { + this.updateLiveRegions(id, parsedState.errors); + } else { + this.updateLiveRegions(id, parsedState.description); + } + return; + } + + this.classList.toggle('is-empty', parsedState.item_count === 0); + + this.renderSections(parsedState, id); + + let hasError = false; + + const currentItem = parsedState.items.find((item) => item.variant_id === parseInt(id)); + const updatedValue = currentItem ? currentItem.quantity : undefined; + if (updatedValue && updatedValue !== quantity) { + this.updateError(updatedValue, id); + hasError = true; + } + + publish(PUB_SUB_EVENTS.cartUpdate, { source: this.quickOrderListId, cartData: parsedState }); + + if (hasError) { + this.updateMessage(); + } else if (action === this.actions.add) { + this.updateMessage(parseInt(quantity)); + } else if (action === this.actions.update) { + this.updateMessage(parseInt(quantity - quantityElement.dataset.cartQuantity)); + } else { + this.updateMessage(-parseInt(quantityElement.dataset.cartQuantity)); + } + }) + .catch((error) => { + this.querySelectorAll('.loading__spinner').forEach((overlay) => overlay.classList.add('hidden')); + this.resetQuantityInput(id); + console.error(error); + this.setErrorMessage(window.cartStrings.error); + }) + .finally(() => { + this.toggleLoading(id); + if (this.lastKey && this.lastElement === id) { + this.querySelector(`#Variant-${id} input`).select(); + } + }); + } + + resetQuantityInput(id, quantityElement) { + const input = quantityElement ?? document.getElementById(`Quantity-${id}`); + input.value = input.getAttribute('value'); + } + + setErrorMessage(message = null) { + this.errorMessageTemplate = + this.errorMessageTemplate ?? + document.getElementById(`QuickOrderListErrorTemplate-${this.dataset.productId}`).cloneNode(true); + const errorElements = document.querySelectorAll('.quick-order-list-error'); + + errorElements.forEach((errorElement) => { + errorElement.innerHTML = ''; + if (!message) return; + const updatedMessageElement = this.errorMessageTemplate.cloneNode(true); + updatedMessageElement.content.querySelector('.quick-order-list-error-message').innerText = message; + errorElement.appendChild(updatedMessageElement.content); + }); + } + + updateMessage(quantity = null) { + const messages = this.querySelectorAll('.quick-order-list__message-text'); + const icons = this.querySelectorAll('.quick-order-list__message-icon'); + + if (quantity === null || isNaN(quantity)) { + messages.forEach((message) => (message.innerHTML = '')); + icons.forEach((icon) => icon.classList.add('hidden')); + return; + } + + const isQuantityNegative = quantity < 0; + const absQuantity = Math.abs(quantity); + + const textTemplate = isQuantityNegative + ? absQuantity === 1 + ? window.quickOrderListStrings.itemRemoved + : window.quickOrderListStrings.itemsRemoved + : quantity === 1 + ? window.quickOrderListStrings.itemAdded + : window.quickOrderListStrings.itemsAdded; + + messages.forEach((msg) => (msg.innerHTML = textTemplate.replace('[quantity]', absQuantity))); + + if (!isQuantityNegative) { + icons.forEach((i) => i.classList.remove('hidden')); + } + } + + updateError(updatedValue, id) { + let message = ''; + if (typeof updatedValue === 'undefined') { + message = window.cartStrings.error; } else { - this.updateMessage(-parseInt(quantityElement.dataset.cartQuantity)) + message = window.cartStrings.quantityError.replace('[quantity]', updatedValue); } - }).catch((error) => { - this.querySelectorAll('.loading__spinner').forEach((overlay) => overlay.classList.add('hidden')); - this.resetQuantityInput(id); - console.error(error); - this.setErrorMessage(window.cartStrings.error); - }) - .finally(() => { - this.toggleLoading(id); - }); - } - - resetQuantityInput(id, quantityElement) { - const input = quantityElement ?? document.getElementById(`Quantity-${id}`); - input.value = input.getAttribute('value'); - } - - setErrorMessage(message = null) { - this.errorMessageTemplate = this.errorMessageTemplate ?? document.getElementById(`QuickOrderListErrorTemplate-${this.sectionId}`).cloneNode(true); - const errorElements = document.querySelectorAll('.quick-order-list-error'); - - errorElements.forEach((errorElement) => { - errorElement.innerHTML = ''; - if (!message) return; - const updatedMessageElement = this.errorMessageTemplate.cloneNode(true); - updatedMessageElement.content.querySelector('.quick-order-list-error-message').innerText = message; - errorElement.appendChild(updatedMessageElement.content); - }); - } - - updateMessage(quantity = null) { - const messages = this.querySelectorAll('.quick-order-list__message-text'); - const icons = this.querySelectorAll('.quick-order-list__message-icon'); - - if (quantity === null || isNaN(quantity)) { - messages.forEach(message => message.innerHTML = ''); - icons.forEach(icon => icon.classList.add('hidden')); - return; - } + this.updateLiveRegions(id, message); + } - const isQuantityNegative = quantity < 0; - const absQuantity = Math.abs(quantity); + cleanErrors() { + this.querySelectorAll('.desktop-row-error').forEach((error) => error.classList.add('hidden')); + this.querySelectorAll(`.variant-item__error-text`).forEach((error) => (error.innerHTML = '')); + } - const textTemplate = isQuantityNegative - ? (absQuantity === 1 ? window.quickOrderListStrings.itemRemoved : window.quickOrderListStrings.itemsRemoved) - : (quantity === 1 ? window.quickOrderListStrings.itemAdded : window.quickOrderListStrings.itemsAdded); + updateLiveRegions(id, message) { + const variantItemErrorDesktop = document.getElementById(`Quick-order-list-item-error-desktop-${id}`); + const variantItemErrorMobile = document.getElementById(`Quick-order-list-item-error-mobile-${id}`); + if (variantItemErrorDesktop) { + variantItemErrorDesktop.querySelector('.variant-item__error-text').innerHTML = message; + variantItemErrorDesktop.closest('tr').classList.remove('hidden'); + } + if (variantItemErrorMobile) + variantItemErrorMobile.querySelector('.variant-item__error-text').innerHTML = message; - messages.forEach((msg) => msg.innerHTML = textTemplate.replace('[quantity]', absQuantity)); + this.variantItemStatusElement.setAttribute('aria-hidden', true); - if (!isQuantityNegative) { - icons.forEach((i) => i.classList.remove('hidden')); - } + const cartStatus = document.getElementById('quick-order-list-live-region-text'); + cartStatus.setAttribute('aria-hidden', false); - } + setTimeout(() => { + cartStatus.setAttribute('aria-hidden', true); + }, 1000); + } - updateError(updatedValue, id) { - let message = ''; - if (typeof updatedValue === 'undefined') { - message = window.cartStrings.error; - } else { - message = window.cartStrings.quantityError.replace('[quantity]', updatedValue); - } - this.updateLiveRegions(id, message); - } - - updateLiveRegions(id, message) { - const variantItemErrorDesktop = document.getElementById(`Quick-order-list-item-error-desktop-${id}`); - const variantItemErrorMobile = document.getElementById(`Quick-order-list-item-error-mobile-${id}`); - if (variantItemErrorDesktop) { - variantItemErrorDesktop.querySelector('.variant-item__error-text').innerHTML = message; - variantItemErrorDesktop.closest('tr').classList.remove('hidden'); - } - if (variantItemErrorMobile) variantItemErrorMobile.querySelector('.variant-item__error-text').innerHTML = message; - - this.variantItemStatusElement.setAttribute('aria-hidden', true); - - const cartStatus = document.getElementById('quick-order-list-live-region-text'); - cartStatus.setAttribute('aria-hidden', false); - - setTimeout(() => { - cartStatus.setAttribute('aria-hidden', true); - }, 1000); - } - - getSectionInnerHTML(html, selector) { - return new DOMParser() - .parseFromString(html, 'text/html') - .querySelector(selector).innerHTML; - } - - toggleLoading(id, enable) { - const quickOrderList = document.getElementById(this.quickOrderListId); - const quickOrderListItems = this.querySelectorAll(`#Variant-${id} .loading__spinner`); - - if (enable) { - quickOrderList.classList.add('quick-order-list__container--disabled'); - [...quickOrderListItems].forEach((overlay) => overlay.classList.remove('hidden')); - document.activeElement.blur(); - this.variantItemStatusElement.setAttribute('aria-hidden', false); - } else { - quickOrderList.classList.remove('quick-order-list__container--disabled'); - quickOrderListItems.forEach((overlay) => overlay.classList.add('hidden')); + getSectionInnerHTML(html, selector) { + return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML; + } + + toggleLoading(id, enable) { + const quickOrderListItems = this.querySelectorAll(`#Variant-${id} .loading__spinner`); + const quickOrderListItem = this.querySelector(`#Variant-${id}`); + + if (enable) { + quickOrderListItem.classList.add('quick-order-list__container--disabled'); + [...quickOrderListItems].forEach((overlay) => overlay.classList.remove('hidden')); + this.variantItemStatusElement.setAttribute('aria-hidden', false); + } else { + quickOrderListItem.classList.remove('quick-order-list__container--disabled'); + quickOrderListItems.forEach((overlay) => overlay.classList.add('hidden')); + } + } } - } + ); } - -customElements.define('quick-order-list', QuickOrderList); diff --git a/assets/section-featured-product.css b/assets/section-featured-product.css index 5290e0612c0..b030d352d31 100644 --- a/assets/section-featured-product.css +++ b/assets/section-featured-product.css @@ -11,11 +11,6 @@ .featured-product .product__media-item { padding-left: 0; - width: 100%; -} - -.featured-product .product__media-item:not(:first-child) { - display: none; } .featured-product .placeholder-svg { @@ -53,6 +48,10 @@ .background-secondary .featured-product { padding: 5rem; } + + .product--right .product__media-wrapper { + order: 2; + } } @media screen and (min-width: 990px) { diff --git a/assets/section-footer.css b/assets/section-footer.css index 9691474db6c..e78fa0984db 100644 --- a/assets/section-footer.css +++ b/assets/section-footer.css @@ -280,12 +280,6 @@ } } -@media screen and (max-width: 989px) { - .footer__localization noscript { - width: 100%; - } -} - @media screen and (min-width: 750px) { .footer__payment { margin-top: 1.5rem; diff --git a/assets/section-image-banner.css b/assets/section-image-banner.css index 355c9e277ca..e446abeeb96 100644 --- a/assets/section-image-banner.css +++ b/assets/section-image-banner.css @@ -339,7 +339,7 @@ } .banner__box { - padding: 4rem 3.5rem; + padding: 4rem 1.5rem; position: relative; height: fit-content; align-items: center; @@ -349,8 +349,17 @@ z-index: 1; } +.banner--mobile-bottom .banner__box { + padding: 4rem 3.5rem; +} + @media screen and (min-width: 750px) { + .banner__box { + padding: 4rem 3.5rem; + } + .banner--desktop-transparent .banner__box { + padding: 4rem 0; background: transparent; max-width: 89rem; border: none; diff --git a/assets/section-main-product.css b/assets/section-main-product.css index 5fb37df78e9..96e6763eaee 100644 --- a/assets/section-main-product.css +++ b/assets/section-main-product.css @@ -50,6 +50,10 @@ .product__media-container .slider-buttons { display: none; } + + .product--right .product__media-wrapper { + order: 2; + } } @media screen and (min-width: 990px) { @@ -88,8 +92,7 @@ min-height: 4.6rem; } -.shopify-payment-button__button [role='button'].focused, -.no-js .shopify-payment-button__button [role='button']:focus { +.shopify-payment-button__button [role='button'].focused { outline: 0.2rem solid rgba(var(--color-foreground), 0.5) !important; outline-offset: 0.3rem; box-shadow: 0 0 0 0.1rem rgba(var(--color-button), var(--alpha-button-border)), @@ -174,12 +177,6 @@ padding-left: 0; } - -/* Fallback */ -.no-js .shopify-payment-button__button [role='button']:focus + label { - box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0 0.5rem rgba(var(--color-foreground), 0.55); -} - .product-form__input .select { max-width: 100%; } @@ -188,12 +185,6 @@ margin-bottom: 1rem; } -.no-js .product-form__submit.button--secondary { - --color-button: inherit; - --color-button-text: inherit; - --alpha-button-background: 1; -} - .product-form__submit[aria-disabled='true'] + .shopify-payment-button .shopify-payment-button__button[disabled], .product-form__submit[disabled] + .shopify-payment-button .shopify-payment-button__button[disabled] { cursor: not-allowed; @@ -308,7 +299,6 @@ a.product__text { .product--no-media .product__title, .product--no-media .product__text, -.product--no-media noscript .product-form__input, .product--no-media .product__tax, .product--no-media .product__sku, .product--no-media shopify-payment-terms { @@ -324,7 +314,6 @@ a.product__text { margin-top: -1.4rem; } -.product--no-media noscript .product-form__input, .product--no-media .share-button { max-width: 100%; } @@ -446,14 +435,6 @@ a.product__text { } } -.product__media-item.product__media-item--variant { - display: none; -} - -.product__media-item--variant:first-child { - display: block; -} - @media screen and (min-width: 750px) and (max-width: 989px) { .product__media-list .product__media-item:first-child { padding-left: 0; @@ -1401,10 +1382,6 @@ a.product__text { cursor: pointer; } -.no-js .recipient-checkbox { - display: none; -} - .recipient-form > input[type='checkbox'] { position: absolute; width: 1.6rem; @@ -1463,8 +1440,7 @@ a.product__text { display: none; } -.js .recipient-email-label.required, -.no-js .recipient-email-label.optional { +.js .recipient-email-label.required { display: inline; } diff --git a/assets/section-multicolumn.css b/assets/section-multicolumn.css index ac843b7e967..bb407542a76 100644 --- a/assets/section-multicolumn.css +++ b/assets/section-multicolumn.css @@ -16,6 +16,13 @@ } } +@media screen and (min-width: 750px) and (max-width: 989px) { + .multicolumn__title { + padding-left: 5rem; + padding-right: 5rem; + } +} + @media screen and (max-width: 989px) { .multicolumn .page-width { padding-left: 0; @@ -120,8 +127,7 @@ } @media screen and (min-width: 750px) { - .multicolumn-list.slider, - .multicolumn-list.grid--4-col-desktop { + .multicolumn-list.slider { padding: 0; } diff --git a/assets/section-password.css b/assets/section-password.css index c6dc73039e3..6ff288750d4 100644 --- a/assets/section-password.css +++ b/assets/section-password.css @@ -230,14 +230,6 @@ details[open] .modal__toggle, background-color: transparent; } -.no-js .modal__close-button { - display: none; -} - -.no-js .modal__toggle { - z-index: 2; -} - .modal__toggle::-webkit-details-marker { display: none; } @@ -272,10 +264,6 @@ details.modal .modal__toggle-open { display: flex; } -.no-js details[open].modal .modal__toggle-open { - display: none; -} - .password-header { padding: 2rem 1.5rem 2.5rem; display: flex; diff --git a/assets/template-giftcard.css b/assets/template-giftcard.css index 7cf71f9a565..958db6cb089 100644 --- a/assets/template-giftcard.css +++ b/assets/template-giftcard.css @@ -268,8 +268,7 @@ h2, } /* Fallback - for browsers that don't support :focus-visible, a fallback is set for :focus */ -.focused, -.no-js *:focus { +.focused { outline: 0.2rem solid rgba(var(--color-foreground), 0.5); outline-offset: 0.3rem; box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0.5rem 0.4rem rgba(var(--color-foreground), 0.3); @@ -280,12 +279,6 @@ h2, 0 0 0.5rem 0.4rem rgba(var(--color-foreground), 0.3); } -/* Negate the fallback side-effect for browsers that support :focus-visible */ -.no-js *:focus:not(:focus-visible) { - outline: 0; - box-shadow: none; -} - .button:focus { box-shadow: 0 0 0 0.1rem rgb(var(--color-button)), 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0.5rem 0.4rem rgba(var(--color-foreground), 0.3); diff --git a/layout/password.liquid b/layout/password.liquid index 250d352abe9..9bae7f57689 100644 --- a/layout/password.liquid +++ b/layout/password.liquid @@ -1,5 +1,5 @@ - +
@@ -175,19 +175,20 @@ {% endstyle %} {%- unless settings.type_body_font.system? -%} - {{ settings.type_body_font | font_url | preload_tag: as: 'font', type: 'font/woff2', crossorigin: 'anonymous' }} + {% comment %}theme-check-disable AssetPreload{% endcomment %} + + {% comment %}theme-check-enable AssetPreload{% endcomment %} {%- endunless -%} {%- unless settings.type_header_font.system? -%} - {{ settings.type_header_font | font_url | preload_tag: as: 'font', type: 'font/woff2', crossorigin: 'anonymous' }} + {% comment %}theme-check-disable AssetPreload{% endcomment %} + + {% comment %}theme-check-enable AssetPreload{% endcomment %} {%- endunless -%} {{ 'section-password.css' | asset_url | stylesheet_tag }} {{ 'base.css' | asset_url | stylesheet_tag }} {{ 'component-list-social.css' | asset_url | stylesheet_tag }} - diff --git a/layout/theme.liquid b/layout/theme.liquid index 93d886c6f06..c07de960c30 100644 --- a/layout/theme.liquid +++ b/layout/theme.liquid @@ -1,5 +1,5 @@ - + @@ -254,10 +254,14 @@ {{ 'base.css' | asset_url | stylesheet_tag }} {%- unless settings.type_body_font.system? -%} + {% comment %}theme-check-disable AssetPreload{% endcomment %} + {% comment %}theme-check-enable AssetPreload{% endcomment %} {%- endunless -%} {%- unless settings.type_header_font.system? -%} + {% comment %}theme-check-disable AssetPreload{% endcomment %} + {% comment %}theme-check-enable AssetPreload{% endcomment %} {%- endunless -%} {%- if localization.available_countries.size > 1 or localization.available_languages.size > 1 -%} @@ -275,7 +279,6 @@ {%- endif -%} +{%- endunless -%} + +{%- if section.settings.quick_add == 'standard' -%} + +{%- endif -%} + +{%- if section.settings.quick_add == 'bulk' -%} + + + + {%- endif -%} {%- style -%} @@ -48,8 +58,14 @@ endif -%} -