├── assets ├── custom.js ├── sparkle.gif ├── section-related-products.css ├── component-show-more.css ├── constants.js ├── password-modal.js ├── section-main-page.css ├── component-list-payment.css ├── component-list-social.css ├── pubsub.js ├── component-totals.css ├── component-list-menu.css ├── component-discounts.css ├── section-contact-form.css ├── show-more.js ├── component-product-model.css ├── newsletter-section.css ├── component-rating.css ├── section-collection-list.css ├── video-section.css ├── component-accordion.css ├── main-search.js ├── product-modal.js ├── component-loading-overlay.css ├── search-form.js ├── product-model.js ├── custom.css ├── component-model-viewer-ui.css ├── component-newsletter.css ├── component-search.css ├── component-pagination.css ├── section-rich-text.css ├── template-collection.css ├── details-disclosure.js ├── section-featured-product.css ├── details-modal.js ├── animations.js ├── theme-editor.js ├── section-featured-blog.css ├── component-price.css ├── component-mega-menu.css ├── localization-form.js ├── slick-theme.css ├── component-modal-video.css ├── share.js ├── magnify.js ├── section-main-blog.css ├── slick.css ├── component-collection-hero.css ├── cart-notification.js ├── component-deferred-media.css ├── section-email-signup-banner.css ├── component-article-card.css ├── customer.js ├── component-cart-notification.css ├── collapsible-content.css ├── section-blog-post.css ├── pickup-availability.js ├── component-pickup-availability.css ├── product-info.js └── component-cart.css ├── sections ├── cart-drawer.liquid ├── cart-notification-button.liquid ├── cart-live-region-text.liquid ├── upsell-slider.liquid ├── cart-icon-bubble.liquid ├── main-404.liquid ├── footer-group.json ├── apps.liquid ├── header-group.json ├── revy-contents.liquid ├── main-page.liquid ├── custom-liquid.liquid ├── cart-notification-product.liquid ├── page.liquid ├── main-collection-banner.liquid └── pickup-availability.liquid ├── README.md ├── templates ├── 404.json ├── page.json ├── customers │ ├── login.json │ ├── order.json │ ├── account.json │ ├── addresses.json │ ├── register.json │ ├── reset_password.json │ └── activate_account.json ├── list-collections.json ├── blog.json ├── page.contact.json ├── search.json ├── cart.json ├── article.json ├── collection.json ├── password.json ├── index.json └── product.json └── snippets ├── icon-caret.liquid ├── icon-tumblr.liquid ├── revy-bundle-script.liquid ├── icon-minus.liquid ├── icon-discount.liquid ├── mask-arch.liquid ├── icon-youtube.liquid ├── icon-tiktok.liquid ├── icon-plus.liquid ├── icon-hamburger.liquid ├── icon-arrow.liquid ├── icon-checkmark.liquid ├── icon-tick.liquid ├── icon-close.liquid ├── icon-clipboard.liquid ├── icon-success.liquid ├── icon-play.liquid ├── icon-close-small.liquid ├── icon-facebook.liquid ├── icon-unavailable.liquid ├── icon-cart-empty.liquid ├── icon-vimeo.liquid ├── icon-account.liquid ├── icon-cart.liquid ├── icon-padlock.liquid ├── icon-filter.liquid ├── icon-pause.liquid ├── icon-remove.liquid ├── icon-twitter.liquid ├── icon-error.liquid ├── icon-pinterest.liquid ├── icon-share.liquid ├── icon-instagram.liquid ├── icon-3d-model.liquid ├── icon-zoom.liquid ├── cart-upsell-item.liquid ├── language-localization.liquid ├── cart-upsell.liquid ├── meta-tags.liquid ├── country-localization.liquid ├── icon-snapchat.liquid ├── share-button.liquid ├── product-media-modal.liquid ├── cart-notification.liquid ├── pagination.liquid ├── product-variant-options.liquid ├── email-signup-banner-background-mobile.liquid ├── social-icons.liquid ├── icon-with-text.liquid ├── header-mega-menu.liquid └── email-signup-banner-background.liquid /assets/custom.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sections/cart-drawer.liquid: -------------------------------------------------------------------------------- 1 | {%- render 'cart-drawer' -%} 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Shopify Cart Upsell 2 | 3 | This product shows how to create Shopify cart upsell. -------------------------------------------------------------------------------- /sections/cart-notification-button.liquid: -------------------------------------------------------------------------------- 1 | {{ 'general.cart.view' | t: count: cart.item_count }} 2 | -------------------------------------------------------------------------------- /assets/sparkle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liquidshopier/Shopify-Cart-upsell/HEAD/assets/sparkle.gif -------------------------------------------------------------------------------- /sections/cart-live-region-text.liquid: -------------------------------------------------------------------------------- 1 | {{ 'sections.cart.new_subtotal' | t }}: {{ cart.total_price | money_with_currency }} 2 | -------------------------------------------------------------------------------- /assets/section-related-products.css: -------------------------------------------------------------------------------- 1 | .related-products { 2 | display: block; 3 | } 4 | 5 | .related-products__heading { 6 | margin: 0 0 3rem; 7 | } 8 | -------------------------------------------------------------------------------- /templates/404.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404", 5 | "settings": {} 6 | } 7 | }, 8 | "order": [ 9 | "main" 10 | ] 11 | } -------------------------------------------------------------------------------- /assets/component-show-more.css: -------------------------------------------------------------------------------- 1 | .button-show-more { 2 | padding-left: 0; 3 | justify-content: flex-start; 4 | padding-bottom: 1.1rem; 5 | } 6 | 7 | .button-show-more, 8 | .button-show-less { 9 | margin-top: 1.5rem; 10 | } 11 | -------------------------------------------------------------------------------- /assets/constants.js: -------------------------------------------------------------------------------- 1 | const ON_CHANGE_DEBOUNCE_TIMER = 300; 2 | 3 | const PUB_SUB_EVENTS = { 4 | cartUpdate: 'cart-update', 5 | quantityUpdate: 'quantity-update', 6 | variantChange: 'variant-change', 7 | cartError: 'cart-error', 8 | }; 9 | -------------------------------------------------------------------------------- /sections/upsell-slider.liquid: -------------------------------------------------------------------------------- 1 | {% schema %} 2 | { 3 | "name": "Section name", 4 | "settings": [] 5 | } 6 | {% endschema %} 7 | 8 | {% stylesheet %} 9 | {% endstylesheet %} 10 | 11 | {% javascript %} 12 | {% endjavascript %} -------------------------------------------------------------------------------- /templates/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-page", 5 | "settings": { 6 | "padding_top": 28, 7 | "padding_bottom": 28 8 | } 9 | } 10 | }, 11 | "order": [ 12 | "main" 13 | ] 14 | } -------------------------------------------------------------------------------- /templates/customers/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-login", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | } 10 | }, 11 | "order": [ 12 | "main" 13 | ] 14 | } -------------------------------------------------------------------------------- /templates/customers/order.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-order", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | } 10 | }, 11 | "order": [ 12 | "main" 13 | ] 14 | } -------------------------------------------------------------------------------- /templates/customers/account.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-account", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | } 10 | }, 11 | "order": [ 12 | "main" 13 | ] 14 | } -------------------------------------------------------------------------------- /templates/customers/addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-addresses", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | } 10 | }, 11 | "order": [ 12 | "main" 13 | ] 14 | } -------------------------------------------------------------------------------- /templates/customers/register.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-register", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | } 10 | }, 11 | "order": [ 12 | "main" 13 | ] 14 | } -------------------------------------------------------------------------------- /templates/customers/reset_password.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-reset-password", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | } 10 | }, 11 | "order": [ 12 | "main" 13 | ] 14 | } -------------------------------------------------------------------------------- /templates/customers/activate_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-activate-account", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | } 10 | }, 11 | "order": [ 12 | "main" 13 | ] 14 | } -------------------------------------------------------------------------------- /snippets/icon-caret.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /assets/password-modal.js: -------------------------------------------------------------------------------- 1 | class PasswordModal extends DetailsModal { 2 | constructor() { 3 | super(); 4 | 5 | if (this.querySelector('input[aria-invalid="true"]')) this.open({ target: this.querySelector('details') }); 6 | } 7 | } 8 | 9 | customElements.define('password-modal', PasswordModal); 10 | -------------------------------------------------------------------------------- /snippets/icon-tumblr.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /snippets/revy-bundle-script.liquid: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /snippets/icon-minus.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-discount.liquid: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /snippets/mask-arch.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /snippets/icon-youtube.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /snippets/icon-tiktok.liquid: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /templates/list-collections.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-list-collections", 5 | "settings": { 6 | "title": "Collections", 7 | "sort": "alphabetical", 8 | "image_ratio": "square", 9 | "columns_desktop": 3, 10 | "columns_mobile": "2" 11 | } 12 | } 13 | }, 14 | "order": [ 15 | "main" 16 | ] 17 | } -------------------------------------------------------------------------------- /snippets/icon-plus.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-hamburger.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /templates/blog.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-blog", 5 | "settings": { 6 | "layout": "collage", 7 | "show_image": true, 8 | "image_height": "medium", 9 | "show_date": true, 10 | "show_author": false, 11 | "padding_top": 36, 12 | "padding_bottom": 36 13 | } 14 | } 15 | }, 16 | "order": [ 17 | "main" 18 | ] 19 | } -------------------------------------------------------------------------------- /snippets/icon-arrow.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /assets/section-main-page.css: -------------------------------------------------------------------------------- 1 | .page-title { 2 | margin-top: 0; 3 | } 4 | 5 | .main-page-title { 6 | margin-bottom: 3rem; 7 | } 8 | 9 | @media screen and (min-width: 750px) { 10 | .main-page-title { 11 | margin-bottom: 4rem; 12 | } 13 | } 14 | 15 | .page-placeholder-wrapper { 16 | display: flex; 17 | justify-content: center; 18 | } 19 | 20 | .page-placeholder { 21 | width: 52.5rem; 22 | height: 52.5rem; 23 | } 24 | -------------------------------------------------------------------------------- /snippets/icon-checkmark.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-tick.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-close.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-clipboard.liquid: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /snippets/icon-success.liquid: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /snippets/icon-play.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /assets/component-list-payment.css: -------------------------------------------------------------------------------- 1 | .list-payment { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | margin: -0.5rem 0; 6 | padding-top: 1rem; 7 | padding-left: 0; 8 | } 9 | 10 | @media screen and (min-width: 750px) { 11 | .list-payment { 12 | justify-content: flex-end; 13 | margin: -0.5rem; 14 | padding-top: 0; 15 | } 16 | } 17 | 18 | .list-payment__item { 19 | align-items: center; 20 | display: flex; 21 | padding: 0.5rem; 22 | } 23 | -------------------------------------------------------------------------------- /snippets/icon-close-small.liquid: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /snippets/icon-facebook.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /snippets/icon-unavailable.liquid: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /snippets/icon-cart-empty.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-vimeo.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /templates/page.contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-page", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | }, 10 | "form": { 11 | "type": "contact-form", 12 | "settings": { 13 | "heading": "", 14 | "heading_size": "h1", 15 | "color_scheme": "background-1", 16 | "padding_top": 36, 17 | "padding_bottom": 36 18 | } 19 | } 20 | }, 21 | "order": [ 22 | "main", 23 | "form" 24 | ] 25 | } -------------------------------------------------------------------------------- /sections/cart-icon-bubble.liquid: -------------------------------------------------------------------------------- 1 | {%- liquid 2 | if cart == empty 3 | render 'icon-cart-empty' 4 | else 5 | render 'icon-cart' 6 | endif 7 | -%} 8 | {{ 'templates.cart.cart' | t }} 9 | {%- if cart != empty -%} 10 |
11 | {%- if cart.item_count < 100 -%} 12 | 13 | {%- endif -%} 14 | {{ 'sections.header.cart_count' | t: count: cart.item_count }} 15 |
16 | {%- endif -%} 17 | -------------------------------------------------------------------------------- /assets/component-list-social.css: -------------------------------------------------------------------------------- 1 | .list-social { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: flex-end; 5 | } 6 | 7 | @media only screen and (max-width: 749px) { 8 | .list-social { 9 | justify-content: center; 10 | } 11 | } 12 | 13 | .list-social__item .icon { 14 | height: 1.8rem; 15 | width: 1.8rem; 16 | } 17 | 18 | .list-social__link { 19 | align-items: center; 20 | display: flex; 21 | padding: 1.4rem; 22 | color: rgb(var(--color-foreground)); 23 | } 24 | 25 | .list-social__link:hover .icon { 26 | transform: scale(1.07); 27 | } 28 | -------------------------------------------------------------------------------- /snippets/icon-account.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-cart.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-padlock.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /snippets/icon-filter.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /sections/main-404.liquid: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 |

15 | {{ 'templates.404.subtext' | t }} 16 |

17 |

18 | {{ 'templates.404.title' | t }} 19 |

20 | 21 | {{ 'general.continue_shopping' | t }} 22 | 23 |
24 | -------------------------------------------------------------------------------- /snippets/icon-pause.liquid: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /assets/pubsub.js: -------------------------------------------------------------------------------- 1 | let subscribers = {}; 2 | 3 | function subscribe(eventName, callback) { 4 | if (subscribers[eventName] === undefined) { 5 | subscribers[eventName] = []; 6 | } 7 | 8 | subscribers[eventName] = [...subscribers[eventName], callback]; 9 | 10 | return function unsubscribe() { 11 | subscribers[eventName] = subscribers[eventName].filter((cb) => { 12 | return cb !== callback; 13 | }); 14 | }; 15 | } 16 | 17 | function publish(eventName, data) { 18 | if (subscribers[eventName]) { 19 | subscribers[eventName].forEach((callback) => { 20 | callback(data); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /assets/component-totals.css: -------------------------------------------------------------------------------- 1 | .totals { 2 | display: flex; 3 | justify-content: center; 4 | align-items: flex-end; 5 | } 6 | 7 | .totals > * { 8 | font-size: 1.6rem; 9 | margin: 0; 10 | } 11 | 12 | .totals > h2 { 13 | font-size: calc(var(--font-heading-scale) * 1.6rem); 14 | } 15 | 16 | .totals * { 17 | line-height: 1; 18 | } 19 | 20 | .totals > * + * { 21 | margin-left: 2rem; 22 | } 23 | 24 | .totals__subtotal-value { 25 | font-size: 1.8rem; 26 | } 27 | 28 | .cart__ctas + .totals { 29 | margin-top: 2rem; 30 | } 31 | 32 | @media all and (min-width: 750px) { 33 | .totals { 34 | justify-content: flex-end; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /assets/component-list-menu.css: -------------------------------------------------------------------------------- 1 | .list-menu--right { 2 | right: 0; 3 | } 4 | 5 | .list-menu--disclosure { 6 | position: absolute; 7 | min-width: 100%; 8 | width: 20rem; 9 | border: 1px solid rgba(var(--color-foreground), 0.2); 10 | } 11 | 12 | .list-menu--disclosure:focus { 13 | outline: none; 14 | } 15 | 16 | .list-menu__item--active { 17 | text-decoration: underline; 18 | text-underline-offset: 0.3rem; 19 | } 20 | 21 | .list-menu__item--active:hover { 22 | text-decoration-thickness: 0.2rem; 23 | } 24 | 25 | .list-menu--disclosure.localization-selector { 26 | max-height: 18rem; 27 | overflow: auto; 28 | width: 10rem; 29 | padding: 0.5rem; 30 | } 31 | -------------------------------------------------------------------------------- /snippets/icon-remove.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /snippets/icon-twitter.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /templates/search.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-search", 5 | "settings": { 6 | "columns_desktop": 4, 7 | "image_ratio": "adapt", 8 | "image_shape": "default", 9 | "show_secondary_image": false, 10 | "show_vendor": false, 11 | "show_rating": false, 12 | "enable_filtering": true, 13 | "filter_type": "horizontal", 14 | "enable_sorting": true, 15 | "article_show_date": true, 16 | "article_show_author": false, 17 | "columns_mobile": "2", 18 | "padding_top": 36, 19 | "padding_bottom": 36 20 | } 21 | } 22 | }, 23 | "order": [ 24 | "main" 25 | ] 26 | } -------------------------------------------------------------------------------- /assets/component-discounts.css: -------------------------------------------------------------------------------- 1 | .discounts { 2 | font-size: 1.2rem; 3 | } 4 | 5 | .discounts__discount { 6 | display: flex; 7 | align-items: center; 8 | line-height: calc(1 + 0.5 / var(--font-body-scale)); 9 | } 10 | 11 | .discounts__discount svg { 12 | color: rgba(var(--color-button), var(--alpha-button-background)); 13 | } 14 | 15 | .discounts__discount--position { 16 | justify-content: center; 17 | } 18 | 19 | @media screen and (min-width: 750px) { 20 | .discounts__discount--position { 21 | justify-content: flex-end; 22 | } 23 | } 24 | 25 | .discounts__discount > .icon { 26 | color: rgb(var(--color-foreground)); 27 | width: 1.2rem; 28 | height: 1.2rem; 29 | margin-right: 0.7rem; 30 | } 31 | -------------------------------------------------------------------------------- /templates/cart.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "cart-items": { 4 | "type": "main-cart-items", 5 | "settings": { 6 | "padding_top": 36, 7 | "padding_bottom": 36 8 | } 9 | }, 10 | "cart-footer": { 11 | "type": "main-cart-footer", 12 | "blocks": { 13 | "subtotal": { 14 | "type": "subtotal", 15 | "settings": {} 16 | }, 17 | "buttons": { 18 | "type": "buttons", 19 | "settings": {} 20 | } 21 | }, 22 | "block_order": [ 23 | "subtotal", 24 | "buttons" 25 | ], 26 | "settings": {} 27 | } 28 | }, 29 | "order": [ 30 | "cart-items", 31 | "cart-footer" 32 | ] 33 | } -------------------------------------------------------------------------------- /sections/footer-group.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "t:sections.footer.name", 3 | "type": "footer", 4 | "sections": { 5 | "footer": { 6 | "type": "footer", 7 | "settings": { 8 | "color_scheme": "background-1", 9 | "newsletter_enable": true, 10 | "newsletter_heading": "Subscribe to our emails", 11 | "enable_follow_on_shop": true, 12 | "show_social": true, 13 | "enable_country_selector": true, 14 | "enable_language_selector": true, 15 | "payment_enable": true, 16 | "show_policy": false, 17 | "margin_top": 36, 18 | "padding_top": 36, 19 | "padding_bottom": 36 20 | } 21 | } 22 | }, 23 | "order": [ 24 | "footer" 25 | ] 26 | } -------------------------------------------------------------------------------- /assets/section-contact-form.css: -------------------------------------------------------------------------------- 1 | .contact img { 2 | max-width: 100%; 3 | } 4 | 5 | .contact .form__message { 6 | align-items: flex-start; 7 | } 8 | 9 | .contact .icon-success { 10 | margin-top: 0.2rem; 11 | } 12 | 13 | .contact .field { 14 | margin-bottom: 1.5rem; 15 | } 16 | 17 | @media screen and (min-width: 750px) { 18 | .contact .field { 19 | margin-bottom: 2rem; 20 | } 21 | } 22 | 23 | .contact__button { 24 | margin-top: 3rem; 25 | } 26 | 27 | @media screen and (min-width: 750px) { 28 | .contact__button { 29 | margin-top: 4rem; 30 | } 31 | } 32 | 33 | @media screen and (min-width: 750px) { 34 | .contact__fields { 35 | display: grid; 36 | grid-template-columns: repeat(2, 1fr); 37 | grid-column-gap: 2rem; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sections/apps.liquid: -------------------------------------------------------------------------------- 1 |
2 | {%- for block in section.blocks -%} 3 | {% render block %} 4 | {%- endfor -%} 5 |
6 | 7 | {% schema %} 8 | { 9 | "name": "t:sections.apps.name", 10 | "tag": "section", 11 | "class": "section", 12 | "settings": [ 13 | { 14 | "type": "checkbox", 15 | "id": "include_margins", 16 | "default": true, 17 | "label": "t:sections.apps.settings.include_margins.label" 18 | } 19 | ], 20 | "blocks": [ 21 | { 22 | "type": "@app" 23 | } 24 | ], 25 | "presets": [ 26 | { 27 | "name": "t:sections.apps.presets.name" 28 | } 29 | ] 30 | } 31 | {% endschema %} 32 | -------------------------------------------------------------------------------- /templates/article.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-article", 5 | "blocks": { 6 | "featured_image": { 7 | "type": "featured_image", 8 | "settings": { 9 | "image_height": "adapt" 10 | } 11 | }, 12 | "title": { 13 | "type": "title", 14 | "settings": { 15 | "blog_show_date": true, 16 | "blog_show_author": false 17 | } 18 | }, 19 | "share": { 20 | "type": "share", 21 | "settings": { 22 | "share_label": "Share" 23 | } 24 | }, 25 | "content": { 26 | "type": "content", 27 | "settings": {} 28 | } 29 | }, 30 | "block_order": [ 31 | "featured_image", 32 | "title", 33 | "share", 34 | "content" 35 | ], 36 | "settings": {} 37 | } 38 | }, 39 | "order": [ 40 | "main" 41 | ] 42 | } -------------------------------------------------------------------------------- /snippets/icon-error.liquid: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /templates/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "banner": { 4 | "type": "main-collection-banner", 5 | "settings": { 6 | "show_collection_description": true, 7 | "show_collection_image": false, 8 | "color_scheme": "background-1" 9 | } 10 | }, 11 | "product-grid": { 12 | "type": "main-collection-product-grid", 13 | "settings": { 14 | "products_per_page": 16, 15 | "columns_desktop": 4, 16 | "image_ratio": "adapt", 17 | "image_shape": "default", 18 | "show_secondary_image": false, 19 | "show_vendor": false, 20 | "show_rating": false, 21 | "enable_quick_add": false, 22 | "enable_filtering": true, 23 | "filter_type": "horizontal", 24 | "enable_sorting": true, 25 | "columns_mobile": "2", 26 | "padding_top": 36, 27 | "padding_bottom": 36 28 | } 29 | } 30 | }, 31 | "order": [ 32 | "banner", 33 | "product-grid" 34 | ] 35 | } -------------------------------------------------------------------------------- /assets/show-more.js: -------------------------------------------------------------------------------- 1 | class ShowMoreButton extends HTMLElement { 2 | constructor() { 3 | super(); 4 | const button = this.querySelector('button'); 5 | button.addEventListener('click', (event) => { 6 | this.expandShowMore(event); 7 | const nextElementToFocus = event.target.closest('.parent-display').querySelector('.show-more-item'); 8 | if (nextElementToFocus && !nextElementToFocus.classList.contains('hidden')) { 9 | nextElementToFocus.querySelector('input').focus(); 10 | } 11 | }); 12 | } 13 | expandShowMore(event) { 14 | const parentDisplay = event.target.closest('[id^="Show-More-"]').closest('.parent-display'); 15 | const parentWrap = parentDisplay.querySelector('.parent-wrap'); 16 | this.querySelectorAll('.label-text').forEach((element) => element.classList.toggle('hidden')); 17 | parentDisplay.querySelectorAll('.show-more-item').forEach((item) => item.classList.toggle('hidden')); 18 | } 19 | } 20 | 21 | customElements.define('show-more-button', ShowMoreButton); 22 | -------------------------------------------------------------------------------- /assets/component-product-model.css: -------------------------------------------------------------------------------- 1 | .product__xr-button { 2 | background: rgba(var(--color-foreground), 0.08); 3 | color: rgb(var(--color-foreground)); 4 | margin: 1rem auto; 5 | box-shadow: none; 6 | display: flex; 7 | } 8 | 9 | .button.product__xr-button:hover { 10 | box-shadow: none; 11 | } 12 | 13 | .product__xr-button[data-shopify-xr-hidden] { 14 | visibility: hidden; 15 | } 16 | 17 | .shopify-design-mode .product__xr-button[data-shopify-xr-hidden] { 18 | display: none; 19 | } 20 | 21 | @media screen and (max-width: 749px) { 22 | slider-component .product__xr-button { 23 | display: none; 24 | } 25 | 26 | .active .product__xr-button:not([data-shopify-xr-hidden]) { 27 | display: block; 28 | } 29 | } 30 | 31 | @media screen and (min-width: 750px) { 32 | slider-component + .button.product__xr-button { 33 | display: none; 34 | } 35 | 36 | .product__xr-button[data-shopify-xr-hidden] { 37 | display: none; 38 | } 39 | } 40 | 41 | .product__xr-button .icon { 42 | width: 1.4rem; 43 | margin-right: 1rem; 44 | } 45 | -------------------------------------------------------------------------------- /snippets/icon-pinterest.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /assets/newsletter-section.css: -------------------------------------------------------------------------------- 1 | .newsletter__wrapper { 2 | padding-right: calc(4rem / var(--font-body-scale)); 3 | padding-left: calc(4rem / var(--font-body-scale)); 4 | } 5 | 6 | @media screen and (min-width: 750px) { 7 | .newsletter__wrapper { 8 | padding-right: 9rem; 9 | padding-left: 9rem; 10 | } 11 | } 12 | 13 | .newsletter__wrapper > * { 14 | margin-top: 0; 15 | margin-bottom: 0; 16 | } 17 | 18 | .newsletter__wrapper > * + * { 19 | margin-top: 2rem; 20 | } 21 | 22 | .newsletter__wrapper > * + .newsletter-form { 23 | margin-top: 3rem; 24 | } 25 | 26 | .newsletter__subheading { 27 | max-width: 70rem; 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | 32 | .newsletter__wrapper .newsletter-form__field-wrapper { 33 | max-width: 36rem; 34 | } 35 | 36 | .newsletter-form__field-wrapper .newsletter-form__message { 37 | margin-top: 1.5rem; 38 | } 39 | 40 | .newsletter__button { 41 | margin-top: 3rem; 42 | width: fit-content; 43 | } 44 | 45 | @media screen and (min-width: 750px) { 46 | .newsletter__button { 47 | flex-shrink: 0; 48 | margin: 0 0 0 1rem; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /snippets/icon-share.liquid: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /templates/password.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": "password", 3 | "sections": { 4 | "main": { 5 | "type": "email-signup-banner", 6 | "blocks": { 7 | "heading": { 8 | "type": "heading", 9 | "settings": { 10 | "heading": "Opening soon", 11 | "heading_size": "h1" 12 | } 13 | }, 14 | "paragraph": { 15 | "type": "paragraph", 16 | "settings": { 17 | "text": "

Be the first to know when we launch.<\/p>", 18 | "text_style": "body" 19 | } 20 | }, 21 | "email_form": { 22 | "type": "email_form", 23 | "settings": {} 24 | } 25 | }, 26 | "block_order": [ 27 | "heading", 28 | "paragraph", 29 | "email_form" 30 | ], 31 | "settings": { 32 | "image_overlay_opacity": 0, 33 | "show_background_image": true, 34 | "image_height": "medium", 35 | "desktop_content_position": "middle-center", 36 | "show_text_box": true, 37 | "desktop_content_alignment": "center", 38 | "color_scheme": "background-1", 39 | "mobile_content_alignment": "center", 40 | "show_text_below": true 41 | } 42 | } 43 | }, 44 | "order": [ 45 | "main" 46 | ] 47 | } -------------------------------------------------------------------------------- /snippets/icon-instagram.liquid: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /sections/header-group.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "t:sections.header.name", 3 | "type": "header", 4 | "sections": { 5 | "announcement-bar": { 6 | "type": "announcement-bar", 7 | "blocks": { 8 | "announcement-bar-0": { 9 | "type": "announcement", 10 | "settings": { 11 | "text": "Welcome to our store", 12 | "link": "" 13 | } 14 | } 15 | }, 16 | "block_order": [ 17 | "announcement-bar-0" 18 | ], 19 | "settings": { 20 | "color_scheme": "background-1", 21 | "show_line_separator": true, 22 | "auto_rotate": false, 23 | "change_slides_speed": 5 24 | } 25 | }, 26 | "header": { 27 | "type": "header", 28 | "settings": { 29 | "logo_position": "middle-left", 30 | "menu": "main-menu", 31 | "menu_type_desktop": "dropdown", 32 | "sticky_header_type": "on-scroll-up", 33 | "show_line_separator": true, 34 | "color_scheme": "background-1", 35 | "enable_country_selector": false, 36 | "enable_language_selector": false, 37 | "mobile_logo_position": "center", 38 | "margin_bottom": 0, 39 | "padding_top": 20, 40 | "padding_bottom": 20 41 | } 42 | } 43 | }, 44 | "order": [ 45 | "announcement-bar", 46 | "header" 47 | ] 48 | } -------------------------------------------------------------------------------- /snippets/icon-3d-model.liquid: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /assets/component-rating.css: -------------------------------------------------------------------------------- 1 | .rating { 2 | display: inline-block; 3 | margin: 0; 4 | } 5 | 6 | .product .rating-star { 7 | --letter-spacing: 0.8; 8 | --font-size: 1.7; 9 | } 10 | 11 | .card-wrapper .rating-star { 12 | --letter-spacing: 0.7; 13 | --font-size: 1.4; 14 | } 15 | 16 | .rating-star { 17 | --color-rating-star: rgb(var(--color-foreground)); 18 | --percent: calc( 19 | ( 20 | var(--rating) / var(--rating-max) + var(--rating-decimal) * var(--font-size) / 21 | (var(--rating-max) * (var(--letter-spacing) + var(--font-size))) 22 | ) * 100% 23 | ); 24 | letter-spacing: calc(var(--letter-spacing) * 1rem); 25 | font-size: calc(var(--font-size) * 1rem); 26 | line-height: 1; 27 | display: inline-block; 28 | font-family: Times; 29 | margin: 0; 30 | } 31 | 32 | .rating-star::before { 33 | content: '★★★★★'; 34 | background: linear-gradient( 35 | 90deg, 36 | var(--color-rating-star) var(--percent), 37 | rgba(var(--color-foreground), 0.15) var(--percent) 38 | ); 39 | -webkit-background-clip: text; 40 | -webkit-text-fill-color: transparent; 41 | } 42 | 43 | .rating-text { 44 | display: none; 45 | } 46 | 47 | .rating-count { 48 | display: inline-block; 49 | margin: 0; 50 | } 51 | 52 | @media (forced-colors: active) { 53 | .rating { 54 | display: none; 55 | } 56 | 57 | .rating-text { 58 | display: block; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /assets/section-collection-list.css: -------------------------------------------------------------------------------- 1 | .collection-list { 2 | margin-top: 0; 3 | margin-bottom: 0; 4 | } 5 | 6 | .collection-list-title { 7 | margin: 0; 8 | } 9 | 10 | @media screen and (max-width: 749px) { 11 | .collection-list:not(.slider) { 12 | padding-left: 0; 13 | padding-right: 0; 14 | } 15 | 16 | .section-collection-list .page-width { 17 | padding-left: 0; 18 | padding-right: 0; 19 | } 20 | 21 | .section-collection-list .collection-list:not(.slider) { 22 | padding-left: 1.5rem; 23 | padding-right: 1.5rem; 24 | } 25 | } 26 | 27 | .collection-list__item:only-child { 28 | max-width: 100%; 29 | width: 100%; 30 | } 31 | 32 | @media screen and (max-width: 749px) { 33 | .slider.collection-list--1-items { 34 | padding-bottom: 0; 35 | } 36 | } 37 | 38 | @media screen and (min-width: 750px) and (max-width: 989px) { 39 | .slider.collection-list--1-items, 40 | .slider.collection-list--2-items, 41 | .slider.collection-list--3-items, 42 | .slider.collection-list--4-items { 43 | padding-bottom: 0; 44 | } 45 | } 46 | 47 | @media screen and (min-width: 750px) { 48 | .collection-list__item a:hover { 49 | box-shadow: none; 50 | } 51 | } 52 | 53 | @media screen and (max-width: 989px) { 54 | .collection-list.slider .collection-list__item { 55 | max-width: 100%; 56 | } 57 | } 58 | 59 | .collection-list-view-all { 60 | margin-top: 2rem; 61 | } 62 | -------------------------------------------------------------------------------- /assets/video-section.css: -------------------------------------------------------------------------------- 1 | .video-section__media { 2 | --ratio-percent: 56.25%; 3 | position: relative; 4 | padding-bottom: calc(var(--ratio-percent) - var(--media-border-width)); 5 | } 6 | 7 | /* Needed for gradient continuity with or without animation so that transparent PNG images come up as we would expect */ 8 | .scroll-trigger:where(.gradient.video-section__media) { 9 | background: transparent; 10 | } 11 | 12 | .video-section__media.global-media-settings--full-width { 13 | padding-bottom: var(--ratio-percent); 14 | } 15 | 16 | .video-section__media.deferred-media { 17 | box-shadow: var(--media-shadow-horizontal-offset) var(--media-shadow-vertical-offset) var(--media-shadow-blur-radius) 18 | rgba(var(--color-shadow), var(--media-shadow-opacity)); 19 | } 20 | 21 | .video-section__media.deferred-media:after { 22 | content: none; 23 | } 24 | 25 | .video-section__poster.deferred-media__poster:focus { 26 | outline-offset: 0.3rem; 27 | } 28 | 29 | .video-section__media iframe { 30 | background-color: rgba(var(--color-foreground), 0.03); 31 | border: 0; 32 | } 33 | 34 | .video-section__poster, 35 | .video-section__media iframe, 36 | .video-section__media video { 37 | position: absolute; 38 | width: 100%; 39 | height: 100%; 40 | } 41 | 42 | .video-section__media video { 43 | background: #000000; 44 | } 45 | 46 | .video-section__media.media-fit-cover video { 47 | object-fit: cover; 48 | } 49 | -------------------------------------------------------------------------------- /assets/component-accordion.css: -------------------------------------------------------------------------------- 1 | .accordion summary { 2 | display: flex; 3 | position: relative; 4 | line-height: 1; 5 | padding: 1.5rem 0; 6 | } 7 | 8 | .accordion .summary__title { 9 | display: flex; 10 | flex: 1; 11 | } 12 | 13 | .accordion .summary__title + .icon-caret { 14 | height: calc(var(--font-heading-scale) * 0.6rem); 15 | } 16 | 17 | .accordion + .accordion { 18 | margin-top: 0; 19 | border-top: none; 20 | } 21 | 22 | .accordion { 23 | margin-top: 2.5rem; 24 | margin-bottom: 0; 25 | border-top: 0.1rem solid rgba(var(--color-foreground), 0.08); 26 | border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.08); 27 | } 28 | 29 | .accordion__title { 30 | display: inline-block; 31 | max-width: calc(100% - 6rem); 32 | min-height: 1.6rem; 33 | margin: 0; 34 | word-break: break-word; 35 | } 36 | 37 | .accordion .icon-accordion { 38 | align-self: center; 39 | fill: rgb(var(--color-foreground)); 40 | height: calc(var(--font-heading-scale) * 2rem); 41 | margin-right: calc(var(--font-heading-scale) * 1rem); 42 | width: calc(var(--font-heading-scale) * 2rem); 43 | } 44 | 45 | .accordion details[open] > summary .icon-caret { 46 | transform: rotate(180deg); 47 | } 48 | 49 | .accordion__content { 50 | margin-bottom: 1.5rem; 51 | word-break: break-word; 52 | overflow-x: auto; 53 | padding: 0 0.6rem; 54 | } 55 | 56 | .accordion__content img { 57 | max-width: 100%; 58 | } 59 | -------------------------------------------------------------------------------- /assets/main-search.js: -------------------------------------------------------------------------------- 1 | class MainSearch extends SearchForm { 2 | constructor() { 3 | super(); 4 | this.allSearchInputs = document.querySelectorAll('input[type="search"]'); 5 | this.setupEventListeners(); 6 | } 7 | 8 | setupEventListeners() { 9 | let allSearchForms = []; 10 | this.allSearchInputs.forEach((input) => allSearchForms.push(input.form)); 11 | this.input.addEventListener('focus', this.onInputFocus.bind(this)); 12 | if (allSearchForms.length < 2) return; 13 | allSearchForms.forEach((form) => form.addEventListener('reset', this.onFormReset.bind(this))); 14 | this.allSearchInputs.forEach((input) => input.addEventListener('input', this.onInput.bind(this))); 15 | } 16 | 17 | onFormReset(event) { 18 | super.onFormReset(event); 19 | if (super.shouldResetForm()) { 20 | this.keepInSync('', this.input); 21 | } 22 | } 23 | 24 | onInput(event) { 25 | const target = event.target; 26 | this.keepInSync(target.value, target); 27 | } 28 | 29 | onInputFocus() { 30 | const isSmallScreen = window.innerWidth < 750; 31 | if (isSmallScreen) { 32 | this.scrollIntoView({ behavior: 'smooth' }); 33 | } 34 | } 35 | 36 | keepInSync(value, target) { 37 | this.allSearchInputs.forEach((input) => { 38 | if (input !== target) { 39 | input.value = value; 40 | } 41 | }); 42 | } 43 | } 44 | 45 | customElements.define('main-search', MainSearch); 46 | -------------------------------------------------------------------------------- /snippets/icon-zoom.liquid: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /assets/product-modal.js: -------------------------------------------------------------------------------- 1 | if (!customElements.get('product-modal')) { 2 | customElements.define( 3 | 'product-modal', 4 | class ProductModal extends ModalDialog { 5 | constructor() { 6 | super(); 7 | } 8 | 9 | hide() { 10 | super.hide(); 11 | } 12 | 13 | show(opener) { 14 | super.show(opener); 15 | this.showActiveMedia(); 16 | } 17 | 18 | showActiveMedia() { 19 | this.querySelectorAll( 20 | `[data-media-id]:not([data-media-id="${this.openedBy.getAttribute('data-media-id')}"])` 21 | ).forEach((element) => { 22 | element.classList.remove('active'); 23 | }); 24 | const activeMedia = this.querySelector(`[data-media-id="${this.openedBy.getAttribute('data-media-id')}"]`); 25 | const activeMediaTemplate = activeMedia.querySelector('template'); 26 | const activeMediaContent = activeMediaTemplate ? activeMediaTemplate.content : null; 27 | activeMedia.classList.add('active'); 28 | activeMedia.scrollIntoView(); 29 | 30 | const container = this.querySelector('[role="document"]'); 31 | container.scrollLeft = (activeMedia.width - container.clientWidth) / 2; 32 | 33 | if ( 34 | activeMedia.nodeName == 'DEFERRED-MEDIA' && 35 | activeMediaContent && 36 | activeMediaContent.querySelector('.js-youtube') 37 | ) 38 | activeMedia.loadContent(); 39 | } 40 | } 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /assets/component-loading-overlay.css: -------------------------------------------------------------------------------- 1 | .loading-overlay { 2 | position: absolute; 3 | z-index: 1; 4 | width: 1.8rem; 5 | } 6 | 7 | @media screen and (max-width: 749px) { 8 | .loading-overlay { 9 | top: 0; 10 | right: 0; 11 | } 12 | } 13 | 14 | @media screen and (min-width: 750px) { 15 | .loading-overlay { 16 | left: 0; 17 | } 18 | } 19 | 20 | .loading-overlay__spinner { 21 | width: 1.8rem; 22 | display: inline-block; 23 | } 24 | 25 | .spinner { 26 | animation: rotator 1.4s linear infinite; 27 | } 28 | 29 | @keyframes rotator { 30 | 0% { 31 | transform: rotate(0deg); 32 | } 33 | 100% { 34 | transform: rotate(270deg); 35 | } 36 | } 37 | 38 | .path { 39 | stroke-dasharray: 280; 40 | stroke-dashoffset: 0; 41 | transform-origin: center; 42 | stroke: rgb(var(--color-foreground)); 43 | animation: dash 1.4s ease-in-out infinite; 44 | } 45 | 46 | @media screen and (forced-colors: active) { 47 | .path { 48 | stroke: CanvasText; 49 | } 50 | } 51 | 52 | @keyframes dash { 53 | 0% { 54 | stroke-dashoffset: 280; 55 | } 56 | 50% { 57 | stroke-dashoffset: 75; 58 | transform: rotate(135deg); 59 | } 60 | 100% { 61 | stroke-dashoffset: 280; 62 | transform: rotate(450deg); 63 | } 64 | } 65 | 66 | .loading-overlay:not(.hidden) + .cart-item__price-wrapper, 67 | .loading-overlay:not(.hidden) ~ cart-remove-button { 68 | opacity: 50%; 69 | } 70 | 71 | .loading-overlay:not(.hidden) ~ cart-remove-button { 72 | pointer-events: none; 73 | cursor: default; 74 | } 75 | -------------------------------------------------------------------------------- /snippets/cart-upsell-item.liquid: -------------------------------------------------------------------------------- 1 |

2 |
3 |
4 | 5 | 6 | 7 |
8 |
9 |
10 | 11 | 12 | {{- upsell_product.title -}} | 13 | 14 | 15 | 16 | {{ upsell_product.selected_or_first_available_variant.price | money_with_currency -}} 17 | 18 |
19 | 20 | 21 | 22 | 33 | 34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /assets/search-form.js: -------------------------------------------------------------------------------- 1 | class SearchForm extends HTMLElement { 2 | constructor() { 3 | super(); 4 | this.input = this.querySelector('input[type="search"]'); 5 | this.resetButton = this.querySelector('button[type="reset"]'); 6 | 7 | if (this.input) { 8 | this.input.form.addEventListener('reset', this.onFormReset.bind(this)); 9 | this.input.addEventListener( 10 | 'input', 11 | debounce((event) => { 12 | this.onChange(event); 13 | }, 300).bind(this) 14 | ); 15 | } 16 | } 17 | 18 | toggleResetButton() { 19 | const resetIsHidden = this.resetButton.classList.contains('hidden'); 20 | if (this.input.value.length > 0 && resetIsHidden) { 21 | this.resetButton.classList.remove('hidden'); 22 | } else if (this.input.value.length === 0 && !resetIsHidden) { 23 | this.resetButton.classList.add('hidden'); 24 | } 25 | } 26 | 27 | onChange() { 28 | this.toggleResetButton(); 29 | } 30 | 31 | shouldResetForm() { 32 | return !document.querySelector('[aria-selected="true"] a'); 33 | } 34 | 35 | onFormReset(event) { 36 | // Prevent default so the form reset doesn't set the value gotten from the url on page load 37 | event.preventDefault(); 38 | // Don't reset if the user has selected an element on the predictive search dropdown 39 | if (this.shouldResetForm()) { 40 | this.input.value = ''; 41 | this.input.focus(); 42 | this.toggleResetButton(); 43 | } 44 | } 45 | } 46 | 47 | customElements.define('search-form', SearchForm); 48 | -------------------------------------------------------------------------------- /sections/revy-contents.liquid: -------------------------------------------------------------------------------- 1 |
2 | {% for block in section.blocks %} 3 |
4 |
5 | {{ block.settings.revy-content-section }} 6 |
7 |
8 | {% endfor %} 9 |
10 | 11 | {% schema %} 12 | { 13 | "name": "Revy Contents", 14 | "max_blocks": 20, 15 | "blocks" : [ 16 | { 17 | "type": "html", 18 | "name": "Revy Content", 19 | "settings": [ 20 | { 21 | "type": "textarea", 22 | "id": "revy-content-section", 23 | "default": "", 24 | "label": "Revy Content" 25 | } 26 | ] 27 | } 28 | ], 29 | "presets": [ 30 | { 31 | "name": "Revy Content", 32 | "category": "Revy Apps", 33 | "blocks": [ 34 | { 35 | "type": "html" 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | {% endschema %} 42 | 43 | 44 | {% stylesheet %} 45 | .revy-custom-content-section-content { 46 | display: -webkit-box; 47 | display: -ms-flexbox; 48 | display: flex; 49 | -webkit-box-align: center; 50 | -ms-flex-align: center; 51 | align-items: center; 52 | -webkit-box-pack: center; 53 | -ms-flex-pack: center; 54 | justify-content: center; 55 | margin: 10px; 56 | max-width: 100%; 57 | -ms-flex-wrap: wrap; 58 | flex-wrap: wrap; 59 | } 60 | {% endstylesheet %} 61 | 62 | {% javascript %} 63 | {% endjavascript %} 64 | -------------------------------------------------------------------------------- /assets/product-model.js: -------------------------------------------------------------------------------- 1 | if (!customElements.get('product-model')) { 2 | customElements.define( 3 | 'product-model', 4 | class ProductModel extends DeferredMedia { 5 | constructor() { 6 | super(); 7 | } 8 | 9 | loadContent() { 10 | super.loadContent(); 11 | 12 | Shopify.loadFeatures([ 13 | { 14 | name: 'model-viewer-ui', 15 | version: '1.0', 16 | onLoad: this.setupModelViewerUI.bind(this), 17 | }, 18 | ]); 19 | } 20 | 21 | setupModelViewerUI(errors) { 22 | if (errors) return; 23 | 24 | this.modelViewerUI = new Shopify.ModelViewerUI(this.querySelector('model-viewer')); 25 | } 26 | } 27 | ); 28 | } 29 | 30 | window.ProductModel = { 31 | loadShopifyXR() { 32 | Shopify.loadFeatures([ 33 | { 34 | name: 'shopify-xr', 35 | version: '1.0', 36 | onLoad: this.setupShopifyXR.bind(this), 37 | }, 38 | ]); 39 | }, 40 | 41 | setupShopifyXR(errors) { 42 | if (errors) return; 43 | 44 | if (!window.ShopifyXR) { 45 | document.addEventListener('shopify_xr_initialized', () => this.setupShopifyXR()); 46 | return; 47 | } 48 | 49 | document.querySelectorAll('[id^="ProductJSON-"]').forEach((modelJSON) => { 50 | window.ShopifyXR.addModels(JSON.parse(modelJSON.textContent)); 51 | modelJSON.remove(); 52 | }); 53 | window.ShopifyXR.setupXRElements(); 54 | }, 55 | }; 56 | 57 | window.addEventListener('DOMContentLoaded', () => { 58 | if (window.ProductModel) window.ProductModel.loadShopifyXR(); 59 | }); 60 | -------------------------------------------------------------------------------- /assets/custom.css: -------------------------------------------------------------------------------- 1 | .cartupsell-item { 2 | border: 1px solid #dbdbdb; 3 | border-radius: 5px; 4 | } 5 | 6 | .cartupsell-slick-slider { 7 | width: 100%; 8 | } 9 | 10 | .cartupsell-panel { 11 | width: 100%; 12 | padding: 10px; 13 | border-radius: 5px; 14 | display: flex; 15 | column-gap:10px; 16 | } 17 | 18 | .cartupsell-image-panel a { 19 | width: 100%; 20 | height: 100%; 21 | } 22 | 23 | .cartupsell-image-panel .cartupsell-img { 24 | width: 100%; 25 | height: 100%; 26 | object-fit: cover; 27 | border-radius: 5px; 28 | } 29 | 30 | .cartupsell-product-title a { 31 | display: inline; 32 | font-size: 18px; 33 | font-weight: bold; 34 | color: #3f3f3f; 35 | text-decoration: none; 36 | } 37 | 38 | .btn-add-order { 39 | font-size: 15px; 40 | cursor: pointer; 41 | background-color: #1973b1; 42 | color: #fff; 43 | width: 100%; 44 | padding: 15px; 45 | border-radius: 3px; 46 | border: none; 47 | } 48 | 49 | .cartupsell-data-panel { 50 | width: 100%; 51 | } 52 | 53 | .cartupsell-product-price { 54 | font-size: 18px; 55 | color: #909090; 56 | } 57 | 58 | .cartupsell-product-data { 59 | margin-bottom: 5px; 60 | } 61 | 62 | .slick-dots li.slick-active button:before, 63 | .cartupsell-slick-slider .slick-dots li button:before { 64 | font-size: 35px; 65 | } 66 | 67 | #saved_money { 68 | display: none; 69 | } 70 | 71 | @media (max-width: 767px) { 72 | .returning-user .badge { 73 | display: none !important; 74 | } 75 | .returning-user #saved_money { 76 | display: inline-block !important; 77 | } 78 | } -------------------------------------------------------------------------------- /assets/component-model-viewer-ui.css: -------------------------------------------------------------------------------- 1 | .shopify-model-viewer-ui .shopify-model-viewer-ui__controls-area { 2 | background: rgb(var(--color-background)); 3 | border-color: rgba(var(--color-foreground), 0.04); 4 | } 5 | 6 | .shopify-model-viewer-ui .shopify-model-viewer-ui__button { 7 | color: rgba(var(--color-foreground), 0.75); 8 | } 9 | 10 | .shopify-model-viewer-ui .shopify-model-viewer-ui__button--control:hover { 11 | color: rgba(var(--color-foreground), 0.55); 12 | } 13 | 14 | .shopify-model-viewer-ui .shopify-model-viewer-ui__button--control:active, 15 | .shopify-model-viewer-ui .shopify-model-viewer-ui__button--control.focus-visible:focus { 16 | color: rgba(var(--color-foreground), 0.55); 17 | background: rgba(var(--color-foreground), 0.04); 18 | } 19 | 20 | .shopify-model-viewer-ui .shopify-model-viewer-ui__button--control:not(:last-child):after { 21 | border-color: rgba(var(--color-foreground), 0.04); 22 | } 23 | 24 | .shopify-model-viewer-ui .shopify-model-viewer-ui__button--poster { 25 | border-radius: 50%; 26 | color: rgb(var(--color-foreground)); 27 | background: rgb(var(--color-background)); 28 | border-color: rgba(var(--color-foreground), 0.1); 29 | transform: translate(-50%, -50%) scale(1); 30 | transition: transform var(--duration-short) ease, color var(--duration-short) ease; 31 | } 32 | 33 | .shopify-model-viewer-ui .shopify-model-viewer-ui__poster-control-icon { 34 | width: 4.8rem; 35 | height: 4.8rem; 36 | margin-top: 0.3rem; 37 | } 38 | 39 | .shopify-model-viewer-ui .shopify-model-viewer-ui__button--poster:hover, 40 | .shopify-model-viewer-ui .shopify-model-viewer-ui__button--poster:focus { 41 | transform: translate(-50%, -50%) scale(1.1); 42 | } 43 | -------------------------------------------------------------------------------- /snippets/language-localization.liquid: -------------------------------------------------------------------------------- 1 | {%- comment -%} 2 | Renders the language picker for the localization form 3 | 4 | Accepts: 5 | - localPosition: pass in the position in which the form is coming up to create specific IDs 6 | {%- endcomment -%} 7 | 8 |
9 | 19 | 39 |
40 | 41 | -------------------------------------------------------------------------------- /assets/component-newsletter.css: -------------------------------------------------------------------------------- 1 | .newsletter-form { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | width: 100%; 7 | position: relative; 8 | } 9 | 10 | @media screen and (min-width: 750px) { 11 | .newsletter-form { 12 | align-items: flex-start; 13 | margin: 0 auto; 14 | max-width: 36rem; 15 | } 16 | } 17 | 18 | .newsletter-form__field-wrapper { 19 | width: 100%; 20 | } 21 | 22 | .newsletter-form__field-wrapper .field__input { 23 | padding-right: 5rem; 24 | } 25 | 26 | .newsletter-form__field-wrapper .field { 27 | z-index: 0; 28 | } 29 | 30 | .newsletter-form__message { 31 | justify-content: center; 32 | margin-bottom: 0; 33 | } 34 | 35 | .newsletter-form__message--success { 36 | margin-top: 2rem; 37 | } 38 | 39 | @media screen and (min-width: 750px) { 40 | .newsletter-form__message { 41 | justify-content: flex-start; 42 | } 43 | } 44 | 45 | .newsletter-form__button { 46 | width: 4.4rem; 47 | margin: 0; 48 | right: var(--inputs-border-width); 49 | top: 0; 50 | height: 100%; 51 | z-index: 2; 52 | } 53 | 54 | .newsletter-form__button:focus-visible { 55 | box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0 0.4rem rgba(var(--color-foreground)); 56 | background-color: rgb(var(--color-background)); 57 | } 58 | 59 | .newsletter-form__button:focus { 60 | box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0 0.4rem rgba(var(--color-foreground)); 61 | background-color: rgb(var(--color-background)); 62 | } 63 | 64 | .newsletter-form__button:not(:focus-visible):not(.focused) { 65 | box-shadow: inherit; 66 | background-color: inherit; 67 | } 68 | 69 | .newsletter-form__button .icon { 70 | width: 1.5rem; 71 | } 72 | -------------------------------------------------------------------------------- /snippets/cart-upsell.liquid: -------------------------------------------------------------------------------- 1 | {% assign u_product1 = all_products[settings.u_product1] %} 2 | {% assign u_product2 = all_products[settings.u_product2] %} 3 | {% assign u_product3 = all_products[settings.u_product3] %} 4 | 5 | {% assign is_upsell1 = false %} 6 | {% assign is_upsell2 = false %} 7 | {% assign is_upsell3 = false %} 8 | 9 | {% for item in cart.items %} 10 | {% if item.product.id == u_product1.id %} 11 | {% assign is_upsell1 = true %} 12 | {% elsif item.product.id == u_product2.id %} 13 | {% assign is_upsell2 = true %} 14 | {% elsif item.product.id == u_product3.id %} 15 | {% assign is_upsell3 = true %} 16 | {% endif %} 17 | {% endfor %} 18 | 19 | {% unless is_upsell1 and is_upsell2 and is_upsell3 %} 20 |
21 |
22 | {% if settings.u_product1 and is_upsell1 == false %} 23 | {% render 'cart-upsell-item', upsell_product: u_product1 %} 24 | {% endif %} 25 | {% if settings.u_product2 and is_upsell2 == false %} 26 | {% render 'cart-upsell-item', upsell_product: u_product2 %} 27 | {% endif %} 28 | {% if settings.u_product3 and is_upsell3 == false %} 29 | {% render 'cart-upsell-item', upsell_product: u_product3 %} 30 | {% endif %} 31 |
32 |
33 | {% endunless %} 34 | 35 | -------------------------------------------------------------------------------- /assets/component-search.css: -------------------------------------------------------------------------------- 1 | .search__input.field__input { 2 | padding-right: 9.8rem; 3 | } 4 | 5 | .search__button { 6 | right: var(--inputs-border-width); 7 | top: var(--inputs-border-width); 8 | } 9 | 10 | .reset__button { 11 | right: calc(var(--inputs-border-width) + 4.4rem); 12 | top: var(--inputs-border-width); 13 | } 14 | 15 | .reset__button:not(:focus-visible)::after { 16 | border-right: 0.1rem solid rgba(var(--color-foreground), 0.08); 17 | display: block; 18 | height: calc(100% - 1.6rem); 19 | content: ''; 20 | position: absolute; 21 | right: 0; 22 | } 23 | 24 | .reset__button:not(:focus)::after { 25 | border-right: 0.1rem solid rgba(var(--color-foreground), 0.08); 26 | display: block; 27 | height: calc(100% - 1.8rem); 28 | content: ''; 29 | position: absolute; 30 | right: 0; 31 | } 32 | 33 | .search__button:focus-visible, 34 | .reset__button:focus-visible { 35 | background-color: rgb(var(--color-background)); 36 | z-index: 4; 37 | } 38 | 39 | .search__button:focus, 40 | .reset__button:focus { 41 | background-color: rgb(var(--color-background)); 42 | z-index: 4; 43 | } 44 | 45 | .search__button:not(:focus-visible):not(.focused), 46 | .reset__button:not(:focus-visible):not(.focused) { 47 | box-shadow: inherit; 48 | background-color: inherit; 49 | } 50 | 51 | .search__button:hover .icon, 52 | .reset__button:hover .icon { 53 | transform: scale(1.07); 54 | } 55 | 56 | .search__button .icon { 57 | height: 1.8rem; 58 | width: 1.8rem; 59 | } 60 | 61 | .reset__button .icon.icon-close { 62 | height: 1.8rem; 63 | width: 1.8rem; 64 | stroke-width: 0.1rem; 65 | } 66 | 67 | /* Remove extra spacing for search inputs in Safari */ 68 | input::-webkit-search-decoration { 69 | -webkit-appearance: none; 70 | } 71 | -------------------------------------------------------------------------------- /assets/component-pagination.css: -------------------------------------------------------------------------------- 1 | .pagination-wrapper { 2 | margin-top: 4rem; 3 | } 4 | 5 | @media screen and (min-width: 990px) { 6 | .pagination-wrapper { 7 | margin-top: 5rem; 8 | } 9 | } 10 | 11 | .pagination__list { 12 | display: flex; 13 | flex-wrap: wrap; 14 | justify-content: center; 15 | } 16 | 17 | .pagination__list > li { 18 | flex: 1 0 4.4rem; 19 | max-width: 4.4rem; 20 | } 21 | 22 | .pagination__list > li:not(:last-child) { 23 | margin-right: 1rem; 24 | } 25 | 26 | .pagination__item { 27 | color: rgb(var(--color-foreground)); 28 | display: inline-flex; 29 | justify-content: center; 30 | align-items: center; 31 | position: relative; 32 | height: 4.5rem; 33 | width: 100%; 34 | padding: 0; 35 | text-decoration: none; 36 | } 37 | 38 | a.pagination__item:hover::after { 39 | height: 0.1rem; 40 | } 41 | 42 | .pagination__item .icon-caret { 43 | height: 0.6rem; 44 | } 45 | 46 | .pagination__item--current::after { 47 | height: 0.1rem; 48 | } 49 | 50 | .pagination__item--current::after, 51 | .pagination__item:hover::after { 52 | content: ''; 53 | display: block; 54 | width: 2rem; 55 | position: absolute; 56 | bottom: 8px; 57 | left: 50%; 58 | transform: translateX(-50%); 59 | background-color: currentColor; 60 | } 61 | 62 | .pagination__item--next .icon { 63 | margin-left: -0.2rem; 64 | transform: rotate(90deg); 65 | } 66 | 67 | .pagination__item--next:hover .icon { 68 | transform: rotate(90deg) scale(1.07); 69 | } 70 | 71 | .pagination__item--prev .icon { 72 | margin-right: -0.2rem; 73 | transform: rotate(-90deg); 74 | } 75 | 76 | .pagination__item--prev:hover .icon { 77 | transform: rotate(-90deg) scale(1.07); 78 | } 79 | 80 | .pagination__item-arrow:hover::after { 81 | display: none; 82 | } 83 | -------------------------------------------------------------------------------- /assets/section-rich-text.css: -------------------------------------------------------------------------------- 1 | .rich-text { 2 | z-index: 1; 3 | } 4 | 5 | .rich-text__wrapper { 6 | display: flex; 7 | justify-content: center; 8 | width: calc(100% - 4rem / var(--font-body-scale)); 9 | } 10 | 11 | .rich-text:not(.rich-text--full-width) .rich-text__wrapper { 12 | margin: auto; 13 | width: calc(100% - 8rem / var(--font-body-scale)); 14 | } 15 | 16 | .rich-text__blocks { 17 | width: 100%; 18 | } 19 | 20 | @media screen and (min-width: 750px) { 21 | .rich-text__wrapper { 22 | width: 100%; 23 | } 24 | 25 | .rich-text__wrapper--left { 26 | justify-content: flex-start; 27 | } 28 | 29 | .rich-text__wrapper--right { 30 | justify-content: flex-end; 31 | } 32 | 33 | .rich-text__blocks { 34 | max-width: 50rem; 35 | } 36 | } 37 | 38 | @media screen and (min-width: 990px) { 39 | .rich-text__blocks { 40 | max-width: 78rem; 41 | } 42 | } 43 | 44 | .rich-text__blocks * { 45 | overflow-wrap: break-word; 46 | } 47 | 48 | .rich-text__blocks > * { 49 | margin-top: 0; 50 | margin-bottom: 0; 51 | } 52 | 53 | .rich-text__blocks > * + * { 54 | margin-top: 2rem; 55 | } 56 | 57 | .rich-text__blocks > * + a { 58 | margin-top: 3rem; 59 | } 60 | 61 | .rich-text__buttons { 62 | display: inline-flex; 63 | justify-content: center; 64 | flex-wrap: wrap; 65 | gap: 1rem; 66 | width: 100%; 67 | max-width: 45rem; 68 | word-break: break-word; 69 | } 70 | 71 | .rich-text__buttons--multiple > * { 72 | flex-grow: 1; 73 | min-width: 22rem; 74 | } 75 | 76 | .rich-text__buttons + .rich-text__buttons { 77 | margin-top: 1rem; 78 | } 79 | 80 | .rich-text__blocks.left .rich-text__buttons { 81 | justify-content: flex-start; 82 | } 83 | 84 | .rich-text__blocks.right .rich-text__buttons { 85 | justify-content: flex-end; 86 | } 87 | -------------------------------------------------------------------------------- /assets/template-collection.css: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 749px) { 2 | .collection .grid__item:only-child { 3 | flex: 0 0 100%; 4 | max-width: 100%; 5 | } 6 | } 7 | 8 | @media screen and (max-width: 989px) { 9 | .collection .slider.slider--tablet { 10 | margin-bottom: 1.5rem; 11 | } 12 | } 13 | 14 | .collection .loading-overlay { 15 | top: 0; 16 | right: 0; 17 | bottom: 0; 18 | left: 0; 19 | display: none; 20 | width: 100%; 21 | padding: 0 1.5rem; 22 | opacity: 0.7; 23 | } 24 | 25 | @media screen and (min-width: 750px) { 26 | .collection .loading-overlay { 27 | padding-left: 5rem; 28 | padding-right: 5rem; 29 | } 30 | } 31 | 32 | .collection.loading .loading-overlay { 33 | display: block; 34 | } 35 | 36 | .collection--empty .title-wrapper { 37 | margin-top: 10rem; 38 | margin-bottom: 15rem; 39 | } 40 | 41 | @media screen and (max-width: 989px) { 42 | .collection .slider--tablet.product-grid { 43 | scroll-padding-left: 1.5rem; 44 | } 45 | } 46 | 47 | .collection__description > * { 48 | margin: 0; 49 | } 50 | 51 | .collection__title.title-wrapper { 52 | margin-bottom: 2.5rem; 53 | } 54 | 55 | .collection__title .title:not(:only-child) { 56 | margin-bottom: 1rem; 57 | } 58 | 59 | @media screen and (min-width: 990px) { 60 | .collection__title--desktop-slider .title { 61 | margin-bottom: 2.5rem; 62 | } 63 | 64 | .collection__title.title-wrapper--self-padded-tablet-down { 65 | padding: 0 5rem; 66 | } 67 | 68 | .collection slider-component:not(.page-width-desktop) { 69 | padding: 0; 70 | } 71 | 72 | .collection--full-width slider-component:not(.slider-component-desktop) { 73 | padding: 0 1.5rem; 74 | max-width: none; 75 | } 76 | } 77 | 78 | .collection__view-all a:not(.link) { 79 | margin-top: 1rem; 80 | } 81 | -------------------------------------------------------------------------------- /sections/main-page.liquid: -------------------------------------------------------------------------------- 1 | {{ 'section-main-page.css' | asset_url | stylesheet_tag }} 2 | 3 | {%- style -%} 4 | .section-{{ section.id }}-padding { 5 | padding-top: {{ section.settings.padding_top | times: 0.75 | round: 0 }}px; 6 | padding-bottom: {{ section.settings.padding_bottom | times: 0.75 | round: 0 }}px; 7 | } 8 | 9 | @media screen and (min-width: 750px) { 10 | .section-{{ section.id }}-padding { 11 | padding-top: {{ section.settings.padding_top }}px; 12 | padding-bottom: {{ section.settings.padding_bottom }}px; 13 | } 14 | } 15 | {%- endstyle -%} 16 | 17 |
18 |

19 | {{ page.title | escape }} 20 |

21 |
22 | {{ page.content }} 23 |
24 |
25 | 26 | {% schema %} 27 | { 28 | "name": "t:sections.main-page.name", 29 | "tag": "section", 30 | "class": "section", 31 | "settings": [ 32 | { 33 | "type": "header", 34 | "content": "t:sections.all.padding.section_padding_heading" 35 | }, 36 | { 37 | "type": "range", 38 | "id": "padding_top", 39 | "min": 0, 40 | "max": 100, 41 | "step": 4, 42 | "unit": "px", 43 | "label": "t:sections.all.padding.padding_top", 44 | "default": 36 45 | }, 46 | { 47 | "type": "range", 48 | "id": "padding_bottom", 49 | "min": 0, 50 | "max": 100, 51 | "step": 4, 52 | "unit": "px", 53 | "label": "t:sections.all.padding.padding_bottom", 54 | "default": 36 55 | } 56 | ] 57 | } 58 | {% endschema %} 59 | -------------------------------------------------------------------------------- /assets/details-disclosure.js: -------------------------------------------------------------------------------- 1 | class DetailsDisclosure extends HTMLElement { 2 | constructor() { 3 | super(); 4 | this.mainDetailsToggle = this.querySelector('details'); 5 | this.content = this.mainDetailsToggle.querySelector('summary').nextElementSibling; 6 | 7 | this.mainDetailsToggle.addEventListener('focusout', this.onFocusOut.bind(this)); 8 | this.mainDetailsToggle.addEventListener('toggle', this.onToggle.bind(this)); 9 | } 10 | 11 | onFocusOut() { 12 | setTimeout(() => { 13 | if (!this.contains(document.activeElement)) this.close(); 14 | }); 15 | } 16 | 17 | onToggle() { 18 | if (!this.animations) this.animations = this.content.getAnimations(); 19 | 20 | if (this.mainDetailsToggle.hasAttribute('open')) { 21 | this.animations.forEach((animation) => animation.play()); 22 | } else { 23 | this.animations.forEach((animation) => animation.cancel()); 24 | } 25 | } 26 | 27 | close() { 28 | this.mainDetailsToggle.removeAttribute('open'); 29 | this.mainDetailsToggle.querySelector('summary').setAttribute('aria-expanded', false); 30 | } 31 | } 32 | 33 | customElements.define('details-disclosure', DetailsDisclosure); 34 | 35 | class HeaderMenu extends DetailsDisclosure { 36 | constructor() { 37 | super(); 38 | this.header = document.querySelector('.header-wrapper'); 39 | } 40 | 41 | onToggle() { 42 | if (!this.header) return; 43 | this.header.preventHide = this.mainDetailsToggle.open; 44 | 45 | if (document.documentElement.style.getPropertyValue('--header-bottom-position-desktop') !== '') return; 46 | document.documentElement.style.setProperty( 47 | '--header-bottom-position-desktop', 48 | `${Math.floor(this.header.getBoundingClientRect().bottom)}px` 49 | ); 50 | } 51 | } 52 | 53 | customElements.define('header-menu', HeaderMenu); 54 | -------------------------------------------------------------------------------- /assets/section-featured-product.css: -------------------------------------------------------------------------------- 1 | .featured-product .product__media-list { 2 | width: 100%; 3 | margin: 0; 4 | padding-bottom: 0; 5 | } 6 | 7 | .featured-product .product-media-container { 8 | margin-bottom: var(--media-shadow-vertical-offset); 9 | max-width: 100%; 10 | } 11 | 12 | .featured-product .product__media-item { 13 | padding-left: 0; 14 | width: 100%; 15 | } 16 | 17 | .featured-product .product__media-item:not(:first-child) { 18 | display: none; 19 | } 20 | 21 | .featured-product .placeholder-svg { 22 | display: block; 23 | height: auto; 24 | width: 100%; 25 | } 26 | 27 | .background-secondary .featured-product { 28 | padding: 2.5rem; 29 | } 30 | 31 | .featured-product .share-button:nth-last-child(2) { 32 | display: inline-flex; 33 | } 34 | 35 | .share-button + .product__view-details { 36 | display: inline-flex; 37 | float: right; 38 | align-items: center; 39 | } 40 | 41 | .share-button + .product__view-details::after { 42 | content: ''; 43 | clear: both; 44 | display: table; 45 | } 46 | 47 | @media screen and (min-width: 750px) { 48 | .featured-product .product__media-item { 49 | padding-bottom: 0; 50 | } 51 | 52 | .background-secondary .featured-product { 53 | padding: 5rem; 54 | } 55 | } 56 | 57 | @media screen and (min-width: 990px) { 58 | .background-secondary .featured-product:not(.product--no-media) > .product__info-wrapper { 59 | padding: 0 0 0 5rem; 60 | } 61 | 62 | .background-secondary .featured-product:not(.product--no-media).product--right > .product__info-wrapper { 63 | padding: 0 5rem 0 0; 64 | } 65 | 66 | .featured-product:not(.product--no-media) > .product__info-wrapper { 67 | padding: 0 7rem; 68 | } 69 | 70 | .background-secondary .featured-product { 71 | padding: 6rem 7rem; 72 | position: relative; 73 | z-index: 1; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /snippets/meta-tags.liquid: -------------------------------------------------------------------------------- 1 | {%- liquid 2 | assign og_title = page_title | default: shop.name 3 | assign og_url = canonical_url | default: request.origin 4 | assign og_type = 'website' 5 | assign og_description = page_description | default: shop.description | default: shop.name 6 | 7 | if request.page_type == 'product' 8 | assign og_type = 'product' 9 | elsif request.page_type == 'article' 10 | assign og_type = 'article' 11 | elsif request.page_type == 'password' 12 | assign og_url = request.origin 13 | endif 14 | %} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {%- if page_image -%} 23 | 24 | 25 | 26 | 27 | {%- endif -%} 28 | 29 | {%- if request.page_type == 'product' -%} 30 | 31 | 32 | {%- endif -%} 33 | 34 | {%- if settings.social_twitter_link != blank -%} 35 | 36 | {%- endif -%} 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /assets/details-modal.js: -------------------------------------------------------------------------------- 1 | class DetailsModal extends HTMLElement { 2 | constructor() { 3 | super(); 4 | this.detailsContainer = this.querySelector('details'); 5 | this.summaryToggle = this.querySelector('summary'); 6 | 7 | this.detailsContainer.addEventListener('keyup', (event) => event.code.toUpperCase() === 'ESCAPE' && this.close()); 8 | this.summaryToggle.addEventListener('click', this.onSummaryClick.bind(this)); 9 | this.querySelector('button[type="button"]').addEventListener('click', this.close.bind(this)); 10 | 11 | this.summaryToggle.setAttribute('role', 'button'); 12 | } 13 | 14 | isOpen() { 15 | return this.detailsContainer.hasAttribute('open'); 16 | } 17 | 18 | onSummaryClick(event) { 19 | event.preventDefault(); 20 | event.target.closest('details').hasAttribute('open') ? this.close() : this.open(event); 21 | } 22 | 23 | onBodyClick(event) { 24 | if (!this.contains(event.target) || event.target.classList.contains('modal-overlay')) this.close(false); 25 | } 26 | 27 | open(event) { 28 | this.onBodyClickEvent = this.onBodyClickEvent || this.onBodyClick.bind(this); 29 | event.target.closest('details').setAttribute('open', true); 30 | document.body.addEventListener('click', this.onBodyClickEvent); 31 | document.body.classList.add('overflow-hidden'); 32 | 33 | trapFocus( 34 | this.detailsContainer.querySelector('[tabindex="-1"]'), 35 | this.detailsContainer.querySelector('input:not([type="hidden"])') 36 | ); 37 | } 38 | 39 | close(focusToggle = true) { 40 | removeTrapFocus(focusToggle ? this.summaryToggle : null); 41 | this.detailsContainer.removeAttribute('open'); 42 | document.body.removeEventListener('click', this.onBodyClickEvent); 43 | document.body.classList.remove('overflow-hidden'); 44 | } 45 | } 46 | 47 | customElements.define('details-modal', DetailsModal); 48 | -------------------------------------------------------------------------------- /snippets/country-localization.liquid: -------------------------------------------------------------------------------- 1 | {%- comment -%} 2 | Renders the country picker for the localization form 3 | 4 | Accepts: 5 | - localPosition: pass in the position in which the form is coming up to create specific IDs 6 | {%- endcomment -%} 7 | 8 |
9 | 22 | 44 |
45 | 46 | -------------------------------------------------------------------------------- /snippets/icon-snapchat.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /sections/custom-liquid.liquid: -------------------------------------------------------------------------------- 1 | {%- style -%} 2 | .section-{{ section.id }}-padding { 3 | padding-top: calc({{ section.settings.padding_top }}px * 0.75); 4 | padding-bottom: calc({{ section.settings.padding_bottom }}px * 0.75); 5 | } 6 | 7 | @media screen and (min-width: 750px) { 8 | .section-{{ section.id }}-padding { 9 | padding-top: {{ section.settings.padding_top }}px; 10 | padding-bottom: {{ section.settings.padding_bottom }}px; 11 | } 12 | } 13 | {%- endstyle -%} 14 |
15 |
16 | {{ section.settings.custom_liquid }} 17 |
18 |
19 | 20 | {% schema %} 21 | { 22 | "name": "t:sections.custom-liquid.name", 23 | "tag": "section", 24 | "class": "section", 25 | "settings": [ 26 | { 27 | "type": "liquid", 28 | "id": "custom_liquid", 29 | "label": "t:sections.custom-liquid.settings.custom_liquid.label", 30 | "info": "t:sections.custom-liquid.settings.custom_liquid.info" 31 | }, 32 | { 33 | "type": "color_scheme", 34 | "id": "color_scheme", 35 | "label": "t:sections.all.colors.label", 36 | "default": "background-1" 37 | }, 38 | { 39 | "type": "header", 40 | "content": "t:sections.all.padding.section_padding_heading" 41 | }, 42 | { 43 | "type": "range", 44 | "id": "padding_top", 45 | "min": 0, 46 | "max": 100, 47 | "step": 4, 48 | "unit": "px", 49 | "label": "t:sections.all.padding.padding_top", 50 | "default": 40 51 | }, 52 | { 53 | "type": "range", 54 | "id": "padding_bottom", 55 | "min": 0, 56 | "max": 100, 57 | "step": 4, 58 | "unit": "px", 59 | "label": "t:sections.all.padding.padding_bottom", 60 | "default": 52 61 | } 62 | ], 63 | "presets": [ 64 | { 65 | "name": "t:sections.custom-liquid.presets.name" 66 | } 67 | ] 68 | } 69 | {% endschema %} 70 | -------------------------------------------------------------------------------- /snippets/share-button.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders share button. 3 | Accepts: 4 | - block: {Object} passes in the block information. 5 | - share_link: {String} url to be added to the input the user will get/copy. 6 | 7 | Usage: 8 | {% render 'share-button', 9 | block: block, 10 | share_link: share_url 11 | %} 12 | {% endcomment %} 13 | 14 | 15 | 16 | 20 |
21 | 25 | 48 |
49 |
50 | -------------------------------------------------------------------------------- /assets/animations.js: -------------------------------------------------------------------------------- 1 | const SCROLL_ANIMATION_TRIGGER_CLASSNAME = 'scroll-trigger'; 2 | const SCROLL_ANIMATION_OFFSCREEN_CLASSNAME = 'scroll-trigger--offscreen'; 3 | const SCROLL_ANIMATION_CANCEL_CLASSNAME = 'scroll-trigger--cancel'; 4 | 5 | function onIntersection(elements, observer) { 6 | elements.forEach((element, index) => { 7 | if (element.isIntersecting) { 8 | const elementTarget = element.target; 9 | if (elementTarget.classList.contains(SCROLL_ANIMATION_OFFSCREEN_CLASSNAME)) { 10 | elementTarget.classList.remove(SCROLL_ANIMATION_OFFSCREEN_CLASSNAME); 11 | if (elementTarget.hasAttribute('data-cascade')) 12 | elementTarget.setAttribute('style', `--animation-order: ${index};`); 13 | } 14 | observer.unobserve(elementTarget); 15 | } else { 16 | element.target.classList.add(SCROLL_ANIMATION_OFFSCREEN_CLASSNAME); 17 | element.target.classList.remove(SCROLL_ANIMATION_CANCEL_CLASSNAME); 18 | } 19 | }); 20 | } 21 | 22 | function initializeScrollAnimationTrigger(rootEl = document, isDesignModeEvent = false) { 23 | const animationTriggerElements = Array.from(rootEl.getElementsByClassName(SCROLL_ANIMATION_TRIGGER_CLASSNAME)); 24 | if (animationTriggerElements.length === 0) return; 25 | 26 | if (isDesignModeEvent) { 27 | animationTriggerElements.forEach((element) => { 28 | element.classList.add('scroll-trigger--design-mode'); 29 | }); 30 | return; 31 | } 32 | 33 | const observer = new IntersectionObserver(onIntersection, { 34 | rootMargin: '0px 0px -50px 0px', 35 | }); 36 | animationTriggerElements.forEach((element) => observer.observe(element)); 37 | } 38 | 39 | window.addEventListener('DOMContentLoaded', () => initializeScrollAnimationTrigger()); 40 | 41 | if (Shopify.designMode) { 42 | document.addEventListener('shopify:section:load', (event) => initializeScrollAnimationTrigger(event.target, true)); 43 | document.addEventListener('shopify:section:reorder', () => initializeScrollAnimationTrigger(document, true)); 44 | } 45 | -------------------------------------------------------------------------------- /assets/theme-editor.js: -------------------------------------------------------------------------------- 1 | function hideProductModal() { 2 | const productModal = document.querySelectorAll('product-modal[open]'); 3 | productModal && productModal.forEach((modal) => modal.hide()); 4 | } 5 | 6 | document.addEventListener('shopify:block:select', function (event) { 7 | hideProductModal(); 8 | const blockSelectedIsSlide = event.target.classList.contains('slideshow__slide'); 9 | if (!blockSelectedIsSlide) return; 10 | 11 | const parentSlideshowComponent = event.target.closest('slideshow-component'); 12 | parentSlideshowComponent.pause(); 13 | 14 | setTimeout(function () { 15 | parentSlideshowComponent.slider.scrollTo({ 16 | left: event.target.offsetLeft, 17 | }); 18 | }, 200); 19 | }); 20 | 21 | document.addEventListener('shopify:block:deselect', function (event) { 22 | const blockDeselectedIsSlide = event.target.classList.contains('slideshow__slide'); 23 | if (!blockDeselectedIsSlide) return; 24 | const parentSlideshowComponent = event.target.closest('slideshow-component'); 25 | if (parentSlideshowComponent.autoplayButtonIsSetToPlay) parentSlideshowComponent.play(); 26 | }); 27 | 28 | document.addEventListener('shopify:section:load', () => { 29 | hideProductModal(); 30 | const zoomOnHoverScript = document.querySelector('[id^=EnableZoomOnHover]'); 31 | if (!zoomOnHoverScript) return; 32 | if (zoomOnHoverScript) { 33 | const newScriptTag = document.createElement('script'); 34 | newScriptTag.src = zoomOnHoverScript.src; 35 | zoomOnHoverScript.parentNode.replaceChild(newScriptTag, zoomOnHoverScript); 36 | } 37 | }); 38 | 39 | document.addEventListener('shopify:section:reorder', () => hideProductModal()); 40 | 41 | document.addEventListener('shopify:section:select', () => hideProductModal()); 42 | 43 | document.addEventListener('shopify:section:deselect', () => hideProductModal()); 44 | 45 | document.addEventListener('shopify:inspector:activate', () => hideProductModal()); 46 | 47 | document.addEventListener('shopify:inspector:deactivate', () => hideProductModal()); 48 | -------------------------------------------------------------------------------- /assets/section-featured-blog.css: -------------------------------------------------------------------------------- 1 | .blog-placeholder { 2 | margin: 0 1.5rem; 3 | background: rgb(var(--color-background)); 4 | } 5 | 6 | @media screen and (min-width: 750px) { 7 | .blog-placeholder { 8 | text-align: center; 9 | width: 50%; 10 | margin: 0; 11 | } 12 | } 13 | 14 | .blog-placeholder__content { 15 | padding: 3rem; 16 | background: rgba(var(--color-foreground), 0.04); 17 | } 18 | 19 | .blog-placeholder .placeholder { 20 | position: relative; 21 | } 22 | 23 | .blog-placeholder .placeholder .placeholder-svg { 24 | height: auto; 25 | } 26 | 27 | .blog-placeholder h2 { 28 | margin: 0; 29 | } 30 | 31 | .blog-placeholder .rte-width { 32 | margin-top: 1.2rem; 33 | color: rgba(var(--color-foreground), 0.75); 34 | } 35 | 36 | @media screen and (min-width: 990px) { 37 | .grid--1-col-desktop .article-card .card__content { 38 | text-align: center; 39 | } 40 | } 41 | 42 | .blog__title { 43 | margin: 0; 44 | } 45 | 46 | .blog__posts.articles-wrapper { 47 | margin-bottom: 1rem; 48 | } 49 | 50 | @media screen and (min-width: 990px) { 51 | .blog__posts.articles-wrapper { 52 | margin-bottom: 0; 53 | } 54 | } 55 | 56 | .blog__posts.articles-wrapper .article { 57 | scroll-snap-align: start; 58 | } 59 | 60 | @media screen and (max-width: 749px) { 61 | .blog__post.article { 62 | width: calc(100% - 3rem - var(--grid-mobile-horizontal-spacing)); 63 | } 64 | } 65 | 66 | .background-secondary .blog-placeholder__content { 67 | background-color: rgb(var(--color-background)); 68 | } 69 | 70 | .blog__posts .card-wrapper { 71 | width: 100%; 72 | } 73 | 74 | .blog__button { 75 | margin-top: 3rem; 76 | } 77 | 78 | @media screen and (min-width: 750px) { 79 | .blog__button { 80 | margin-top: 5rem; 81 | } 82 | } 83 | 84 | /* check for flexbox gap in older Safari versions */ 85 | @supports not (inset: 10px) { 86 | @media screen and (min-width: 750px) { 87 | .blog__posts .article + .article { 88 | margin-left: var(--grid-desktop-horizontal-spacing); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /snippets/product-media-modal.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders a product media modal. Also see 'product-media-gallery' 3 | 4 | Accepts: 5 | - product: {Object} Product liquid object 6 | - variant_images: {Array} Product images associated with a variant 7 | 8 | Usage: 9 | {% render 'product-media-modal' %} 10 | {% endcomment %} 11 | 12 | 13 | 57 | 58 | -------------------------------------------------------------------------------- /assets/component-price.css: -------------------------------------------------------------------------------- 1 | .price { 2 | font-size: 1.6rem; 3 | letter-spacing: 0.1rem; 4 | line-height: calc(1 + 0.5 / var(--font-body-scale)); 5 | color: rgb(var(--color-foreground)); 6 | } 7 | 8 | .price > * { 9 | display: inline-block; 10 | vertical-align: top; 11 | } 12 | 13 | .price.price--unavailable { 14 | visibility: hidden; 15 | } 16 | 17 | .price--end { 18 | text-align: right; 19 | } 20 | 21 | .price .price-item { 22 | display: inline-block; 23 | margin: 0 1rem 0 0; 24 | } 25 | 26 | .price__regular .price-item--regular { 27 | margin-right: 0; 28 | } 29 | 30 | .price:not(.price--show-badge) .price-item--last:last-of-type { 31 | margin: 0; 32 | } 33 | 34 | @media screen and (min-width: 750px) { 35 | .price { 36 | margin-bottom: 0; 37 | } 38 | } 39 | 40 | .price--large { 41 | font-size: 1.6rem; 42 | line-height: calc(1 + 0.5 / var(--font-body-scale)); 43 | letter-spacing: 0.13rem; 44 | } 45 | 46 | @media screen and (min-width: 750px) { 47 | .price--large { 48 | font-size: 1.8rem; 49 | } 50 | } 51 | 52 | .price--sold-out .price__availability, 53 | .price__regular { 54 | display: block; 55 | } 56 | 57 | .price__sale, 58 | .price__availability, 59 | .price .price__badge-sale, 60 | .price .price__badge-sold-out, 61 | .price--on-sale .price__regular, 62 | .price--on-sale .price__availability { 63 | display: none; 64 | } 65 | 66 | .price--sold-out .price__badge-sold-out, 67 | .price--on-sale .price__badge-sale { 68 | display: inline-block; 69 | } 70 | 71 | .price--on-sale .price__sale { 72 | display: initial; 73 | flex-direction: row; 74 | flex-wrap: wrap; 75 | } 76 | 77 | .price--center { 78 | display: initial; 79 | justify-content: center; 80 | } 81 | 82 | .price--on-sale .price-item--regular { 83 | text-decoration: line-through; 84 | color: rgba(var(--color-foreground), 0.75); 85 | font-size: 1.3rem; 86 | } 87 | 88 | .unit-price { 89 | display: block; 90 | font-size: 1.1rem; 91 | letter-spacing: 0.04rem; 92 | line-height: calc(1 + 0.2 / var(--font-body-scale)); 93 | margin-top: 0.2rem; 94 | text-transform: uppercase; 95 | color: rgba(var(--color-foreground), 0.7); 96 | } 97 | -------------------------------------------------------------------------------- /assets/component-mega-menu.css: -------------------------------------------------------------------------------- 1 | .mega-menu { 2 | position: static; 3 | } 4 | 5 | .mega-menu__content { 6 | background-color: rgb(var(--color-background)); 7 | border-left: 0; 8 | border-radius: 0; 9 | border-right: 0; 10 | left: 0; 11 | overflow-y: auto; 12 | padding-bottom: 2.4rem; 13 | padding-top: 2.4rem; 14 | position: absolute; 15 | right: 0; 16 | top: 100%; 17 | } 18 | 19 | .shopify-section-header-sticky .mega-menu__content { 20 | max-height: calc(100vh - var(--header-bottom-position-desktop, 20rem) - 4rem); 21 | } 22 | 23 | .header-wrapper--border-bottom .mega-menu__content { 24 | border-top: 0; 25 | } 26 | 27 | .js .mega-menu__content { 28 | opacity: 0; 29 | transform: translateY(-1.5rem); 30 | } 31 | 32 | .mega-menu[open] .mega-menu__content { 33 | opacity: 1; 34 | transform: translateY(0); 35 | } 36 | 37 | .mega-menu__list { 38 | display: grid; 39 | gap: 2.4rem 4rem; 40 | grid-template-columns: repeat(6, minmax(0, 1fr)); 41 | list-style: none; 42 | } 43 | 44 | .mega-menu__link { 45 | color: rgba(var(--color-foreground), 0.75); 46 | display: block; 47 | font-size: 1.3rem; 48 | line-height: calc(1 + 0.3 / var(--font-body-scale)); 49 | padding-bottom: 0.6rem; 50 | padding-top: 0.6rem; 51 | text-decoration: none; 52 | transition: text-decoration var(--duration-short) ease; 53 | word-wrap: break-word; 54 | } 55 | 56 | .mega-menu__link--level-2 { 57 | font-size: 1.4rem; 58 | } 59 | 60 | .mega-menu__link--level-2:not(:only-child) { 61 | margin-bottom: 0.8rem; 62 | } 63 | 64 | .header--top-center .mega-menu__list { 65 | display: flex; 66 | justify-content: center; 67 | flex-wrap: wrap; 68 | column-gap: 0; 69 | } 70 | 71 | .header--top-center .mega-menu__list > li { 72 | width: 16%; 73 | padding-right: 2.4rem; 74 | } 75 | 76 | .mega-menu__link:hover, 77 | .mega-menu__link--active { 78 | color: rgb(var(--color-foreground)); 79 | text-decoration: underline; 80 | } 81 | 82 | .mega-menu__link--active:hover { 83 | text-decoration-thickness: 0.2rem; 84 | } 85 | 86 | .mega-menu .mega-menu__list--condensed { 87 | display: block; 88 | } 89 | 90 | .mega-menu__list--condensed .mega-menu__link { 91 | font-weight: normal; 92 | } 93 | -------------------------------------------------------------------------------- /assets/localization-form.js: -------------------------------------------------------------------------------- 1 | if (!customElements.get('localization-form')) { 2 | customElements.define( 3 | 'localization-form', 4 | class LocalizationForm extends HTMLElement { 5 | constructor() { 6 | super(); 7 | this.elements = { 8 | input: this.querySelector('input[name="locale_code"], input[name="country_code"]'), 9 | button: this.querySelector('button'), 10 | panel: this.querySelector('.disclosure__list-wrapper'), 11 | }; 12 | this.elements.button.addEventListener('click', this.openSelector.bind(this)); 13 | this.elements.button.addEventListener('focusout', this.closeSelector.bind(this)); 14 | this.addEventListener('keyup', this.onContainerKeyUp.bind(this)); 15 | 16 | this.querySelectorAll('a').forEach((item) => item.addEventListener('click', this.onItemClick.bind(this))); 17 | } 18 | 19 | hidePanel() { 20 | this.elements.button.setAttribute('aria-expanded', 'false'); 21 | this.elements.panel.setAttribute('hidden', true); 22 | } 23 | 24 | onContainerKeyUp(event) { 25 | if (event.code.toUpperCase() !== 'ESCAPE') return; 26 | 27 | if(this.elements.button.getAttribute('aria-expanded') == 'false') return; 28 | this.hidePanel(); 29 | event.stopPropagation(); 30 | this.elements.button.focus(); 31 | } 32 | 33 | onItemClick(event) { 34 | event.preventDefault(); 35 | const form = this.querySelector('form'); 36 | this.elements.input.value = event.currentTarget.dataset.value; 37 | if (form) form.submit(); 38 | } 39 | 40 | openSelector() { 41 | this.elements.button.focus(); 42 | this.elements.panel.toggleAttribute('hidden'); 43 | this.elements.button.setAttribute( 44 | 'aria-expanded', 45 | (this.elements.button.getAttribute('aria-expanded') === 'false').toString() 46 | ); 47 | } 48 | 49 | closeSelector(event) { 50 | const isChild = 51 | this.elements.panel.contains(event.relatedTarget) || this.elements.button.contains(event.relatedTarget); 52 | if (!event.relatedTarget || !isChild) { 53 | this.hidePanel(); 54 | } 55 | } 56 | } 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /assets/slick-theme.css: -------------------------------------------------------------------------------- 1 | @charset 'UTF-8';.slick-loading .slick-list{background:#fff url(ajax-loader.gif) center center no-repeat}@font-face{font-family:slick;font-weight:400;font-style:normal;src:url(fonts/slick.eot);src:url(fonts/slick.eot?#iefix) format('embedded-opentype'),url(fonts/slick.woff) format('woff'),url(fonts/slick.ttf) format('truetype'),url(fonts/slick.svg#slick) format('svg')}.slick-next,.slick-prev{font-size:0;line-height:0;position:absolute;top:50%;display:block;width:20px;height:20px;padding:0;-webkit-transform:translate(0,-50%);-ms-transform:translate(0,-50%);transform:translate(0,-50%);cursor:pointer;color:transparent;border:none;outline:0;background:0 0}.slick-next:focus,.slick-next:hover,.slick-prev:focus,.slick-prev:hover{color:transparent;outline:0;background:0 0}.slick-next:focus:before,.slick-next:hover:before,.slick-prev:focus:before,.slick-prev:hover:before{opacity:1}.slick-next.slick-disabled:before,.slick-prev.slick-disabled:before{opacity:.25}.slick-next:before,.slick-prev:before{font-family:slick;font-size:20px;line-height:1;opacity:.75;color:#fff;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.slick-prev{left:-25px}[dir=rtl] .slick-prev{right:-25px;left:auto}.slick-prev:before{content:'←'}[dir=rtl] .slick-prev:before{content:'→'}.slick-next{right:-25px}[dir=rtl] .slick-next{right:auto;left:-25px}.slick-next:before{content:'→'}[dir=rtl] .slick-next:before{content:'←'}.slick-dotted.slick-slider{margin-bottom:30px}.slick-dots{position:absolute;bottom:-25px;display:block;width:100%;padding:0;margin:0;list-style:none;text-align:center}.slick-dots li{position:relative;display:inline-block;width:20px;height:20px;margin:0 5px;padding:0;cursor:pointer}.slick-dots li button{font-size:0;line-height:0;display:block;width:20px;height:20px;padding:5px;cursor:pointer;color:transparent;border:0;outline:0;background:0 0}.slick-dots li button:focus,.slick-dots li button:hover{outline:0}.slick-dots li button:focus:before,.slick-dots li button:hover:before{opacity:1}.slick-dots li button:before{font-family:slick;font-size:6px;line-height:20px;position:absolute;top:0;left:0;width:20px;height:20px;content:'•';text-align:center;opacity:.25;color:#000;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.slick-dots li.slick-active button:before{opacity:.75;color:#000} 2 | /*# sourceMappingURL=slick-theme.min.css.map */ 3 | -------------------------------------------------------------------------------- /sections/cart-notification-product.liquid: -------------------------------------------------------------------------------- 1 | {%- if cart != empty -%} 2 | {%- for item in cart.items -%} 3 |
4 | {%- if item.image -%} 5 |
6 | {{ item.image.alt | escape }} 13 |
14 | {%- endif -%} 15 |
16 | {%- if settings.show_vendor -%} 17 |

{{ item.product.vendor }}

18 | {%- endif -%} 19 |

{{ item.product.title | escape }}

20 |
21 | {%- unless item.product.has_only_default_variant -%} 22 | {%- for option in item.options_with_values -%} 23 |
24 |
{{ option.name }}:
25 |
{{ option.value }}
26 |
27 | {%- endfor -%} 28 | {%- endunless -%} 29 | {%- for property in item.properties -%} 30 | {%- assign property_first_char = property.first | slice: 0 -%} 31 | {%- if property.last != blank and property_first_char != '_' -%} 32 |
33 |
{{ property.first }}:
34 |
35 | {%- if property.last contains '/uploads/' -%} 36 | 37 | {{ property.last | split: '/' | last }} 38 | 39 | {%- else -%} 40 | {{ property.last }} 41 | {%- endif -%} 42 |
43 |
44 | {%- endif -%} 45 | {%- endfor -%} 46 |
47 | {%- if item.selling_plan_allocation != null -%} 48 |

{{ item.selling_plan_allocation.selling_plan.name }}

49 | {%- endif -%} 50 |
51 |
52 | {%- endfor -%} 53 | {%- endif -%} 54 | -------------------------------------------------------------------------------- /snippets/cart-notification.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders cart notification 3 | 4 | Accepts: 5 | - color_scheme: {String} sets the color scheme of the notification (optional) 6 | - desktop_menu_type: {String} passes the desktop menu type which allows us to use the right css class (optional) 7 | 8 | Usage: 9 | {% render 'cart-notification' %} 10 | {% endcomment %} 11 | 12 | 13 |
14 | 52 |
53 |
54 | {% style %} 55 | .cart-notification { 56 | display: none; 57 | } 58 | {% endstyle %} 59 | -------------------------------------------------------------------------------- /templates/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "image_banner": { 4 | "type": "image-banner", 5 | "blocks": { 6 | "heading": { 7 | "type": "heading", 8 | "settings": { 9 | "heading": "Browse our latest products", 10 | "heading_size": "h0" 11 | } 12 | }, 13 | "button": { 14 | "type": "buttons", 15 | "settings": { 16 | "button_label_1": "Shop all", 17 | "button_link_1": "shopify:\/\/collections\/all", 18 | "button_style_secondary_1": true, 19 | "button_label_2": "", 20 | "button_link_2": "", 21 | "button_style_secondary_2": false 22 | } 23 | } 24 | }, 25 | "block_order": [ 26 | "heading", 27 | "button" 28 | ], 29 | "settings": { 30 | "image_overlay_opacity": 40, 31 | "image_height": "large", 32 | "desktop_content_position": "bottom-center", 33 | "show_text_box": false, 34 | "desktop_content_alignment": "center", 35 | "color_scheme": "inverse", 36 | "image_behavior": "none", 37 | "mobile_content_alignment": "center", 38 | "stack_images_on_mobile": false, 39 | "show_text_below": false 40 | } 41 | }, 42 | "featured_collection": { 43 | "type": "featured-collection", 44 | "settings": { 45 | "title": "Featured products", 46 | "heading_size": "h2", 47 | "description": "", 48 | "show_description": false, 49 | "description_style": "body", 50 | "collection": "all", 51 | "products_to_show": 8, 52 | "columns_desktop": 4, 53 | "full_width": false, 54 | "show_view_all": true, 55 | "view_all_style": "solid", 56 | "enable_desktop_slider": false, 57 | "color_scheme": "background-1", 58 | "image_ratio": "adapt", 59 | "image_shape": "default", 60 | "show_secondary_image": true, 61 | "show_vendor": false, 62 | "show_rating": false, 63 | "enable_quick_add": false, 64 | "columns_mobile": "2", 65 | "swipe_on_mobile": false, 66 | "padding_top": 44, 67 | "padding_bottom": 36 68 | } 69 | } 70 | }, 71 | "order": [ 72 | "image_banner", 73 | "featured_collection" 74 | ] 75 | } -------------------------------------------------------------------------------- /assets/component-modal-video.css: -------------------------------------------------------------------------------- 1 | .modal-video { 2 | background: rgba(var(--color-foreground), 0.2); 3 | box-sizing: border-box; 4 | height: 100%; 5 | left: 0; 6 | margin: 0 auto; 7 | opacity: 0; 8 | overflow: auto; 9 | position: fixed; 10 | top: 0; 11 | visibility: hidden; 12 | width: 100%; 13 | z-index: -1; 14 | } 15 | 16 | .modal-video[open] { 17 | opacity: 1; 18 | visibility: visible; 19 | z-index: 101; 20 | } 21 | 22 | .modal-video__content { 23 | background-color: rgb(var(--color-background)); 24 | height: 100%; 25 | margin: 0; 26 | overflow: auto; 27 | padding: 0; 28 | position: absolute; 29 | width: 100%; 30 | } 31 | 32 | .modal-video__toggle { 33 | align-items: center; 34 | background-color: rgb(var(--color-background)); 35 | border-radius: 50%; 36 | border: 0.1rem solid rgba(var(--color-foreground), 0.1); 37 | color: rgba(var(--color-foreground), 0.55); 38 | cursor: pointer; 39 | display: flex; 40 | justify-content: center; 41 | margin: 0 0 0 auto; 42 | padding: 1.2rem; 43 | position: fixed; 44 | right: 0.5rem; 45 | top: 2rem; 46 | width: 4rem; 47 | z-index: 2; 48 | } 49 | 50 | @media screen and (min-width: 750px) { 51 | .modal-video__toggle { 52 | right: 4.8rem; 53 | top: 3.5rem; 54 | } 55 | } 56 | 57 | @media screen and (min-width: 990px) { 58 | .modal-video__toggle { 59 | right: 4.3rem; 60 | top: 3rem; 61 | } 62 | } 63 | 64 | .modal-video__toggle .icon { 65 | height: auto; 66 | margin: 0; 67 | width: 2.2rem; 68 | } 69 | 70 | .modal-video__content-info { 71 | height: calc(100% - 6rem); 72 | margin: 0 auto; 73 | padding-top: 8rem; 74 | width: calc(100% - 1rem); 75 | } 76 | 77 | @media screen and (min-width: 750px) { 78 | .modal-video__content-info { 79 | height: calc(100% - 7.5rem); 80 | padding-top: 9.5rem; 81 | width: calc(100% - 9.6rem); 82 | } 83 | } 84 | 85 | @media screen and (min-width: 990px) { 86 | .modal-video__content-info { 87 | height: calc(100% - 7rem); 88 | padding-top: 9rem; 89 | width: calc(100% - 8.6rem); 90 | } 91 | } 92 | 93 | .modal-video__video, 94 | .modal-video__video iframe { 95 | height: 100%; 96 | width: 100%; 97 | } 98 | 99 | .modal-video__video iframe { 100 | position: static; 101 | border: 0; 102 | } 103 | -------------------------------------------------------------------------------- /assets/share.js: -------------------------------------------------------------------------------- 1 | if (!customElements.get('share-button')) { 2 | customElements.define( 3 | 'share-button', 4 | class ShareButton extends DetailsDisclosure { 5 | constructor() { 6 | super(); 7 | 8 | this.elements = { 9 | shareButton: this.querySelector('button'), 10 | shareSummary: this.querySelector('summary'), 11 | closeButton: this.querySelector('.share-button__close'), 12 | successMessage: this.querySelector('[id^="ShareMessage"]'), 13 | urlInput: this.querySelector('input'), 14 | }; 15 | this.urlToShare = this.elements.urlInput ? this.elements.urlInput.value : document.location.href; 16 | 17 | if (navigator.share) { 18 | this.mainDetailsToggle.setAttribute('hidden', ''); 19 | this.elements.shareButton.classList.remove('hidden'); 20 | this.elements.shareButton.addEventListener('click', () => { 21 | navigator.share({ url: this.urlToShare, title: document.title }); 22 | }); 23 | } else { 24 | this.mainDetailsToggle.addEventListener('toggle', this.toggleDetails.bind(this)); 25 | this.mainDetailsToggle 26 | .querySelector('.share-button__copy') 27 | .addEventListener('click', this.copyToClipboard.bind(this)); 28 | this.mainDetailsToggle.querySelector('.share-button__close').addEventListener('click', this.close.bind(this)); 29 | } 30 | } 31 | 32 | toggleDetails() { 33 | if (!this.mainDetailsToggle.open) { 34 | this.elements.successMessage.classList.add('hidden'); 35 | this.elements.successMessage.textContent = ''; 36 | this.elements.closeButton.classList.add('hidden'); 37 | this.elements.shareSummary.focus(); 38 | } 39 | } 40 | 41 | copyToClipboard() { 42 | navigator.clipboard.writeText(this.elements.urlInput.value).then(() => { 43 | this.elements.successMessage.classList.remove('hidden'); 44 | this.elements.successMessage.textContent = window.accessibilityStrings.shareSuccess; 45 | this.elements.closeButton.classList.remove('hidden'); 46 | this.elements.closeButton.focus(); 47 | }); 48 | } 49 | 50 | updateUrl(url) { 51 | this.urlToShare = url; 52 | this.elements.urlInput.value = url; 53 | } 54 | } 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /assets/magnify.js: -------------------------------------------------------------------------------- 1 | // create a container and set the full-size image as its background 2 | function createOverlay(image) { 3 | const overlayImage = document.createElement('img'); 4 | overlayImage.setAttribute('src', `${image.src}`); 5 | overlay = document.createElement('div'); 6 | prepareOverlay(overlay, overlayImage); 7 | 8 | image.style.opacity = '50%'; 9 | toggleLoadingSpinner(image); 10 | 11 | overlayImage.onload = () => { 12 | toggleLoadingSpinner(image); 13 | image.parentElement.insertBefore(overlay, image); 14 | image.style.opacity = '100%'; 15 | }; 16 | 17 | return overlay; 18 | } 19 | 20 | function prepareOverlay(container, image) { 21 | container.setAttribute('class', 'image-magnify-full-size'); 22 | container.setAttribute('aria-hidden', 'true'); 23 | container.style.backgroundImage = `url('${image.src}')`; 24 | container.style.backgroundColor = 'var(--gradient-background)'; 25 | } 26 | 27 | function toggleLoadingSpinner(image) { 28 | const loadingSpinner = image.parentElement.parentElement.querySelector(`.loading-overlay__spinner`); 29 | loadingSpinner.classList.toggle('hidden'); 30 | } 31 | 32 | function moveWithHover(image, event, zoomRatio) { 33 | // calculate mouse position 34 | const ratio = image.height / image.width; 35 | const container = event.target.getBoundingClientRect(); 36 | const xPosition = event.clientX - container.left; 37 | const yPosition = event.clientY - container.top; 38 | const xPercent = `${xPosition / (image.clientWidth / 100)}%`; 39 | const yPercent = `${yPosition / ((image.clientWidth * ratio) / 100)}%`; 40 | 41 | // determine what to show in the frame 42 | overlay.style.backgroundPosition = `${xPercent} ${yPercent}`; 43 | overlay.style.backgroundSize = `${image.width * zoomRatio}px`; 44 | } 45 | 46 | function magnify(image, zoomRatio) { 47 | const overlay = createOverlay(image); 48 | overlay.onclick = () => overlay.remove(); 49 | overlay.onmousemove = (event) => moveWithHover(image, event, zoomRatio); 50 | overlay.onmouseleave = () => overlay.remove(); 51 | } 52 | 53 | function enableZoomOnHover(zoomRatio) { 54 | const images = document.querySelectorAll('.image-magnify-hover'); 55 | images.forEach((image) => { 56 | image.onclick = (event) => { 57 | magnify(image, zoomRatio); 58 | moveWithHover(image, event, zoomRatio); 59 | }; 60 | }); 61 | } 62 | 63 | enableZoomOnHover(2); 64 | -------------------------------------------------------------------------------- /assets/section-main-blog.css: -------------------------------------------------------------------------------- 1 | .blog-articles { 2 | display: grid; 3 | grid-gap: 1rem; 4 | column-gap: var(--grid-mobile-horizontal-spacing); 5 | row-gap: var(--grid-mobile-vertical-spacing); 6 | } 7 | 8 | .blog-articles .card-wrapper { 9 | width: 100%; 10 | } 11 | 12 | @media screen and (min-width: 750px) { 13 | .blog-articles { 14 | grid-template-columns: 1fr 1fr; 15 | column-gap: var(--grid-desktop-horizontal-spacing); 16 | row-gap: var(--grid-desktop-vertical-spacing); 17 | } 18 | 19 | .blog-articles--collage > *:nth-child(3n + 1), 20 | .blog-articles--collage > *:nth-child(3n + 2):last-child { 21 | grid-column: span 2; 22 | text-align: center; 23 | } 24 | 25 | .blog-articles--collage > *:nth-child(3n + 1) .card, 26 | .blog-articles--collage > *:nth-child(3n + 2):last-child .card { 27 | text-align: center; 28 | } 29 | 30 | .blog-articles--collage > *:nth-child(3n + 1) .article-card__image--small .ratio::before, 31 | .blog-articles--collage > *:nth-child(3n + 2):last-child .article-card__image--small .ratio::before { 32 | padding-bottom: 22rem; 33 | } 34 | 35 | .blog-articles--collage > *:nth-child(3n + 1) .article-card__image--medium .ratio::before, 36 | .blog-articles--collage > *:nth-child(3n + 2):last-child .article-card__image--medium .ratio::before { 37 | padding-bottom: 44rem; 38 | } 39 | 40 | .blog-articles--collage > *:nth-child(3n + 1) .article-card__image--large .ratio::before, 41 | .blog-articles--collage > *:nth-child(3n + 2):last-child .article-card__image--large .ratio::before { 42 | padding-bottom: 66rem; 43 | } 44 | } 45 | 46 | @media screen and (min-width: 990px) { 47 | .blog-articles--collage > *:nth-child(3n + 1) .article-card__image--small .ratio .ratio::before, 48 | .blog-articles--collage > *:nth-child(3n + 2):last-child .article-card__image--small .ratio .ratio::before { 49 | padding-bottom: 27.5rem; 50 | } 51 | 52 | .blog-articles--collage > *:nth-child(3n + 1) .article-card__image--medium .ratio::before, 53 | .blog-articles--collage > *:nth-child(3n + 2):last-child .article-card__image--medium .ratio::before { 54 | padding-bottom: 55rem; 55 | } 56 | 57 | .blog-articles--collage > *:nth-child(3n + 1) .article-card__image--large .ratio::before, 58 | .blog-articles--collage > *:nth-child(3n + 2):last-child .article-card__image--large .ratio::before { 59 | padding-bottom: 82.5rem; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /assets/slick.css: -------------------------------------------------------------------------------- 1 | /* Slider */ 2 | .slick-slider 3 | { 4 | position: relative; 5 | 6 | display: block; 7 | box-sizing: border-box; 8 | 9 | -webkit-user-select: none; 10 | -moz-user-select: none; 11 | -ms-user-select: none; 12 | user-select: none; 13 | 14 | -webkit-touch-callout: none; 15 | -khtml-user-select: none; 16 | -ms-touch-action: pan-y; 17 | touch-action: pan-y; 18 | -webkit-tap-highlight-color: transparent; 19 | } 20 | 21 | .slick-list 22 | { 23 | position: relative; 24 | 25 | display: block; 26 | overflow: hidden; 27 | 28 | margin: 0; 29 | padding: 0; 30 | } 31 | .slick-list:focus 32 | { 33 | outline: none; 34 | } 35 | .slick-list.dragging 36 | { 37 | cursor: pointer; 38 | cursor: hand; 39 | } 40 | 41 | .slick-slider .slick-track, 42 | .slick-slider .slick-list 43 | { 44 | -webkit-transform: translate3d(0, 0, 0); 45 | -moz-transform: translate3d(0, 0, 0); 46 | -ms-transform: translate3d(0, 0, 0); 47 | -o-transform: translate3d(0, 0, 0); 48 | transform: translate3d(0, 0, 0); 49 | } 50 | 51 | .slick-track 52 | { 53 | position: relative; 54 | top: 0; 55 | left: 0; 56 | 57 | display: block; 58 | margin-left: auto; 59 | margin-right: auto; 60 | } 61 | .slick-track:before, 62 | .slick-track:after 63 | { 64 | display: table; 65 | 66 | content: ''; 67 | } 68 | .slick-track:after 69 | { 70 | clear: both; 71 | } 72 | .slick-loading .slick-track 73 | { 74 | visibility: hidden; 75 | } 76 | 77 | .slick-slide 78 | { 79 | display: none; 80 | float: left; 81 | 82 | height: 100%; 83 | min-height: 1px; 84 | } 85 | [dir='rtl'] .slick-slide 86 | { 87 | float: right; 88 | } 89 | .slick-slide img 90 | { 91 | display: block; 92 | } 93 | .slick-slide.slick-loading img 94 | { 95 | display: none; 96 | } 97 | .slick-slide.dragging img 98 | { 99 | pointer-events: none; 100 | } 101 | .slick-initialized .slick-slide 102 | { 103 | display: block; 104 | } 105 | .slick-loading .slick-slide 106 | { 107 | visibility: hidden; 108 | } 109 | .slick-vertical .slick-slide 110 | { 111 | display: block; 112 | 113 | height: auto; 114 | 115 | border: 1px solid transparent; 116 | } 117 | .slick-arrow.slick-hidden { 118 | display: none; 119 | } 120 | -------------------------------------------------------------------------------- /assets/component-collection-hero.css: -------------------------------------------------------------------------------- 1 | .collection-hero__inner { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .collection-hero--with-image .collection-hero__inner { 7 | margin-bottom: 0; 8 | padding-bottom: 2rem; 9 | } 10 | 11 | @media screen and (min-width: 750px) { 12 | .collection-hero.collection-hero--with-image { 13 | padding: calc(4rem + var(--page-width-margin)) 0 calc(4rem + var(--page-width-margin)); 14 | overflow: hidden; 15 | } 16 | 17 | .collection-hero--with-image .collection-hero__inner { 18 | padding-bottom: 0; 19 | } 20 | } 21 | 22 | .collection-hero__text-wrapper { 23 | flex-basis: 100%; 24 | } 25 | 26 | @media screen and (min-width: 750px) { 27 | .collection-hero { 28 | padding: 0; 29 | } 30 | 31 | .collection-hero__inner { 32 | align-items: center; 33 | flex-direction: row; 34 | padding-bottom: 0; 35 | } 36 | } 37 | 38 | .collection-hero__title { 39 | margin: 2.5rem 0; 40 | } 41 | 42 | .collection-hero__title + .collection-hero__description { 43 | margin-top: 1.5rem; 44 | margin-bottom: 1.5rem; 45 | font-size: 1.6rem; 46 | line-height: calc(1 + 0.5 / var(--font-body-scale)); 47 | } 48 | 49 | @media screen and (min-width: 750px) { 50 | .collection-hero__title + .collection-hero__description { 51 | font-size: 1.8rem; 52 | margin-top: 2rem; 53 | margin-bottom: 2rem; 54 | } 55 | 56 | .collection-hero__description { 57 | max-width: 66.67%; 58 | } 59 | 60 | .collection-hero--with-image .collection-hero__description { 61 | max-width: 100%; 62 | } 63 | } 64 | 65 | .collection-hero--with-image .collection-hero__title { 66 | margin: 0; 67 | } 68 | 69 | .collection-hero--with-image .collection-hero__text-wrapper { 70 | padding: 5rem 0 4rem; 71 | } 72 | 73 | .collection-hero__image-container { 74 | border: var(--media-border-width) solid rgba(var(--color-foreground), var(--media-border-opacity)); 75 | border-radius: var(--media-radius); 76 | box-shadow: var(--media-shadow-horizontal-offset) var(--media-shadow-vertical-offset) var(--media-shadow-blur-radius) 77 | rgba(var(--color-shadow), var(--media-shadow-opacity)); 78 | } 79 | 80 | @media screen and (max-width: 749px) { 81 | .collection-hero__image-container { 82 | height: 20rem; 83 | } 84 | } 85 | 86 | @media screen and (min-width: 750px) { 87 | .collection-hero--with-image .collection-hero__text-wrapper { 88 | padding: 4rem 2rem 4rem 0; 89 | flex-basis: 50%; 90 | } 91 | 92 | .collection-hero__image-container { 93 | align-self: stretch; 94 | flex: 1 0 50%; 95 | margin-left: 3rem; 96 | min-height: 20rem; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /assets/cart-notification.js: -------------------------------------------------------------------------------- 1 | class CartNotification extends HTMLElement { 2 | constructor() { 3 | super(); 4 | 5 | this.notification = document.getElementById('cart-notification'); 6 | this.header = document.querySelector('sticky-header'); 7 | this.onBodyClick = this.handleBodyClick.bind(this); 8 | 9 | this.notification.addEventListener('keyup', (evt) => evt.code === 'Escape' && this.close()); 10 | this.querySelectorAll('button[type="button"]').forEach((closeButton) => 11 | closeButton.addEventListener('click', this.close.bind(this)) 12 | ); 13 | } 14 | 15 | open() { 16 | this.notification.classList.add('animate', 'active'); 17 | 18 | this.notification.addEventListener( 19 | 'transitionend', 20 | () => { 21 | this.notification.focus(); 22 | trapFocus(this.notification); 23 | }, 24 | { once: true } 25 | ); 26 | 27 | document.body.addEventListener('click', this.onBodyClick); 28 | } 29 | 30 | close() { 31 | this.notification.classList.remove('active'); 32 | document.body.removeEventListener('click', this.onBodyClick); 33 | 34 | removeTrapFocus(this.activeElement); 35 | } 36 | 37 | renderContents(parsedState) { 38 | this.cartItemKey = parsedState.key; 39 | this.getSectionsToRender().forEach((section) => { 40 | document.getElementById(section.id).innerHTML = this.getSectionInnerHTML( 41 | parsedState.sections[section.id], 42 | section.selector 43 | ); 44 | }); 45 | 46 | if (this.header) this.header.reveal(); 47 | this.open(); 48 | } 49 | 50 | getSectionsToRender() { 51 | return [ 52 | { 53 | id: 'cart-notification-product', 54 | selector: `[id="cart-notification-product-${this.cartItemKey}"]`, 55 | }, 56 | { 57 | id: 'cart-notification-button', 58 | }, 59 | { 60 | id: 'cart-icon-bubble', 61 | }, 62 | ]; 63 | } 64 | 65 | getSectionInnerHTML(html, selector = '.shopify-section') { 66 | return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML; 67 | } 68 | 69 | handleBodyClick(evt) { 70 | const target = evt.target; 71 | if (target !== this.notification && !target.closest('cart-notification')) { 72 | const disclosure = target.closest('details-disclosure, header-menu'); 73 | this.activeElement = disclosure ? disclosure.querySelector('summary') : null; 74 | this.close(); 75 | } 76 | } 77 | 78 | setActiveElement(element) { 79 | this.activeElement = element; 80 | } 81 | } 82 | 83 | customElements.define('cart-notification', CartNotification); 84 | -------------------------------------------------------------------------------- /templates/product.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-product", 5 | "blocks": { 6 | "vendor": { 7 | "type": "text", 8 | "settings": { 9 | "text": "{{ product.vendor }}", 10 | "text_style": "uppercase" 11 | } 12 | }, 13 | "title": { 14 | "type": "title", 15 | "settings": {} 16 | }, 17 | "price": { 18 | "type": "price", 19 | "settings": {} 20 | }, 21 | "variant_picker": { 22 | "type": "variant_picker", 23 | "settings": { 24 | "picker_type": "button" 25 | } 26 | }, 27 | "quantity_selector": { 28 | "type": "quantity_selector", 29 | "settings": {} 30 | }, 31 | "buy_buttons": { 32 | "type": "buy_buttons", 33 | "settings": { 34 | "show_dynamic_checkout": true, 35 | "show_gift_card_recipient": false 36 | } 37 | }, 38 | "description": { 39 | "type": "description", 40 | "settings": {} 41 | }, 42 | "share": { 43 | "type": "share", 44 | "settings": { 45 | "share_label": "Share" 46 | } 47 | } 48 | }, 49 | "block_order": [ 50 | "vendor", 51 | "title", 52 | "price", 53 | "variant_picker", 54 | "quantity_selector", 55 | "buy_buttons", 56 | "description", 57 | "share" 58 | ], 59 | "settings": { 60 | "enable_sticky_info": true, 61 | "media_size": "large", 62 | "constrain_to_viewport": true, 63 | "media_fit": "contain", 64 | "gallery_layout": "stacked", 65 | "media_position": "left", 66 | "image_zoom": "lightbox", 67 | "mobile_thumbnails": "hide", 68 | "hide_variants": true, 69 | "enable_video_looping": false, 70 | "padding_top": 36, 71 | "padding_bottom": 12 72 | } 73 | }, 74 | "related-products": { 75 | "type": "related-products", 76 | "settings": { 77 | "heading": "You may also like", 78 | "heading_size": "h2", 79 | "products_to_show": 4, 80 | "columns_desktop": 4, 81 | "color_scheme": "background-1", 82 | "image_ratio": "square", 83 | "image_shape": "default", 84 | "show_secondary_image": true, 85 | "show_vendor": false, 86 | "show_rating": false, 87 | "columns_mobile": "2", 88 | "padding_top": 36, 89 | "padding_bottom": 28 90 | } 91 | } 92 | }, 93 | "order": [ 94 | "main", 95 | "related-products" 96 | ] 97 | } -------------------------------------------------------------------------------- /snippets/pagination.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders a set of links for paginated results. Must be used within paginate tags. 3 | 4 | Usage: 5 | {% paginate results by 2 %} 6 | {% render 'pagination', paginate: paginate, anchor: '#yourID' %} 7 | {% endpaginate %} 8 | 9 | Accepts: 10 | - paginate: {Object} 11 | - anchor: {String} (optional) This can be added so that on page reload it takes you to wherever you've placed your anchor tag. 12 | {% endcomment %} 13 | 14 | {{ 'component-pagination.css' | asset_url | stylesheet_tag }} 15 | 16 | {%- if paginate.parts.size > 0 -%} 17 |
18 | 73 |
74 | {%- endif -%} 75 | -------------------------------------------------------------------------------- /assets/component-deferred-media.css: -------------------------------------------------------------------------------- 1 | .deferred-media__poster { 2 | background-color: transparent; 3 | border: none; 4 | cursor: pointer; 5 | margin: 0; 6 | padding: 0; 7 | height: 100%; 8 | width: 100%; 9 | overflow: hidden; 10 | border-radius: calc(var(--border-radius) - var(--border-width)); 11 | } 12 | 13 | .media > .deferred-media__poster { 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | } 18 | 19 | .deferred-media__poster img { 20 | width: auto; 21 | max-width: 100%; 22 | height: 100%; 23 | } 24 | 25 | .deferred-media { 26 | overflow: hidden; 27 | } 28 | 29 | .deferred-media:not([loaded]) template { 30 | z-index: -1; 31 | } 32 | 33 | .deferred-media[loaded] > .deferred-media__poster { 34 | display: none; 35 | } 36 | 37 | .deferred-media__poster:focus-visible { 38 | outline: none; 39 | box-shadow: 0 0 0 var(--media-border-width) rgba(var(--color-foreground), var(--media-border-opacity)), 40 | 0 0 0 calc(var(--media-border-width) + 0.3rem) rgb(var(--color-background)), 41 | 0 0 0 calc(var(--media-border-width) + 0.5rem) rgba(var(--color-foreground), 0.5); 42 | border-radius: calc(var(--media-radius) - var(--media-border-width)); 43 | } 44 | 45 | .deferred-media__poster:focus { 46 | outline: none; 47 | box-shadow: 0 0 0 var(--media-border-width) rgba(var(--color-foreground), var(--media-border-opacity)), 48 | 0 0 0 calc(var(--media-border-width) + 0.3rem) rgb(var(--color-background)), 49 | 0 0 0 calc(var(--media-border-width) + 0.5rem) rgba(var(--color-foreground), 0.5); 50 | border-radius: calc(var(--media-radius) - var(--media-border-width)); 51 | } 52 | 53 | .global-media-settings--full-width .deferred-media__poster, 54 | .global-media-settings--full-width .deferred-media__poster:is(:focus, :focus-visible) { 55 | border-radius: 0; 56 | } 57 | 58 | /* outline styling for Windows High Contrast Mode */ 59 | @media (forced-colors: active) { 60 | .deferred-media__poster:focus { 61 | outline: transparent solid 1px; 62 | } 63 | } 64 | .deferred-media__poster:focus:not(:focus-visible) { 65 | outline: 0; 66 | box-shadow: none; 67 | } 68 | 69 | .deferred-media__poster-button { 70 | background-color: rgb(var(--color-background)); 71 | border: 0.1rem solid rgba(var(--color-foreground), 0.1); 72 | border-radius: 50%; 73 | color: rgb(var(--color-foreground)); 74 | display: flex; 75 | align-items: center; 76 | justify-content: center; 77 | height: 6.2rem; 78 | width: 6.2rem; 79 | position: absolute; 80 | left: 50%; 81 | top: 50%; 82 | transform: translate(-50%, -50%) scale(1); 83 | transition: transform var(--duration-short) ease, color var(--duration-short) ease; 84 | z-index: 1; 85 | } 86 | 87 | .deferred-media__poster-button:hover { 88 | transform: translate(-50%, -50%) scale(1.1); 89 | } 90 | 91 | .deferred-media__poster-button .icon { 92 | width: 2rem; 93 | height: 2rem; 94 | } 95 | 96 | .deferred-media__poster-button .icon-play { 97 | margin-left: 0.2rem; 98 | } 99 | -------------------------------------------------------------------------------- /snippets/product-variant-options.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders product variant options 3 | 4 | Accepts: 5 | - product: {Object} product object. 6 | - option: {Object} current product_option object. 7 | - block: {Object} block object. 8 | 9 | 10 | Usage: 11 | {% render 'product-variant-options', 12 | product: product, 13 | option: option, 14 | block: block 15 | %} 16 | {% endcomment %} 17 | {%- liquid 18 | assign variants_available_arr = product.variants | map: 'available' 19 | assign variants_option1_arr = product.variants | map: 'option1' 20 | assign variants_option2_arr = product.variants | map: 'option2' 21 | assign variants_option3_arr = product.variants | map: 'option3' 22 | 23 | assign product_form_id = 'product-form-' | append: section.id 24 | -%} 25 | 26 | {%- for value in option.values -%} 27 | {%- liquid 28 | assign option_disabled = true 29 | 30 | for option1_name in variants_option1_arr 31 | case option.position 32 | when 1 33 | if variants_option1_arr[forloop.index0] == value and variants_available_arr[forloop.index0] 34 | assign option_disabled = false 35 | endif 36 | when 2 37 | if option1_name == product.selected_or_first_available_variant.option1 and variants_option2_arr[forloop.index0] == value and variants_available_arr[forloop.index0] 38 | assign option_disabled = false 39 | endif 40 | when 3 41 | if option1_name == product.selected_or_first_available_variant.option1 and variants_option2_arr[forloop.index0] == product.selected_or_first_available_variant.option2 and variants_option3_arr[forloop.index0] == value and variants_available_arr[forloop.index0] 42 | assign option_disabled = false 43 | endif 44 | endcase 45 | endfor 46 | -%} 47 | 48 | {%- if block.settings.picker_type == 'button' -%} 49 | 62 | 66 | {%- elsif block.settings.picker_type == 'dropdown' -%} 67 | 79 | {%- endif -%} 80 | {%- endfor -%} 81 | -------------------------------------------------------------------------------- /assets/section-email-signup-banner.css: -------------------------------------------------------------------------------- 1 | .email-signup-banner .newsletter-form, 2 | .email-signup-banner .newsletter-form__field-wrapper { 3 | display: inline-block; 4 | } 5 | 6 | @media only screen and (min-width: 750px) { 7 | .email-signup-banner:not(.banner--desktop-transparent) .email-signup-banner__box { 8 | width: 100%; 9 | } 10 | } 11 | 12 | .email-signup-banner__box .email-signup-banner__heading { 13 | margin-bottom: 0; 14 | } 15 | 16 | .email-signup-banner__box > * + .newsletter__subheading { 17 | margin-top: 2rem; 18 | } 19 | 20 | .email-signup-banner__box .newsletter__subheading p { 21 | margin: 0; 22 | } 23 | 24 | .email-signup-banner-background { 25 | width: 100%; 26 | height: 100%; 27 | position: relative; 28 | left: 50%; 29 | transform: translateX(-50%); 30 | } 31 | 32 | @media screen and (max-width: 749px) { 33 | .email-signup-banner:not(.banner--mobile-bottom) .banner__box:not(.email-signup-banner__box--no-image) { 34 | background-color: transparent; 35 | --color-foreground: 255, 255, 255; 36 | --color-button: 255, 255, 255; 37 | --color-button-text: 0, 0, 0; 38 | } 39 | } 40 | 41 | @media only screen and (min-width: 750px) { 42 | .banner--desktop-transparent .email-signup-banner__box--no-image * { 43 | color: rgb(var(--color-foreground)); 44 | } 45 | 46 | .banner--desktop-transparent .email-signup-banner__box .field__input { 47 | background-color: transparent; 48 | } 49 | 50 | .banner--desktop-transparent .email-signup-banner__box--no-image .field__input { 51 | box-shadow: 0 0 0 0.1rem rgba(var(--color-foreground), 0.55); 52 | } 53 | 54 | .banner--desktop-transparent .email-signup-banner__box--no-image .field__input:focus { 55 | box-shadow: 0 0 0 0.2rem rgba(var(--color-foreground), 0.75); 56 | } 57 | 58 | .banner--desktop-transparent .email-signup-banner__box--no-image .field__button:focus-visible { 59 | outline: 0.2rem solid rgba(var(--color-foreground), 0.5); 60 | box-shadow: 0 0 0 0.3rem rgb(var(--color-background)), 0 0 0.5rem 0.4rem rgba(var(--color-foreground), 0.3); 61 | } 62 | } 63 | 64 | @media only screen and (min-width: 750px) { 65 | .email-signup-banner-background-mobile { 66 | display: none; 67 | } 68 | } 69 | 70 | @media only screen and (max-width: 749px) { 71 | .email-signup-banner-background:not(.email-signup-banner-background-mobile) { 72 | display: none; 73 | } 74 | } 75 | 76 | .email-signup-banner .banner__media { 77 | overflow: hidden; 78 | } 79 | 80 | @media screen and (max-width: 749px) { 81 | .banner--mobile-content-align-left .newsletter-form__message { 82 | justify-content: flex-start; 83 | } 84 | 85 | .banner--mobile-content-align-right .newsletter-form__message { 86 | justify-content: right; 87 | } 88 | } 89 | 90 | @media screen and (min-width: 750px) { 91 | .banner--content-align-center .newsletter-form__message { 92 | justify-content: center; 93 | } 94 | 95 | .banner--content-align-right .newsletter-form__message { 96 | justify-content: right; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /assets/component-article-card.css: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 749px) { 2 | .articles-wrapper .article { 3 | width: 100%; 4 | } 5 | } 6 | 7 | .article { 8 | display: flex; 9 | align-items: center; 10 | } 11 | 12 | .article.grid__item { 13 | padding: 0; 14 | } 15 | 16 | .grid--peek .article-card { 17 | box-sizing: border-box; 18 | } 19 | 20 | .article-card__image-wrapper > a { 21 | display: block; 22 | } 23 | 24 | .article-card__title { 25 | text-decoration: none; 26 | word-break: break-word; 27 | } 28 | 29 | .article-card__title a:after { 30 | bottom: 0; 31 | content: ''; 32 | height: 100%; 33 | left: 0; 34 | position: absolute; 35 | right: 0; 36 | top: 0; 37 | width: 100%; 38 | z-index: 1; 39 | } 40 | 41 | .article-card__link.link { 42 | padding: 0; 43 | } 44 | 45 | .article-card__link { 46 | text-underline-offset: 0.3rem; 47 | } 48 | 49 | .article-card .card__heading { 50 | margin-bottom: 0.6rem; 51 | } 52 | 53 | .blog-articles .article-card .card__information, 54 | .blog__posts .article-card .card__information { 55 | padding-left: 2rem; 56 | padding-right: 2rem; 57 | } 58 | 59 | .article-card__info { 60 | padding-top: 0.4rem; 61 | } 62 | 63 | .article-card__footer { 64 | letter-spacing: 0.1rem; 65 | font-size: 1.4rem; 66 | } 67 | 68 | .article-card__footer:not(:last-child) { 69 | margin-bottom: 1rem; 70 | } 71 | 72 | .article-card__footer:last-child { 73 | margin-top: auto; 74 | } 75 | 76 | .article-card__excerpt { 77 | width: 100%; 78 | margin-top: 1.2rem; 79 | } 80 | 81 | .article-card__link:not(:only-child) { 82 | margin-right: 3rem; 83 | } 84 | 85 | @media screen and (min-width: 990px) { 86 | .article-card__link:not(:only-child) { 87 | margin-right: 4rem; 88 | } 89 | } 90 | 91 | .article-card__image--small .ratio::before { 92 | padding-bottom: 11rem; 93 | } 94 | 95 | .article-card__image--medium .ratio::before { 96 | padding-bottom: 22rem; 97 | } 98 | 99 | .article-card__image--large .ratio::before { 100 | padding-bottom: 33rem; 101 | } 102 | 103 | @media screen and (min-width: 750px) { 104 | .article-card__image--small .ratio::before { 105 | padding-bottom: 14.3rem; 106 | } 107 | 108 | .article-card__image--medium .ratio::before { 109 | padding-bottom: 21.9rem; 110 | } 111 | 112 | .article-card__image--large .ratio::before { 113 | padding-bottom: 27.5rem; 114 | } 115 | } 116 | 117 | @media screen and (min-width: 990px) { 118 | .article-card__image--small .ratio::before { 119 | padding-bottom: 17.7rem; 120 | } 121 | 122 | .article-card__image--medium .ratio::before { 123 | padding-bottom: 30.7rem; 124 | } 125 | 126 | .article-card__image--large .ratio::before { 127 | padding-bottom: 40.7rem; 128 | } 129 | } 130 | 131 | /* check for flexbox gap in older Safari versions */ 132 | @supports not (inset: 10px) { 133 | .articles-wrapper.grid { 134 | margin: 0 0 5rem 0; 135 | } 136 | 137 | @media screen and (min-width: 750px) { 138 | .articles-wrapper.grid { 139 | margin-bottom: 7rem; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /assets/customer.js: -------------------------------------------------------------------------------- 1 | const selectors = { 2 | customerAddresses: '[data-customer-addresses]', 3 | addressCountrySelect: '[data-address-country-select]', 4 | addressContainer: '[data-address]', 5 | toggleAddressButton: 'button[aria-expanded]', 6 | cancelAddressButton: 'button[type="reset"]', 7 | deleteAddressButton: 'button[data-confirm-message]', 8 | }; 9 | 10 | const attributes = { 11 | expanded: 'aria-expanded', 12 | confirmMessage: 'data-confirm-message', 13 | }; 14 | 15 | class CustomerAddresses { 16 | constructor() { 17 | this.elements = this._getElements(); 18 | if (Object.keys(this.elements).length === 0) return; 19 | this._setupCountries(); 20 | this._setupEventListeners(); 21 | } 22 | 23 | _getElements() { 24 | const container = document.querySelector(selectors.customerAddresses); 25 | return container 26 | ? { 27 | container, 28 | addressContainer: container.querySelector(selectors.addressContainer), 29 | toggleButtons: document.querySelectorAll(selectors.toggleAddressButton), 30 | cancelButtons: container.querySelectorAll(selectors.cancelAddressButton), 31 | deleteButtons: container.querySelectorAll(selectors.deleteAddressButton), 32 | countrySelects: container.querySelectorAll(selectors.addressCountrySelect), 33 | } 34 | : {}; 35 | } 36 | 37 | _setupCountries() { 38 | if (Shopify && Shopify.CountryProvinceSelector) { 39 | // eslint-disable-next-line no-new 40 | new Shopify.CountryProvinceSelector('AddressCountryNew', 'AddressProvinceNew', { 41 | hideElement: 'AddressProvinceContainerNew', 42 | }); 43 | this.elements.countrySelects.forEach((select) => { 44 | const formId = select.dataset.formId; 45 | // eslint-disable-next-line no-new 46 | new Shopify.CountryProvinceSelector(`AddressCountry_${formId}`, `AddressProvince_${formId}`, { 47 | hideElement: `AddressProvinceContainer_${formId}`, 48 | }); 49 | }); 50 | } 51 | } 52 | 53 | _setupEventListeners() { 54 | this.elements.toggleButtons.forEach((element) => { 55 | element.addEventListener('click', this._handleAddEditButtonClick); 56 | }); 57 | this.elements.cancelButtons.forEach((element) => { 58 | element.addEventListener('click', this._handleCancelButtonClick); 59 | }); 60 | this.elements.deleteButtons.forEach((element) => { 61 | element.addEventListener('click', this._handleDeleteButtonClick); 62 | }); 63 | } 64 | 65 | _toggleExpanded(target) { 66 | target.setAttribute(attributes.expanded, (target.getAttribute(attributes.expanded) === 'false').toString()); 67 | } 68 | 69 | _handleAddEditButtonClick = ({ currentTarget }) => { 70 | this._toggleExpanded(currentTarget); 71 | }; 72 | 73 | _handleCancelButtonClick = ({ currentTarget }) => { 74 | this._toggleExpanded(currentTarget.closest(selectors.addressContainer).querySelector(`[${attributes.expanded}]`)); 75 | }; 76 | 77 | _handleDeleteButtonClick = ({ currentTarget }) => { 78 | // eslint-disable-next-line no-alert 79 | if (confirm(currentTarget.getAttribute(attributes.confirmMessage))) { 80 | Shopify.postLink(currentTarget.dataset.target, { 81 | parameters: { _method: 'delete' }, 82 | }); 83 | } 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /snippets/email-signup-banner-background-mobile.liquid: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /snippets/social-icons.liquid: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sections/page.liquid: -------------------------------------------------------------------------------- 1 | {{ 'section-main-page.css' | asset_url | stylesheet_tag }} 2 | 3 | {%- style -%} 4 | .section-{{ section.id }}-padding { 5 | padding-top: {{ section.settings.padding_top | times: 0.75 | round: 0 }}px; 6 | padding-bottom: {{ section.settings.padding_bottom | times: 0.75 | round: 0 }}px; 7 | } 8 | 9 | @media screen and (min-width: 750px) { 10 | .section-{{ section.id }}-padding { 11 | padding-top: {{ section.settings.padding_top }}px; 12 | padding-bottom: {{ section.settings.padding_bottom }}px; 13 | } 14 | } 15 | {%- endstyle -%} 16 | 17 |
18 |
19 |

20 | {%- if section.settings.page.title != blank -%} 21 | {{ section.settings.page.title | escape }} 22 | {%- else -%} 23 | {{ 'sections.page.title' | t }} 24 | {%- endif -%} 25 |

26 |
27 | {%- if section.settings.page.content != blank -%} 28 | {{ section.settings.page.content }} 29 | {%- else -%} 30 |
31 | {{ 'page' | placeholder_svg_tag: 'page-placeholder' }} 32 |
33 | {%- endif -%} 34 |
35 |
36 |
37 | 38 | {% schema %} 39 | { 40 | "name": "t:sections.page.name", 41 | "tag": "section", 42 | "class": "section", 43 | "disabled_on": { 44 | "groups": ["header", "footer"] 45 | }, 46 | "settings": [ 47 | { 48 | "type": "page", 49 | "id": "page", 50 | "label": "t:sections.page.settings.page.label" 51 | }, 52 | { 53 | "type": "select", 54 | "id": "heading_size", 55 | "options": [ 56 | { 57 | "value": "h2", 58 | "label": "t:sections.all.heading_size.options__1.label" 59 | }, 60 | { 61 | "value": "h1", 62 | "label": "t:sections.all.heading_size.options__2.label" 63 | }, 64 | { 65 | "value": "h0", 66 | "label": "t:sections.all.heading_size.options__3.label" 67 | } 68 | ], 69 | "default": "h1", 70 | "label": "t:sections.all.heading_size.label" 71 | }, 72 | { 73 | "type": "color_scheme", 74 | "id": "color_scheme", 75 | "label": "t:sections.all.colors.label", 76 | "default": "background-1" 77 | }, 78 | { 79 | "type": "header", 80 | "content": "t:sections.all.padding.section_padding_heading" 81 | }, 82 | { 83 | "type": "range", 84 | "id": "padding_top", 85 | "min": 0, 86 | "max": 100, 87 | "step": 4, 88 | "unit": "px", 89 | "label": "t:sections.all.padding.padding_top", 90 | "default": 36 91 | }, 92 | { 93 | "type": "range", 94 | "id": "padding_bottom", 95 | "min": 0, 96 | "max": 100, 97 | "step": 4, 98 | "unit": "px", 99 | "label": "t:sections.all.padding.padding_bottom", 100 | "default": 36 101 | } 102 | ], 103 | "presets": [ 104 | { 105 | "name": "t:sections.page.presets.name" 106 | } 107 | ] 108 | } 109 | {% endschema %} 110 | -------------------------------------------------------------------------------- /assets/component-cart-notification.css: -------------------------------------------------------------------------------- 1 | .cart-notification-wrapper { 2 | position: relative; 3 | } 4 | 5 | .cart-notification-wrapper .cart-notification { 6 | display: block; 7 | } 8 | 9 | .cart-notification { 10 | border-bottom-right-radius: var(--popup-corner-radius); 11 | border-bottom-left-radius: var(--popup-corner-radius); 12 | border-color: rgba(var(--color-foreground), var(--popup-border-opacity)); 13 | border-style: solid; 14 | border-width: 0 0 var(--popup-border-width); 15 | padding: 2.5rem 3.5rem; 16 | position: absolute; 17 | right: 0; 18 | transform: translateY(-100%); 19 | visibility: hidden; 20 | width: 100%; 21 | box-shadow: var(--popup-shadow-horizontal-offset) var(--popup-shadow-vertical-offset) var(--popup-shadow-blur-radius) 22 | rgba(var(--color-shadow), var(--popup-shadow-opacity)); 23 | z-index: -1; 24 | } 25 | 26 | .cart-notification.focused { 27 | box-shadow: 0 0 0.2rem 0 rgba(var(--color-foreground), 0.3), 28 | var(--popup-shadow-horizontal-offset) var(--popup-shadow-vertical-offset) var(--popup-shadow-blur-radius) 29 | rgba(var(--color-shadow), var(--popup-shadow-opacity)); 30 | } 31 | 32 | .cart-notification:focus-visible { 33 | box-shadow: 0 0 0.2rem 0 rgba(var(--color-foreground), 0.3), 34 | var(--popup-shadow-horizontal-offset) var(--popup-shadow-vertical-offset) var(--popup-shadow-blur-radius) 35 | rgba(var(--color-shadow), var(--popup-shadow-opacity)); 36 | } 37 | 38 | @media screen and (min-width: 750px) { 39 | .header-wrapper:not(.header-wrapper--border-bottom) + cart-notification .cart-notification { 40 | border-top-width: var(--popup-border-width); 41 | } 42 | 43 | .cart-notification { 44 | border-width: 0 var(--popup-border-width) var(--popup-border-width); 45 | max-width: 36.8rem; 46 | right: 2.2rem; 47 | } 48 | } 49 | 50 | @media screen and (min-width: 990px) { 51 | .cart-notification-wrapper:is(.page-width) > .cart-notification { 52 | right: 4rem; 53 | } 54 | } 55 | 56 | .cart-notification.animate { 57 | transition: transform var(--duration-short) ease, visibility 0s var(--duration-short) ease; 58 | } 59 | 60 | .cart-notification.active { 61 | transform: translateY(0); 62 | transition: transform var(--duration-default) ease, visibility 0s; 63 | visibility: visible; 64 | } 65 | 66 | .cart-notification__header { 67 | align-items: flex-start; 68 | display: flex; 69 | } 70 | 71 | .cart-notification__heading { 72 | align-items: center; 73 | display: flex; 74 | flex-grow: 1; 75 | margin-bottom: 0; 76 | margin-top: 0; 77 | } 78 | 79 | .cart-notification__heading .icon-checkmark { 80 | color: rgb(var(--color-foreground)); 81 | margin-right: 1rem; 82 | width: 1.3rem; 83 | } 84 | 85 | .cart-notification__close { 86 | margin-top: -2rem; 87 | margin-right: -3rem; 88 | } 89 | 90 | .cart-notification__links { 91 | text-align: center; 92 | } 93 | 94 | .cart-notification__links > * { 95 | margin-top: 1rem; 96 | } 97 | 98 | .cart-notification-product { 99 | align-items: flex-start; 100 | display: flex; 101 | padding-bottom: 3rem; 102 | padding-top: 2rem; 103 | } 104 | 105 | .cart-notification-product dl { 106 | margin-bottom: 0; 107 | margin-top: 0; 108 | } 109 | 110 | .cart-notification-product__image { 111 | display: inline-flex; 112 | margin-right: 1.5rem; 113 | margin-top: 0.5rem; 114 | } 115 | 116 | .cart-notification-product__image:after { 117 | content: none; 118 | } 119 | 120 | .cart-notification-product__name { 121 | margin-bottom: 0.5rem; 122 | margin-top: 0; 123 | } 124 | -------------------------------------------------------------------------------- /assets/collapsible-content.css: -------------------------------------------------------------------------------- 1 | .collapsible-content { 2 | position: relative; 3 | z-index: 0; 4 | } 5 | 6 | .collapsible-section-layout { 7 | padding-bottom: 5rem; 8 | padding-top: 5rem; 9 | } 10 | 11 | @media screen and (min-width: 750px) { 12 | .collapsible-section-layout { 13 | padding-bottom: 7rem; 14 | padding-top: 7rem; 15 | } 16 | } 17 | 18 | /* Needed for gradient continuity with or without animation so that transparent PNG images come up as we would expect */ 19 | .collapsible-content__media { 20 | background: transparent; 21 | } 22 | 23 | .collapsible-content__media--small { 24 | height: 19.4rem; 25 | } 26 | 27 | .collapsible-content__media--large { 28 | height: 43.5rem; 29 | } 30 | 31 | @media screen and (min-width: 750px) { 32 | .collapsible-content__media--small { 33 | height: 31.4rem; 34 | } 35 | 36 | .collapsible-content__media--large { 37 | height: 69.5rem; 38 | } 39 | } 40 | 41 | @media screen and (min-width: 750px) { 42 | .collapsible-content__grid--reverse { 43 | flex-direction: row-reverse; 44 | } 45 | } 46 | 47 | .collapsible-content-wrapper-narrow { 48 | margin: 0 auto; 49 | padding-right: 1.5rem; 50 | padding-left: 1.5rem; 51 | max-width: 73.4rem; 52 | } 53 | 54 | .collapsible-content__header { 55 | word-break: break-word; 56 | } 57 | 58 | .collapsible-content__heading { 59 | margin-bottom: 2rem; 60 | margin-top: 0; 61 | } 62 | 63 | @media screen and (min-width: 750px) { 64 | .collapsible-content__heading { 65 | margin-bottom: 3rem; 66 | } 67 | } 68 | 69 | .collapsible-none-layout .accordion + .accordion { 70 | border-top: 0; 71 | } 72 | 73 | .collapsible-row-layout .accordion:not(:first-child):not(.color-background-1) { 74 | margin-top: 1rem; 75 | } 76 | 77 | .caption-with-letter-spacing + h2 { 78 | margin-top: 1rem; 79 | } 80 | 81 | @media screen and (min-width: 750px) { 82 | .collapsible-content .accordion { 83 | margin-top: 0; 84 | } 85 | } 86 | 87 | .collapsible-row-layout .accordion { 88 | border: var(--text-boxes-border-width) solid rgba(var(--color-foreground), var(--text-boxes-border-opacity)); 89 | margin-bottom: 1.5rem; 90 | /* Needed for gradient continuity with or without animation, the transform scopes the gradient to its container which happens already when animation are turned on */ 91 | transform: perspective(0); 92 | } 93 | 94 | .collapsible-row-layout .accordion summary, 95 | .collapsible-row-layout .accordion .accordion__content { 96 | padding: 1.5rem; 97 | } 98 | 99 | .collapsible-row-layout .accordion .accordion__content { 100 | padding-top: 0; 101 | } 102 | 103 | .collapsible-content summary:hover { 104 | background: rgba(var(--color-foreground), 0.04); 105 | } 106 | 107 | .collapsible-content summary:hover .accordion__title { 108 | text-decoration: underline; 109 | text-underline-offset: 0.3rem; 110 | } 111 | 112 | /* check for flexbox gap in older Safari versions */ 113 | @supports not (inset: 10px) { 114 | @media screen and (min-width: 750px) { 115 | .collapsible-content__grid:not(.collapsible-content__grid--reverse) .grid__item:last-child, 116 | .collapsible-content__grid--reverse .collapsible-content__grid-item { 117 | padding-left: 5rem; 118 | padding-right: 0; 119 | } 120 | } 121 | 122 | @media screen and (min-width: 990px) { 123 | .collapsible-content__grid:not(.collapsible-content__grid--reverse) .grid__item:last-child, 124 | .collapsible-content__grid--reverse .collapsible-content__grid-item { 125 | padding-left: 7rem; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /sections/main-collection-banner.liquid: -------------------------------------------------------------------------------- 1 | {% comment %}theme-check-disable ImgLazyLoading{% endcomment %} 2 | {{ 'component-collection-hero.css' | asset_url | stylesheet_tag }} 3 | 4 | {%- style -%} 5 | @media screen and (max-width: 749px) { 6 | .collection-hero--with-image .collection-hero__inner { 7 | padding-bottom: calc({{ settings.media_shadow_vertical_offset | at_least: 0 }}px + 2rem); 8 | } 9 | } 10 | {%- endstyle -%} 11 | 12 |
13 |
14 |
15 |

16 | {{ 'sections.collection_template.title' | t }}: 17 | {{- collection.title | escape -}} 18 |

19 | 20 | {%- if section.settings.show_collection_description -%} 21 |
{{ collection.description }}
22 | {%- endif -%} 23 |
24 | 25 | {%- if section.settings.show_collection_image and collection.image -%} 26 |
27 | 43 |
44 | {%- endif -%} 45 |
46 |
47 | 48 | {% schema %} 49 | { 50 | "name": "t:sections.main-collection-banner.name", 51 | "class": "section", 52 | "settings": [ 53 | { 54 | "type": "paragraph", 55 | "content": "t:sections.main-collection-banner.settings.paragraph.content" 56 | }, 57 | { 58 | "type": "checkbox", 59 | "id": "show_collection_description", 60 | "default": true, 61 | "label": "t:sections.main-collection-banner.settings.show_collection_description.label" 62 | }, 63 | { 64 | "type": "checkbox", 65 | "id": "show_collection_image", 66 | "default": false, 67 | "label": "t:sections.main-collection-banner.settings.show_collection_image.label", 68 | "info": "t:sections.main-collection-banner.settings.show_collection_image.info" 69 | }, 70 | { 71 | "type": "color_scheme", 72 | "id": "color_scheme", 73 | "label": "t:sections.all.colors.label", 74 | "default": "background-1" 75 | } 76 | ] 77 | } 78 | {% endschema %} 79 | -------------------------------------------------------------------------------- /snippets/icon-with-text.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders icon with text block 3 | 4 | Accepts: 5 | - block: {Object} passes in the block information. 6 | 7 | 8 | Usage: 9 | {% render 'icon-with-text', 10 | block: block 11 | %} 12 | {% endcomment %} 13 | {%- liquid 14 | assign heading_1_empty = false 15 | assign heading_2_empty = false 16 | assign heading_3_empty = false 17 | assign text_only_all_items = true 18 | 19 | if block.settings.heading_1 == empty 20 | assign heading_1_empty = true 21 | endif 22 | 23 | if block.settings.heading_2 == empty 24 | assign heading_2_empty = true 25 | endif 26 | 27 | if block.settings.heading_3 == empty 28 | assign heading_3_empty = true 29 | endif 30 | 31 | if heading_1_empty == false and block.settings.icon_1 != 'none' or block.settings.image_1 != null 32 | assign text_only_all_items = false 33 | elsif heading_2_empty == false and block.settings.icon_2 != 'none' or block.settings.image_2 != null 34 | assign text_only_all_items = false 35 | elsif heading_3_empty == false and block.settings.icon_3 != 'none' or block.settings.image_3 != null 36 | assign text_only_all_items = false 37 | endif 38 | -%} 39 | 110 | -------------------------------------------------------------------------------- /snippets/header-mega-menu.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders a megamenu for the header. 3 | 4 | Usage: 5 | {% render 'header-mega-menu' %} 6 | {% endcomment %} 7 | 8 | 95 | -------------------------------------------------------------------------------- /assets/section-blog-post.css: -------------------------------------------------------------------------------- 1 | .article-template > *:first-child:not(.article-template__hero-container) { 2 | margin-top: 5rem; 3 | } 4 | 5 | @media screen and (min-width: 750px) { 6 | .article-template > *:first-child:not(.article-template__hero-container) { 7 | margin-top: calc(5rem + var(--page-width-margin)); 8 | } 9 | } 10 | 11 | .article-template__hero-container { 12 | max-width: 130rem; 13 | margin: 0 auto; 14 | } 15 | 16 | .article-template__hero-small { 17 | height: 11rem; 18 | } 19 | 20 | .article-template__hero-medium { 21 | height: 22rem; 22 | } 23 | 24 | .article-template__hero-large { 25 | height: 33rem; 26 | } 27 | 28 | @media screen and (min-width: 750px) and (max-width: 989px) { 29 | .article-template__hero-small { 30 | height: 22rem; 31 | } 32 | 33 | .article-template__hero-medium { 34 | height: 44rem; 35 | } 36 | 37 | .article-template__hero-large { 38 | height: 66rem; 39 | } 40 | } 41 | 42 | @media screen and (min-width: 990px) { 43 | .article-template__hero-small { 44 | height: 27.5rem; 45 | } 46 | 47 | .article-template__hero-medium { 48 | height: 55rem; 49 | } 50 | 51 | .article-template__hero-large { 52 | height: 82.5rem; 53 | } 54 | } 55 | 56 | .article-template header { 57 | margin-top: 4.4rem; 58 | margin-bottom: 2rem; 59 | line-height: calc(0.8 / var(--font-body-scale)); 60 | } 61 | 62 | @media screen and (min-width: 750px) { 63 | .article-template header { 64 | margin-top: 5rem; 65 | } 66 | } 67 | 68 | .article-template__title { 69 | margin: 0; 70 | } 71 | 72 | .article-template__title:not(:only-child) { 73 | margin-bottom: 1rem; 74 | } 75 | 76 | .article-template__link { 77 | font-size: 1.8rem; 78 | display: flex; 79 | justify-content: center; 80 | align-items: center; 81 | text-decoration: none; 82 | } 83 | 84 | .article-template__link .icon-wrap { 85 | display: flex; 86 | margin-right: 1rem; 87 | transform: rotate(180deg); 88 | } 89 | 90 | .article-template__content { 91 | margin-top: 3rem; 92 | margin-bottom: 3rem; 93 | } 94 | 95 | .article-template__social-sharing { 96 | margin-top: 3rem; 97 | } 98 | 99 | .article-template__social-sharing + header, 100 | .article-template__social-sharing + .article-template__content { 101 | margin-top: 1.5rem; 102 | } 103 | 104 | .article-template__comment-wrapper { 105 | margin-top: 5rem; 106 | padding: 2.7rem 0; 107 | } 108 | 109 | @media screen and (min-width: 750px) { 110 | .article-template__comment-wrapper { 111 | margin-top: 6rem; 112 | padding: 3.6rem 0; 113 | } 114 | } 115 | 116 | .article-template__comment-wrapper h2 { 117 | margin-top: 0; 118 | } 119 | 120 | .article-template__comments { 121 | margin-bottom: 5rem; 122 | } 123 | 124 | @media screen and (min-width: 750px) { 125 | .article-template__comments { 126 | margin-bottom: 7rem; 127 | } 128 | } 129 | 130 | .article-template__comments-fields { 131 | margin-bottom: 4rem; 132 | } 133 | 134 | .article-template__comments-comment { 135 | color: rgba(var(--color-foreground), 0.75); 136 | background-color: rgb(var(--color-background)); 137 | margin-bottom: 1.5rem; 138 | padding: 2rem 2rem 1.5rem; 139 | } 140 | 141 | @media screen and (min-width: 750px) { 142 | .article-template__comments-comment { 143 | padding: 2rem 2.5rem; 144 | } 145 | } 146 | 147 | .article-template__comments-comment p { 148 | margin: 0 0 1rem; 149 | } 150 | 151 | .article-template__comment-fields > * { 152 | margin-bottom: 3rem; 153 | } 154 | 155 | @media screen and (min-width: 750px) { 156 | .article-template__comment-fields { 157 | display: grid; 158 | grid-template-columns: repeat(2, 1fr); 159 | grid-column-gap: 4rem; 160 | } 161 | } 162 | 163 | .article-template__comment-warning { 164 | margin: 2rem 0 2.5rem; 165 | } 166 | 167 | @media screen and (min-width: 990px) { 168 | .article-template__comments .pagination-wrapper { 169 | margin: 5rem 0 8rem; 170 | } 171 | } 172 | 173 | .article-template__back:last-child { 174 | margin-bottom: 3.2rem; 175 | } 176 | -------------------------------------------------------------------------------- /assets/pickup-availability.js: -------------------------------------------------------------------------------- 1 | if (!customElements.get('pickup-availability')) { 2 | customElements.define( 3 | 'pickup-availability', 4 | class PickupAvailability extends HTMLElement { 5 | constructor() { 6 | super(); 7 | 8 | if (!this.hasAttribute('available')) return; 9 | 10 | this.errorHtml = this.querySelector('template').content.firstElementChild.cloneNode(true); 11 | this.onClickRefreshList = this.onClickRefreshList.bind(this); 12 | this.fetchAvailability(this.dataset.variantId); 13 | } 14 | 15 | fetchAvailability(variantId) { 16 | let rootUrl = this.dataset.rootUrl; 17 | if (!rootUrl.endsWith('/')) { 18 | rootUrl = rootUrl + '/'; 19 | } 20 | const variantSectionUrl = `${rootUrl}variants/${variantId}/?section_id=pickup-availability`; 21 | 22 | fetch(variantSectionUrl) 23 | .then((response) => response.text()) 24 | .then((text) => { 25 | const sectionInnerHTML = new DOMParser() 26 | .parseFromString(text, 'text/html') 27 | .querySelector('.shopify-section'); 28 | this.renderPreview(sectionInnerHTML); 29 | }) 30 | .catch((e) => { 31 | const button = this.querySelector('button'); 32 | if (button) button.removeEventListener('click', this.onClickRefreshList); 33 | this.renderError(); 34 | }); 35 | } 36 | 37 | onClickRefreshList(evt) { 38 | this.fetchAvailability(this.dataset.variantId); 39 | } 40 | 41 | renderError() { 42 | this.innerHTML = ''; 43 | this.appendChild(this.errorHtml); 44 | 45 | this.querySelector('button').addEventListener('click', this.onClickRefreshList); 46 | } 47 | 48 | renderPreview(sectionInnerHTML) { 49 | const drawer = document.querySelector('pickup-availability-drawer'); 50 | if (drawer) drawer.remove(); 51 | if (!sectionInnerHTML.querySelector('pickup-availability-preview')) { 52 | this.innerHTML = ''; 53 | this.removeAttribute('available'); 54 | return; 55 | } 56 | 57 | this.innerHTML = sectionInnerHTML.querySelector('pickup-availability-preview').outerHTML; 58 | this.setAttribute('available', ''); 59 | 60 | document.body.appendChild(sectionInnerHTML.querySelector('pickup-availability-drawer')); 61 | 62 | const button = this.querySelector('button'); 63 | if (button) 64 | button.addEventListener('click', (evt) => { 65 | document.querySelector('pickup-availability-drawer').show(evt.target); 66 | }); 67 | } 68 | } 69 | ); 70 | } 71 | 72 | if (!customElements.get('pickup-availability-drawer')) { 73 | customElements.define( 74 | 'pickup-availability-drawer', 75 | class PickupAvailabilityDrawer extends HTMLElement { 76 | constructor() { 77 | super(); 78 | 79 | this.onBodyClick = this.handleBodyClick.bind(this); 80 | 81 | this.querySelector('button').addEventListener('click', () => { 82 | this.hide(); 83 | }); 84 | 85 | this.addEventListener('keyup', (event) => { 86 | if (event.code.toUpperCase() === 'ESCAPE') this.hide(); 87 | }); 88 | } 89 | 90 | handleBodyClick(evt) { 91 | const target = evt.target; 92 | if ( 93 | target != this && 94 | !target.closest('pickup-availability-drawer') && 95 | target.id != 'ShowPickupAvailabilityDrawer' 96 | ) { 97 | this.hide(); 98 | } 99 | } 100 | 101 | hide() { 102 | this.removeAttribute('open'); 103 | document.body.removeEventListener('click', this.onBodyClick); 104 | document.body.classList.remove('overflow-hidden'); 105 | removeTrapFocus(this.focusElement); 106 | } 107 | 108 | show(focusElement) { 109 | this.focusElement = focusElement; 110 | this.setAttribute('open', ''); 111 | document.body.addEventListener('click', this.onBodyClick); 112 | document.body.classList.add('overflow-hidden'); 113 | trapFocus(this); 114 | } 115 | } 116 | ); 117 | } 118 | -------------------------------------------------------------------------------- /assets/component-pickup-availability.css: -------------------------------------------------------------------------------- 1 | pickup-availability { 2 | display: block; 3 | } 4 | 5 | pickup-availability[available] { 6 | min-height: 8rem; 7 | } 8 | 9 | .pickup-availability-preview { 10 | align-items: flex-start; 11 | display: flex; 12 | gap: 0.2rem; 13 | padding: 1rem 2rem 0 0; 14 | } 15 | 16 | .pickup-availability-preview .icon { 17 | flex-shrink: 0; 18 | height: 1.8rem; 19 | } 20 | 21 | .pickup-availability-preview .icon-unavailable { 22 | height: 1.6rem; 23 | margin-top: 0.1rem; 24 | } 25 | 26 | .pickup-availability-button { 27 | background-color: transparent; 28 | color: rgba(var(--color-foreground), 0.75); 29 | letter-spacing: 0.06rem; 30 | padding: 0 0 0.2rem; 31 | text-align: left; 32 | text-decoration: underline; 33 | } 34 | 35 | .pickup-availability-button:hover { 36 | color: rgb(var(--color-foreground)); 37 | } 38 | 39 | .pickup-availability-info * { 40 | margin: 0 0 0.6rem; 41 | } 42 | 43 | pickup-availability-drawer { 44 | background-color: rgb(var(--color-background)); 45 | height: 100%; 46 | opacity: 0; 47 | overflow-y: auto; 48 | padding: 2rem; 49 | position: fixed; 50 | top: 0; 51 | right: 0; 52 | z-index: 4; 53 | transition: opacity var(--duration-default) ease, transform var(--duration-default) ease; 54 | transform: translateX(100%); 55 | width: 100%; 56 | border-width: 0 0 0 var(--drawer-border-width); 57 | border-color: rgba(var(--color-foreground), var(--drawer-border-opacity)); 58 | border-style: solid; 59 | filter: drop-shadow( 60 | var(--drawer-shadow-horizontal-offset) var(--drawer-shadow-vertical-offset) var(--drawer-shadow-blur-radius) 61 | rgba(var(--color-shadow), var(--drawer-shadow-opacity)) 62 | ); 63 | } 64 | 65 | pickup-availability-drawer[open] { 66 | transform: translateX(0); 67 | opacity: 1; 68 | } 69 | 70 | @media screen and (min-width: 750px) { 71 | pickup-availability-drawer { 72 | transform: translateX(100%); 73 | width: 37.5rem; 74 | } 75 | 76 | pickup-availability-drawer[open] { 77 | opacity: 1; 78 | transform: translateX(0); 79 | animation: animateDrawerOpen var(--duration-default) ease; 80 | } 81 | } 82 | 83 | .pickup-availability-header { 84 | align-items: flex-start; 85 | display: flex; 86 | justify-content: space-between; 87 | margin-bottom: 1.2rem; 88 | } 89 | 90 | .pickup-availability-drawer-title { 91 | margin: 0.5rem 0 0; 92 | } 93 | 94 | .pickup-availability-header .icon { 95 | width: 2rem; 96 | } 97 | 98 | .pickup-availability-drawer-button { 99 | background-color: transparent; 100 | border: none; 101 | color: rgb(var(--color-foreground)); 102 | cursor: pointer; 103 | display: block; 104 | height: 4.4rem; 105 | padding: 1.2rem; 106 | width: 4.4rem; 107 | } 108 | 109 | .pickup-availability-drawer-button:hover { 110 | color: rgba(var(--color-foreground), 0.75); 111 | } 112 | 113 | .pickup-availability-variant { 114 | font-size: 1.3rem; 115 | line-height: calc(1 + 0.2 / var(--font-body-scale)); 116 | margin: 0 0 1.2rem; 117 | text-transform: capitalize; 118 | } 119 | 120 | .pickup-availability-variant > * + strong { 121 | margin-left: 1rem; 122 | } 123 | 124 | .pickup-availability-list__item { 125 | border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.08); 126 | padding: 2rem 0; 127 | } 128 | 129 | .pickup-availability-list__item:first-child { 130 | border-top: 0.1rem solid rgba(var(--color-foreground), 0.08); 131 | } 132 | 133 | .pickup-availability-list__item > * { 134 | margin: 0; 135 | } 136 | 137 | .pickup-availability-list__item > * + * { 138 | margin-top: 1rem; 139 | } 140 | 141 | .pickup-availability-address { 142 | font-style: normal; 143 | font-size: 1.2rem; 144 | line-height: calc(1 + 0.5 / var(--font-body-scale)); 145 | } 146 | 147 | .pickup-availability-address p { 148 | margin: 0; 149 | } 150 | 151 | @keyframes animateDrawerOpen { 152 | @media screen and (max-width: 749px) { 153 | 0% { 154 | opacity: 0; 155 | transform: translateX(100%); 156 | } 157 | 158 | 100% { 159 | opacity: 1; 160 | transform: translateX(0); 161 | } 162 | } 163 | 164 | @media screen and (min-width: 750px) { 165 | 0% { 166 | opacity: 0; 167 | transform: translateX(100%); 168 | } 169 | 170 | 100% { 171 | opacity: 1; 172 | transform: translateX(0); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /snippets/email-signup-banner-background.liquid: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /assets/product-info.js: -------------------------------------------------------------------------------- 1 | if (!customElements.get('product-info')) { 2 | customElements.define( 3 | 'product-info', 4 | class ProductInfo extends HTMLElement { 5 | constructor() { 6 | super(); 7 | this.input = this.querySelector('.quantity__input'); 8 | this.currentVariant = this.querySelector('.product-variant-id'); 9 | this.variantSelects = this.querySelector('variant-radios'); 10 | this.submitButton = this.querySelector('[type="submit"]'); 11 | } 12 | 13 | cartUpdateUnsubscriber = undefined; 14 | variantChangeUnsubscriber = undefined; 15 | 16 | connectedCallback() { 17 | if (!this.input) return; 18 | this.quantityForm = this.querySelector('.product-form__quantity'); 19 | if (!this.quantityForm) return; 20 | this.setQuantityBoundries(); 21 | if (!this.dataset.originalSection) { 22 | this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, this.fetchQuantityRules.bind(this)); 23 | } 24 | this.variantChangeUnsubscriber = subscribe(PUB_SUB_EVENTS.variantChange, (event) => { 25 | const sectionId = this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section; 26 | if (event.data.sectionId !== sectionId) return; 27 | this.updateQuantityRules(event.data.sectionId, event.data.html); 28 | this.setQuantityBoundries(); 29 | }); 30 | } 31 | 32 | disconnectedCallback() { 33 | if (this.cartUpdateUnsubscriber) { 34 | this.cartUpdateUnsubscriber(); 35 | } 36 | if (this.variantChangeUnsubscriber) { 37 | this.variantChangeUnsubscriber(); 38 | } 39 | } 40 | 41 | setQuantityBoundries() { 42 | const data = { 43 | cartQuantity: this.input.dataset.cartQuantity ? parseInt(this.input.dataset.cartQuantity) : 0, 44 | min: this.input.dataset.min ? parseInt(this.input.dataset.min) : 1, 45 | max: this.input.dataset.max ? parseInt(this.input.dataset.max) : null, 46 | step: this.input.step ? parseInt(this.input.step) : 1, 47 | }; 48 | 49 | let min = data.min; 50 | const max = data.max === null ? data.max : data.max - data.cartQuantity; 51 | if (max !== null) min = Math.min(min, max); 52 | if (data.cartQuantity >= data.min) min = Math.min(min, data.step); 53 | 54 | this.input.min = min; 55 | this.input.max = max; 56 | this.input.value = min; 57 | publish(PUB_SUB_EVENTS.quantityUpdate, undefined); 58 | } 59 | 60 | fetchQuantityRules() { 61 | if (!this.currentVariant || !this.currentVariant.value) return; 62 | this.querySelector('.quantity__rules-cart .loading-overlay').classList.remove('hidden'); 63 | fetch(`${this.dataset.url}?variant=${this.currentVariant.value}§ion_id=${this.dataset.section}`) 64 | .then((response) => { 65 | return response.text(); 66 | }) 67 | .then((responseText) => { 68 | const html = new DOMParser().parseFromString(responseText, 'text/html'); 69 | this.updateQuantityRules(this.dataset.section, html); 70 | this.setQuantityBoundries(); 71 | }) 72 | .catch((e) => { 73 | console.error(e); 74 | }) 75 | .finally(() => { 76 | this.querySelector('.quantity__rules-cart .loading-overlay').classList.add('hidden'); 77 | }); 78 | } 79 | 80 | updateQuantityRules(sectionId, html) { 81 | const quantityFormUpdated = html.getElementById(`Quantity-Form-${sectionId}`); 82 | const selectors = ['.quantity__input', '.quantity__rules', '.quantity__label']; 83 | for (let selector of selectors) { 84 | const current = this.quantityForm.querySelector(selector); 85 | const updated = quantityFormUpdated.querySelector(selector); 86 | if (!current || !updated) continue; 87 | if (selector === '.quantity__input') { 88 | const attributes = ['data-cart-quantity', 'data-min', 'data-max', 'step']; 89 | for (let attribute of attributes) { 90 | const valueUpdated = updated.getAttribute(attribute); 91 | if (valueUpdated !== null) current.setAttribute(attribute, valueUpdated); 92 | } 93 | } else { 94 | current.innerHTML = updated.innerHTML; 95 | } 96 | } 97 | } 98 | } 99 | ); 100 | } 101 | -------------------------------------------------------------------------------- /sections/pickup-availability.liquid: -------------------------------------------------------------------------------- 1 | {% comment %}theme-check-disable UndefinedObject{% endcomment %} 2 | {%- assign pick_up_availabilities = product_variant.store_availabilities | where: 'pick_up_enabled', true -%} 3 | 4 | {%- if pick_up_availabilities.size > 0 -%} 5 | 6 | {%- liquid 7 | assign closest_location = pick_up_availabilities.first 8 | 9 | if closest_location.available 10 | render 'icon-tick' 11 | endif 12 | -%} 13 | 14 |
15 | {%- if closest_location.available -%} 16 |

17 | {{ 18 | 'products.product.pickup_availability.pick_up_available_at_html' 19 | | t: location_name: closest_location.location.name 20 | }} 21 |

22 |

{{ closest_location.pick_up_time }}

23 | 34 | {%- else -%} 35 |

36 | {{ 37 | 'products.product.pickup_availability.pick_up_unavailable_at_html' 38 | | t: location_name: closest_location.location.name 39 | }} 40 |

41 | {%- if pick_up_availabilities.size > 1 -%} 42 | 49 | {%- endif -%} 50 | {%- endif -%} 51 |
52 |
53 | 54 | 61 |
62 |

63 | {{ product_variant.product.title | escape }} 64 |

65 | 68 |
69 | 70 | {%- unless product_variant.product.has_only_default_variant -%} 71 |

72 | {%- for product_option in product_variant.product.options_with_values -%} 73 | {{ product_option.name | escape }}:  74 | {%- for value in product_option.values -%} 75 | {%- if product_option.selected_value == value -%} 76 | {{ value | escape }} 77 | {%- endif -%} 78 | {%- endfor -%} 79 | {%- unless forloop.last -%}, {%- endunless -%} 80 | {%- endfor -%} 81 |

82 | {%- endunless -%} 83 | 84 | 107 |
108 | {%- endif -%} 109 | -------------------------------------------------------------------------------- /assets/component-cart.css: -------------------------------------------------------------------------------- 1 | .cart { 2 | position: relative; 3 | display: block; 4 | } 5 | 6 | .cart__empty-text, 7 | .is-empty .cart__contents, 8 | cart-items.is-empty .title-wrapper-with-link, 9 | .is-empty .cart__footer { 10 | display: none; 11 | } 12 | 13 | .is-empty .cart__empty-text, 14 | .is-empty .cart__warnings { 15 | display: block; 16 | } 17 | 18 | .cart__warnings { 19 | display: none; 20 | text-align: center; 21 | padding: 3rem 0 1rem; 22 | } 23 | 24 | .cart__empty-text { 25 | margin: 4.5rem 0 2rem; 26 | } 27 | 28 | .cart__contents > * + * { 29 | margin-top: 2.5rem; 30 | } 31 | 32 | .cart__login-title { 33 | margin: 5.5rem 0 0.5rem; 34 | } 35 | 36 | .cart__login-paragraph { 37 | margin-top: 0.8rem; 38 | } 39 | 40 | .cart__login-paragraph a { 41 | font-size: inherit; 42 | } 43 | 44 | @media screen and (min-width: 990px) { 45 | .cart__warnings { 46 | padding: 7rem 0 1rem; 47 | } 48 | 49 | .cart__empty-text { 50 | margin: 0 0 3rem; 51 | } 52 | } 53 | 54 | cart-items { 55 | display: block; 56 | } 57 | 58 | .cart__items { 59 | position: relative; 60 | padding-bottom: 3rem; 61 | border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.08); 62 | } 63 | 64 | .cart__items--disabled { 65 | pointer-events: none; 66 | } 67 | 68 | .cart__footer { 69 | padding: 4rem 0 0; 70 | } 71 | 72 | .cart__footer-wrapper:last-child .cart__footer { 73 | padding-bottom: 5rem; 74 | } 75 | 76 | .cart__footer > div:only-child { 77 | margin-left: auto; 78 | } 79 | 80 | .cart__footer > * + * { 81 | margin-top: 4rem; 82 | } 83 | 84 | .cart__footer .discounts { 85 | margin-top: 1rem; 86 | } 87 | 88 | .cart__note { 89 | height: fit-content; 90 | } 91 | 92 | .cart__note label { 93 | display: flex; 94 | align-items: flex-end; 95 | position: absolute; 96 | line-height: 1; 97 | height: 1.8rem; 98 | top: -3rem; 99 | color: rgba(var(--color-foreground), 0.75); 100 | } 101 | 102 | .cart__note .field__input { 103 | height: 100%; 104 | position: relative; 105 | border-radius: var(--inputs-radius); 106 | padding: 1rem 2rem; 107 | } 108 | 109 | .cart__note .text-area { 110 | resize: vertical; 111 | } 112 | 113 | .cart__note:after, 114 | .cart__note:hover.cart__note:after, 115 | .cart__note:before, 116 | .cart__note:hover.cart__note:before, 117 | .cart__note .field__input:focus, 118 | .cart__note .field__input { 119 | border-bottom-right-radius: 0; 120 | } 121 | 122 | @media screen and (min-width: 750px) { 123 | .cart__items { 124 | grid-column-start: 1; 125 | grid-column-end: 3; 126 | padding-bottom: 4rem; 127 | } 128 | 129 | .cart__contents > * + * { 130 | margin-top: 0; 131 | } 132 | 133 | .cart__items + .cart__footer { 134 | grid-column: 2; 135 | } 136 | 137 | .cart__footer { 138 | display: flex; 139 | justify-content: space-between; 140 | border: 0; 141 | } 142 | 143 | .cart__footer-wrapper:last-child { 144 | padding-top: 0; 145 | } 146 | 147 | .cart__footer > * { 148 | width: 35rem; 149 | } 150 | 151 | .cart__footer > * + * { 152 | margin-left: 4rem; 153 | margin-top: 0; 154 | } 155 | } 156 | 157 | .cart__ctas button { 158 | width: 100%; 159 | } 160 | 161 | .cart__ctas > *:not(noscript:first-child) + * { 162 | margin-top: 1rem; 163 | } 164 | 165 | .cart__update-button { 166 | margin-bottom: 1rem; 167 | } 168 | 169 | .cart__dynamic-checkout-buttons { 170 | max-width: 36rem; 171 | margin: 0 auto; 172 | } 173 | 174 | .cart__blocks > * + * { 175 | margin-top: 1rem; 176 | } 177 | 178 | .cart__dynamic-checkout-buttons div[role='button'] { 179 | border-radius: var(--buttons-radius-outset) !important; 180 | } 181 | 182 | .cart-note__label { 183 | display: inline-block; 184 | margin-bottom: 1rem; 185 | line-height: calc(1 + 1 / var(--font-body-scale)); 186 | } 187 | 188 | .tax-note { 189 | margin: 2.2rem 0 1.6rem auto; 190 | text-align: center; 191 | display: block; 192 | } 193 | 194 | .cart__checkout-button { 195 | max-width: 36rem; 196 | } 197 | 198 | .cart__ctas { 199 | text-align: center; 200 | } 201 | 202 | @media screen and (min-width: 750px) { 203 | .cart-note { 204 | max-width: 35rem; 205 | } 206 | 207 | .cart__update-button { 208 | margin-bottom: 0; 209 | margin-right: 0.8rem; 210 | } 211 | 212 | .tax-note { 213 | margin-bottom: 2.2rem; 214 | text-align: right; 215 | } 216 | 217 | [data-shopify-buttoncontainer] { 218 | justify-content: flex-end; 219 | } 220 | 221 | .cart__ctas { 222 | display: flex; 223 | gap: 1rem; 224 | } 225 | } 226 | --------------------------------------------------------------------------------