├── .babelrc ├── .gitignore ├── .theme-check.yml ├── .vscode └── extensions.json ├── build └── Dawn-1.0.0.zip ├── package.json ├── postcss.config.js ├── readme.md ├── rollup.config.js ├── src ├── LICENSE.md ├── README.md ├── assets │ ├── base.css │ ├── cart-notification.js │ ├── cart.js │ ├── collage.css │ ├── collection-filters-form.js │ ├── component-accordion.css │ ├── component-article-card.css │ ├── component-badge.css │ ├── component-card.css │ ├── component-cart-items.css │ ├── component-cart-notification.css │ ├── component-cart.css │ ├── component-collection-hero.css │ ├── component-deferred-media.css │ ├── component-discounts.css │ ├── component-image-with-text.css │ ├── component-list-menu.css │ ├── component-list-payment.css │ ├── component-list-social.css │ ├── component-loading-overlay.css │ ├── component-menu-drawer.css │ ├── component-model-viewer-ui.css │ ├── component-newsletter.css │ ├── component-pagination.css │ ├── component-pickup-availability.css │ ├── component-price.css │ ├── component-product-model.css │ ├── component-rte.css │ ├── component-search.css │ ├── component-slider.css │ ├── component-totals.css │ ├── customer.css │ ├── customer.js │ ├── details-disclosure.js │ ├── details-modal.js │ ├── disclosure.css │ ├── global.js │ ├── newsletter-section.css │ ├── password-modal.js │ ├── pickup-availability.js │ ├── product-form.js │ ├── product-model.js │ ├── section-blog-post.css │ ├── section-collection-list.css │ ├── section-contact-form.css │ ├── section-featured-blog.css │ ├── section-footer.css │ ├── section-image-banner.css │ ├── section-main-blog.css │ ├── section-main-page.css │ ├── section-main-product.css │ ├── section-multicolumn.css │ ├── section-password.css │ ├── section-product-recommendations.css │ ├── section-rich-text.css │ ├── share.js │ ├── slider.js │ ├── template-collection.css │ ├── template-giftcard.css │ └── variants.js ├── config │ ├── settings_data.json │ └── settings_schema.json ├── layout │ ├── password.liquid │ └── theme.liquid ├── locales │ ├── bg-BG.json │ ├── cs.json │ ├── cs.schema.json │ ├── da.json │ ├── da.schema.json │ ├── de.json │ ├── de.schema.json │ ├── el.json │ ├── en.default.json │ ├── en.default.schema.json │ ├── es.json │ ├── es.schema.json │ ├── fi.json │ ├── fi.schema.json │ ├── fr.json │ ├── fr.schema.json │ ├── hr-HR.json │ ├── hu.json │ ├── id.json │ ├── it.json │ ├── it.schema.json │ ├── ja.json │ ├── ja.schema.json │ ├── ko.json │ ├── ko.schema.json │ ├── lt-LT.json │ ├── nb.json │ ├── nb.schema.json │ ├── nl.json │ ├── nl.schema.json │ ├── pl.json │ ├── pl.schema.json │ ├── pt-BR.json │ ├── pt-BR.schema.json │ ├── pt-PT.json │ ├── pt-PT.schema.json │ ├── ro-RO.json │ ├── ru.json │ ├── sk-SK.json │ ├── sl-SI.json │ ├── sv.json │ ├── sv.schema.json │ ├── th.json │ ├── th.schema.json │ ├── tr.json │ ├── tr.schema.json │ ├── vi.json │ ├── vi.schema.json │ ├── zh-CN.json │ ├── zh-CN.schema.json │ ├── zh-TW.json │ └── zh-TW.schema.json ├── scripts │ ├── sections │ │ ├── header.js │ │ └── image-banner.js │ ├── theme.js │ └── utils │ │ └── operations.js ├── sections │ ├── announcement-bar.liquid │ ├── apps.liquid │ ├── cart-icon-bubble.liquid │ ├── cart-live-region-text.liquid │ ├── cart-notification-button.liquid │ ├── cart-notification-product.liquid │ ├── collage.liquid │ ├── collection-list.liquid │ ├── contact-form.liquid │ ├── custom-liquid.liquid │ ├── featured-blog.liquid │ ├── featured-collection.liquid │ ├── footer.liquid │ ├── header.liquid │ ├── image-banner.liquid │ ├── image-with-text.liquid │ ├── main-404.liquid │ ├── main-article.liquid │ ├── main-blog.liquid │ ├── main-cart-footer.liquid │ ├── main-cart-items.liquid │ ├── main-collection-banner.liquid │ ├── main-collection-product-grid.liquid │ ├── main-list-collections.liquid │ ├── main-page.liquid │ ├── main-password-footer.liquid │ ├── main-password-header.liquid │ ├── main-product.liquid │ ├── main-search.liquid │ ├── multicolumn.liquid │ ├── newsletter.liquid │ ├── page.liquid │ ├── pickup-availability.liquid │ ├── product-recommendations.liquid │ └── rich-text.liquid ├── snippets │ ├── article-card.liquid │ ├── cart-notification.liquid │ ├── icon-3d-model.liquid │ ├── icon-accordion.liquid │ ├── icon-account.liquid │ ├── icon-arrow.liquid │ ├── icon-caret.liquid │ ├── icon-cart-empty.liquid │ ├── icon-cart.liquid │ ├── icon-checkmark.liquid │ ├── icon-clipboard.liquid │ ├── icon-close-small.liquid │ ├── icon-close.liquid │ ├── icon-discount.liquid │ ├── icon-error.liquid │ ├── icon-facebook.liquid │ ├── icon-filter.liquid │ ├── icon-hamburger.liquid │ ├── icon-instagram.liquid │ ├── icon-minus.liquid │ ├── icon-padlock.liquid │ ├── icon-pinterest.liquid │ ├── icon-play.liquid │ ├── icon-plus.liquid │ ├── icon-remove.liquid │ ├── icon-share.liquid │ ├── icon-snapchat.liquid │ ├── icon-success.liquid │ ├── icon-tick.liquid │ ├── icon-tiktok.liquid │ ├── icon-tumblr.liquid │ ├── icon-twitter.liquid │ ├── icon-unavailable.liquid │ ├── icon-vimeo.liquid │ ├── icon-youtube.liquid │ ├── icon-zoom.liquid │ ├── meta-tags.liquid │ ├── pagination.liquid │ ├── price.liquid │ ├── product-card-placeholder.liquid │ ├── product-card.liquid │ ├── product-thumbnail.liquid │ └── social-sharing.liquid ├── styles │ ├── sections │ │ ├── header.css │ │ └── image-banner.css │ ├── theme.css │ └── utils │ │ ├── autocomplete.css │ │ └── base.css ├── templates │ ├── 404.json │ ├── article.json │ ├── blog.json │ ├── cart.json │ ├── collection.json │ ├── customers │ │ ├── account.liquid │ │ ├── activate_account.liquid │ │ ├── addresses.liquid │ │ ├── login.liquid │ │ ├── order.liquid │ │ ├── register.liquid │ │ └── reset_password.liquid │ ├── gift_card.liquid │ ├── index.json │ ├── list-collections.json │ ├── page.contact.json │ ├── page.json │ ├── password.json │ ├── product.json │ └── search.json └── translation.yml └── tailwind.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /node_modules 4 | dist/ 5 | yarn.lock -------------------------------------------------------------------------------- /.theme-check.yml: -------------------------------------------------------------------------------- 1 | # If your theme is not using the supported directory structure, provide the root path 2 | # where to find the `templates/`, `sections/`, `snippets/` directories as they would 3 | # be uploaded to Shopify. 4 | root: src -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "shopify.theme-check-vscode" 4 | ] 5 | } -------------------------------------------------------------------------------- /build/Dawn-1.0.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stylehatch/hatch/f3f366a5e2d075538804e14d26a5fffb7d792bd1/build/Dawn-1.0.0.zip -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hatch", 3 | "description": "Minimal Shopify OS2 theme build tools using Shopify CLI, Rollupjs and Tailwinds/Postcss", 4 | "version": "1.0.0", 5 | "main": "index.js", 6 | "author": { 7 | "name": "Jonathan Moore", 8 | "url": "https://github.com/jonathanmoore/" 9 | }, 10 | "license": "MIT", 11 | "browserslist": [ 12 | "last 2 Chrome versions" 13 | ], 14 | "scripts": { 15 | "clean": "rimraf dist && mkdir dist", 16 | "dev": "npm run clean && npm run build && npm-run-all --parallel dev:*", 17 | "dev:rollup": "NODE_ENV=development rollup -c --watch", 18 | "dev:postcss": "NODE_ENV=development TAILWIND_MODE=watch postcss src/styles/{theme.css,sections/*.css} --dir dist/assets --watch", 19 | "dev:shopify": "cd ./dist && shopify theme serve", 20 | "build": "npm-run-all --sequential build:*", 21 | "build:rollup": "NODE_ENV=production rollup -c", 22 | "build:postcss": "NODE_ENV=production postcss src/styles/{theme.css,sections/*.css} --dir dist/assets", 23 | "deploy": "npm-run-all --sequential deploy:*", 24 | "deploy:build": "npm-run-all --sequential build:*", 25 | "deploy:shopify": "cd ./dist && shopify theme push --development", 26 | "package": "npm-run-all --sequential package:*", 27 | "package:build": "npm-run-all --sequential build:*", 28 | "package:shopify": "cd ./dist && shopify theme package", 29 | "package:copy": "mkdir -p build && cpy 'dist/*.zip' build" 30 | }, 31 | "devDependencies": { 32 | "@babel/core": "^7.14.6", 33 | "@babel/preset-env": "^7.14.7", 34 | "@jonathanmoore/rollup-plugin-copy": "^3.5.1", 35 | "@rollup/plugin-babel": "^5.3.0", 36 | "@rollup/plugin-commonjs": "^19.0.0", 37 | "@rollup/plugin-eslint": "^8.0.1", 38 | "@rollup/plugin-node-resolve": "^13.0.0", 39 | "@rollup/plugin-replace": "^2.4.2", 40 | "@shopify/stylelint-plugin": "^10.1.2", 41 | "chokidar": "^3.5.2", 42 | "colorette": "^1.2.2", 43 | "cpy-cli": "^3.1.1", 44 | "eslint": "^7.29.0", 45 | "fs-extra": "^10.0.0", 46 | "globby": "^11.0.4", 47 | "is-plain-object": "^5.0.0", 48 | "npm-run-all": "^4.1.5", 49 | "postcss": "^8.3.5", 50 | "postcss-cli": "^8.3.1", 51 | "postcss-import": "^14.0.2", 52 | "postcss-nested": "^5.0.5", 53 | "prettier": "^2.3.2", 54 | "rollup": "^2.52.4", 55 | "rollup-plugin-multi-input": "^1.3.1", 56 | "stylelint": "^13.13.1", 57 | "tailwindcss": "^2.2.4" 58 | }, 59 | "dependencies": { 60 | "canvas-confetti": "^1.4.0" 61 | }, 62 | "stylelint": { 63 | "extends": [ 64 | "@shopify/stylelint-plugin/prettier" 65 | ] 66 | }, 67 | "prettier": { 68 | "singleQuote": true, 69 | "trailingComma": "es5", 70 | "bracketSpacing": false 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('tailwindcss'), 5 | require('postcss-nested'), 6 | // Example of how to exclude plugins by env 7 | // process.env.NODE_ENV === 'production' ? require('autoprefixer') : null, 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Hatch 2 | 3 | Hatch is a minimal tool to help you build Shopify Online Store 2.0 themes using the (Shopify CLI)[https://github.com/shopify/shopify-cli]. The focus of the build tools is to use the most lightweight, minimal tools for processing JavaScript and CSS file for the use in themes. 4 | 5 | The theme principals and concepts are following Shopify's Dawn theme with individual CSS and JavaScript files associated with each section. 6 | 7 | *Hatch is currently a work in progress and more of a working concept that we are using for our new Online Store 2.0 themes.* 8 | 9 | ## Tools 10 | 11 | Hatch uses the `/src` directory for theme development introducing new `/styles` and `/scripts` directories for processing CSS (PostCSS) files and JavaScript. When running `yarn dev` a new `/dist` directory will be created where all files will be watched and copied into the directory, PostCSS will run to process and copy CSS files into `/dist/assets`, and Rollup.js will bundle and transpile Javascript files into `/dist/assets`. 12 | 13 | (PostCSS)[https://github.com/postcss/postcss] and (Tailwindcss)[https://github.com/tailwindlabs/tailwindcss] JIT compiler is used for creating lean CSS files based on classes used throughout the JavaScript and Liquid files. *Configurations in `postcss.config.js` and `taiwind.config.js`* 14 | 15 | (Rollup.js)[https://github.com/rollup/rollup] is used to copy files, transpile JavaScript and bundle modules. Hatch uses the same evergreen, modern browser approach as Dawn to focus on widely supported ES6 JavaScript rather than adding extra bloat to support older browsers. *Configuration in `rollup.config.js`* 16 | 17 | #### JavaScript files 18 | 19 | `/src/scripts/theme.js` is the primary JavaScript file intended to be used in Layouts/theme.liquid. Each of the sections that require a JavaScript file will be located in `/src/scripts/[section-name].js`. 20 | 21 | #### CSS files 22 | 23 | `/src/scripts/theme.css` is the primary CSS file intended to be used in Layouts/theme.liquid. Each of the sections that require a CSS file will be located in `/src/scripts/[section-name].css`. 24 | 25 | #### Caveats 26 | 27 | Currently adding a new .js or .css file for sections `src/{scripts,styles}/sections` you need to restart `yarn dev` to include the new files in the build tools. 28 | 29 | ## Usage 30 | 31 | #### Installation 32 | 33 | ```bash 34 | # yarn 35 | yarn install 36 | ``` 37 | 38 | #### Development 39 | 40 | ```bash 41 | # yarn 42 | yarn dev 43 | ``` 44 | 45 | 1. Cleans up and creates the `/dist` directory 46 | 2. Runs Rollup.js, watches for file changes, processes JavaScript files and copies everything into `/src` 47 | 3. Runs Postcss and Tailwindcss JIT to create just-in-time compiled CSS files in `/src/assets` 48 | 4. Runs Shopify CLI `shopify theme serve` in the `/dist` folder 49 | 50 | #### Build 51 | 52 | ```bash 53 | # yarn 54 | yarn build 55 | ``` 56 | 57 | 1. Runs Rollup.js, processes JavaScript files and copies everything into `/src` 58 | 2. Runs Postcss and Tailwindcss to create lean CSS files in `/src/assets` 59 | 60 | #### Deploy 61 | 62 | ```bash 63 | # yarn 64 | yarn deploy 65 | ``` 66 | 67 | 1. Builds the theme (see above) 68 | 2. Runs Shopify CLI `shopify theme push --development` to push to a development unpublished theme 69 | 70 | #### Package 71 | 72 | ```bash 73 | # yarn 74 | yarn package 75 | ``` 76 | 77 | 1. Builds the theme (see above) 78 | 2. Runs Shopify CLI `shopify theme package` to create a versioned .zip file for the theme 79 | 3. Copies the versioned theme .zip file to `/build` 80 | 81 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import babel from '@rollup/plugin-babel'; 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import copy from '@jonathanmoore/rollup-plugin-copy'; 6 | import multiInput from 'rollup-plugin-multi-input'; 7 | 8 | export default { 9 | // Import multiples 10 | input: ['src/scripts/theme.js', 'src/scripts/sections/*.js'], 11 | output: { 12 | format: 'esm', 13 | dir: 'dist', 14 | }, 15 | plugins: [ 16 | multiInput({ 17 | relative: 'src/', 18 | transformOutputPath: (output, input) => `assets/${path.basename(output)}`, 19 | }), 20 | copy({ 21 | watch: process.env.NODE_ENV === 'development' ? [ 22 | 'src/assets/', 23 | 'src/config/', 24 | 'src/layout/', 25 | 'src/locales', 26 | 'src/sections/', 27 | 'src/snippets/', 28 | 'src/templates/', 29 | ] : null, 30 | targets: [ 31 | {src: 'src/assets/**/*', dest: 'dist/assets'}, 32 | {src: 'src/config/*.json', dest: 'dist/config'}, 33 | {src: 'src/layout/*.liquid', dest: 'dist/layout'}, 34 | {src: 'src/locales/*.json', dest: 'dist/locales'}, 35 | {src: 'src/sections/*.liquid', dest: 'dist/sections'}, 36 | {src: 'src/snippets/*.liquid', dest: 'dist/snippets'}, 37 | {src: 'src/templates/**/*', dest: 'dist/templates'}, 38 | ], 39 | verbose: true, 40 | copyOnce: true, 41 | }), 42 | resolve(), 43 | commonjs(), 44 | babel({ 45 | exclude: 'node_modules/**', 46 | babelHelpers: 'bundled', 47 | }), 48 | ], 49 | }; 50 | -------------------------------------------------------------------------------- /src/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-present Shopify Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, sell and/or create derivative works of the Software or any part thereof, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The rights granted above may only be exercised to develop themes that integrate or interoperate with Shopify software or services, and, if applicable, to distribute, offer for sale or otherwise make available any such themes via the Shopify Theme Store. All other uses of the Software are strictly prohibited. 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /src/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('transitionend', () => { 19 | this.notification.focus(); 20 | trapFocus(this.notification); 21 | }, { once: true }); 22 | 23 | document.body.addEventListener('click', this.onBodyClick); 24 | } 25 | 26 | close() { 27 | this.notification.classList.remove('active'); 28 | 29 | document.body.removeEventListener('click', this.onBodyClick); 30 | 31 | removeTrapFocus(this.activeElement); 32 | } 33 | 34 | renderContents(parsedState) { 35 | this.productId = parsedState.id; 36 | this.getSectionsToRender().forEach((section => { 37 | document.getElementById(section.id).innerHTML = 38 | this.getSectionInnerHTML(parsedState.sections[section.id], section.selector); 39 | })); 40 | 41 | this.header?.reveal(); 42 | this.open(); 43 | } 44 | 45 | getSectionsToRender() { 46 | return [ 47 | { 48 | id: 'cart-notification-product', 49 | selector: `#cart-notification-product-${this.productId}`, 50 | }, 51 | { 52 | id: 'cart-notification-button' 53 | }, 54 | { 55 | id: 'cart-icon-bubble' 56 | } 57 | ]; 58 | } 59 | 60 | getSectionInnerHTML(html, selector = '.shopify-section') { 61 | return new DOMParser() 62 | .parseFromString(html, 'text/html') 63 | .querySelector(selector).innerHTML; 64 | } 65 | 66 | handleBodyClick(evt) { 67 | const target = evt.target; 68 | if (target !== this.notification && !target.closest('cart-notification')) { 69 | const disclosure = target.closest('details-disclosure'); 70 | this.activeElement = disclosure ? disclosure.querySelector('summary') : null; 71 | this.close(); 72 | } 73 | } 74 | 75 | setActiveElement(element) { 76 | this.activeElement = element; 77 | } 78 | } 79 | 80 | customElements.define('cart-notification', CartNotification); 81 | -------------------------------------------------------------------------------- /src/assets/cart.js: -------------------------------------------------------------------------------- 1 | class CartRemoveButton extends HTMLElement { 2 | constructor() { 3 | super(); 4 | this.addEventListener('click', (event) => { 5 | event.preventDefault(); 6 | this.closest('cart-items').updateQuantity(this.dataset.index, 0); 7 | }); 8 | } 9 | } 10 | 11 | customElements.define('cart-remove-button', CartRemoveButton); 12 | 13 | class CartItems extends HTMLElement { 14 | constructor() { 15 | super(); 16 | 17 | this.lineItemStatusElement = document.getElementById('shopping-cart-line-item-status'); 18 | 19 | this.currentItemCount = Array.from(this.querySelectorAll('[name="updates[]"]')) 20 | .reduce((total, quantityInput) => total + parseInt(quantityInput.value), 0); 21 | 22 | this.debouncedOnChange = debounce((event) => { 23 | this.onChange(event); 24 | }, 300); 25 | 26 | this.addEventListener('change', this.debouncedOnChange.bind(this)); 27 | } 28 | 29 | onChange(event) { 30 | this.updateQuantity(event.target.dataset.index, event.target.value, document.activeElement.getAttribute('name')); 31 | } 32 | 33 | getSectionsToRender() { 34 | return [ 35 | { 36 | id: 'main-cart-items', 37 | section: document.getElementById('main-cart-items').dataset.id, 38 | selector: '.js-contents', 39 | }, 40 | { 41 | id: 'cart-icon-bubble', 42 | section: 'cart-icon-bubble', 43 | selector: '.shopify-section' 44 | }, 45 | { 46 | id: 'cart-live-region-text', 47 | section: 'cart-live-region-text', 48 | selector: '.shopify-section' 49 | }, 50 | { 51 | id: 'main-cart-footer', 52 | section: document.getElementById('main-cart-footer').dataset.id, 53 | selector: '.js-contents', 54 | } 55 | ]; 56 | } 57 | 58 | updateQuantity(line, quantity, name) { 59 | this.enableLoading(line); 60 | 61 | const body = JSON.stringify({ 62 | line, 63 | quantity, 64 | sections: this.getSectionsToRender().map((section) => section.section), 65 | sections_url: window.location.pathname 66 | }); 67 | 68 | fetch(`${routes.cart_change_url}`, {...fetchConfig(), ...{ body }}) 69 | .then((response) => { 70 | return response.text(); 71 | }) 72 | .then((state) => { 73 | const parsedState = JSON.parse(state); 74 | this.classList.toggle('is-empty', parsedState.item_count === 0); 75 | document.getElementById('main-cart-footer')?.classList.toggle('is-empty', parsedState.item_count === 0); 76 | 77 | this.getSectionsToRender().forEach((section => { 78 | const elementToReplace = 79 | document.getElementById(section.id).querySelector(section.selector) || document.getElementById(section.id); 80 | 81 | elementToReplace.innerHTML = 82 | this.getSectionInnerHTML(parsedState.sections[section.section], section.selector); 83 | })); 84 | 85 | this.updateLiveRegions(line, parsedState.item_count); 86 | document.getElementById(`CartItem-${line}`)?.querySelector(`[name="${name}"]`)?.focus(); 87 | this.disableLoading(); 88 | }).catch(() => { 89 | this.querySelectorAll('.loading-overlay').forEach((overlay) => overlay.classList.add('hidden')); 90 | document.getElementById('cart-errors').textContent = window.cartStrings.error; 91 | this.disableLoading(); 92 | }); 93 | } 94 | 95 | updateLiveRegions(line, itemCount) { 96 | if (this.currentItemCount === itemCount) { 97 | document.getElementById(`Line-item-error-${line}`) 98 | .querySelector('.cart-item__error-text') 99 | .innerHTML = window.cartStrings.quantityError.replace( 100 | '[quantity]', 101 | document.getElementById(`Quantity-${line}`).value 102 | ); 103 | } 104 | 105 | this.currentItemCount = itemCount; 106 | this.lineItemStatusElement.setAttribute('aria-hidden', true); 107 | 108 | const cartStatus = document.getElementById('cart-live-region-text'); 109 | cartStatus.setAttribute('aria-hidden', false); 110 | 111 | setTimeout(() => { 112 | cartStatus.setAttribute('aria-hidden', true); 113 | }, 1000); 114 | } 115 | 116 | getSectionInnerHTML(html, selector) { 117 | return new DOMParser() 118 | .parseFromString(html, 'text/html') 119 | .querySelector(selector).innerHTML; 120 | } 121 | 122 | enableLoading(line) { 123 | document.getElementById('main-cart-items').classList.add('cart__items--disabled'); 124 | this.querySelectorAll('.loading-overlay')[line - 1].classList.remove('hidden'); 125 | document.activeElement.blur(); 126 | this.lineItemStatusElement.setAttribute('aria-hidden', false); 127 | } 128 | 129 | disableLoading() { 130 | document.getElementById('main-cart-items').classList.remove('cart__items--disabled'); 131 | } 132 | } 133 | 134 | customElements.define('cart-items', CartItems); 135 | -------------------------------------------------------------------------------- /src/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 + .accordion { 14 | margin-top: 0; 15 | border-top: none; 16 | } 17 | 18 | .accordion { 19 | margin-top: 2.5rem; 20 | margin-bottom: 0; 21 | border-top: 0.1rem solid rgba(var(--color-foreground), 0.2); 22 | border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.2); 23 | } 24 | 25 | .accordion__title { 26 | display: inline-block; 27 | max-width: calc(100% - 6rem); 28 | min-height: 1.6rem; 29 | margin: 0; 30 | word-break: break-word; 31 | } 32 | 33 | .accordion .icon-accordion { 34 | align-self: center; 35 | min-width: 1.6rem; 36 | margin-right: 1rem; 37 | fill: rgb(var(--color-foreground)); 38 | } 39 | 40 | .accordion details[open] > summary .icon-caret { 41 | transform: rotate(180deg); 42 | } 43 | 44 | .accordion__content { 45 | margin-bottom: 1.5rem; 46 | word-break: break-word; 47 | } 48 | 49 | .accordion__content img { 50 | max-width: 100%; 51 | } 52 | -------------------------------------------------------------------------------- /src/assets/component-article-card.css: -------------------------------------------------------------------------------- 1 | .articles-wrapper.grid { 2 | margin: 0 0 5rem 0; 3 | } 4 | 5 | @media screen and (min-width: 750px) { 6 | .articles-wrapper.grid { 7 | margin-bottom: 7rem; 8 | } 9 | } 10 | 11 | .articles-wrapper .article { 12 | max-width: 100%; 13 | } 14 | 15 | @media screen and (max-width: 749px) { 16 | .articles-wrapper .article { 17 | width: 100%; 18 | } 19 | } 20 | 21 | .article { 22 | display: flex; 23 | align-items: center; 24 | } 25 | 26 | .article.grid__item { 27 | padding: 0; 28 | } 29 | 30 | .article-card { 31 | background-color: rgba(var(--color-foreground), 0.04); 32 | align-self: flex-start; 33 | flex: 0 1 100%; 34 | display: flex; 35 | align-items: flex-start; 36 | height: 100%; 37 | } 38 | 39 | .grid--peek .article-card { 40 | box-sizing: border-box; 41 | } 42 | 43 | .article-card__info { 44 | padding: 2.5rem 2.5rem 3rem; 45 | display: flex; 46 | flex-direction: column; 47 | flex-grow: 1; 48 | } 49 | 50 | @media screen and (min-width: 750px) { 51 | .article-card__info { 52 | padding: 4rem 5rem; 53 | } 54 | } 55 | 56 | .article-content { 57 | width: 100%; 58 | height: 100%; 59 | display: flex; 60 | flex-direction: column; 61 | text-decoration: none; 62 | color: inherit; 63 | } 64 | 65 | .article-content:hover .article-card__title { 66 | text-decoration: underline; 67 | text-underline-offset: 0.3rem; 68 | } 69 | 70 | .article-card__image { 71 | overflow: hidden; 72 | } 73 | 74 | .article-content img { 75 | transition: transform var(--duration-default) ease; 76 | } 77 | 78 | .article-content:hover img { 79 | transform: scale(1.07); 80 | } 81 | 82 | .article-card__image-wrapper > a { 83 | display: block; 84 | } 85 | 86 | .article-card__title { 87 | text-decoration: none; 88 | word-break: break-word; 89 | } 90 | 91 | .article-card__link.link { 92 | padding: 0; 93 | } 94 | 95 | .article-card__link { 96 | text-underline-offset: 0.3rem; 97 | } 98 | 99 | .article-content:hover .article-card__link { 100 | text-decoration-thickness: 0.2rem; 101 | } 102 | 103 | .article-card__header h2 { 104 | margin: 0; 105 | } 106 | 107 | .article-card__header h2:not(:first-child) { 108 | margin-top: 1rem; 109 | } 110 | 111 | .article-card__footer { 112 | letter-spacing: 0.1rem; 113 | font-size: 1.4rem; 114 | } 115 | 116 | .article-card__footer:not(:last-child) { 117 | margin-bottom: 1rem; 118 | } 119 | 120 | .article-card__footer:last-child { 121 | margin-top: auto; 122 | } 123 | 124 | .article-card__link:not(:only-child) { 125 | margin-right: 3rem; 126 | } 127 | 128 | @media screen and (min-width: 990px) { 129 | .article-card__link:not(:only-child) { 130 | margin-right: 4rem; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/assets/component-badge.css: -------------------------------------------------------------------------------- 1 | .badge { 2 | border: 1px solid transparent; 3 | border-radius: 4rem; 4 | display: inline-block; 5 | font-size: 1.2rem; 6 | letter-spacing: 0.1rem; 7 | line-height: 1; 8 | padding: 0.6rem 1.6rem; 9 | text-align: center; 10 | background-color: rgb(var(--color-badge-background)); 11 | border-color: rgba(var(--color-badge-border), var(--alpha-badge-border)); 12 | color: rgb(var(--color-foreground)); 13 | word-break: break-word; 14 | } 15 | -------------------------------------------------------------------------------- /src/assets/component-cart-items.css: -------------------------------------------------------------------------------- 1 | .cart-items td, 2 | .cart-items th { 3 | padding: 0; 4 | border: none; 5 | } 6 | 7 | .cart-items th { 8 | text-align: left; 9 | padding-bottom: 1.8rem; 10 | opacity: 0.85; 11 | font-weight: normal; 12 | } 13 | 14 | .cart-item__totals { 15 | position: relative; 16 | } 17 | 18 | .cart-items *.right { 19 | text-align: right; 20 | } 21 | 22 | .cart-item__image { 23 | max-width: 100%; 24 | } 25 | 26 | .cart-item__details { 27 | font-size: 1.6rem; 28 | line-height: 1.4; 29 | } 30 | 31 | .cart-item__details > * { 32 | margin: 0; 33 | max-width: 30rem; 34 | } 35 | 36 | .cart-item__details > * + * { 37 | margin-top: 0.8rem; 38 | } 39 | 40 | .cart-item__media { 41 | position: relative; 42 | } 43 | 44 | .cart-item__name { 45 | color: rgb(var(--color-foreground)); 46 | text-decoration: none; 47 | display: block; 48 | } 49 | 50 | .cart-item__name:hover { 51 | text-decoration: underline; 52 | text-underline-offset: 0.3rem; 53 | text-decoration-thickness: 0.2rem; 54 | } 55 | 56 | .cart-item__price-wrapper { 57 | margin: 0; 58 | } 59 | 60 | .cart-item__price-wrapper > * { 61 | display: block; 62 | margin: 0; 63 | padding: 0; 64 | } 65 | 66 | .cart-item__discounted-prices dd { 67 | margin: 0; 68 | } 69 | 70 | .cart-item__old-price { 71 | opacity: 0.7; 72 | } 73 | 74 | .product-option { 75 | font-size: 1.6rem; 76 | line-height: 1.5; 77 | } 78 | 79 | .cart-item cart-remove-button { 80 | display: inline-block; 81 | margin: 2rem 0 0 auto; 82 | } 83 | 84 | @media screen and (min-width: 750px) and (max-width: 989px) { 85 | .cart-item cart-remove-button { 86 | width: 4.5rem; 87 | height: 4.5rem; 88 | } 89 | } 90 | 91 | cart-remove-button .button { 92 | min-width: 4.5rem; 93 | min-height: 4.5rem; 94 | padding: 0; 95 | margin: 0 0.1rem 0.1rem 0; 96 | } 97 | 98 | @media screen and (min-width: 750px) { 99 | cart-remove-button .button { 100 | min-width: 3.5rem; 101 | min-height: 3.5rem; 102 | } 103 | } 104 | 105 | cart-remove-button .icon-remove { 106 | height: 1.5rem; 107 | width: 1.5rem; 108 | } 109 | 110 | .cart-item .loading-overlay { 111 | top: auto; 112 | left: auto; 113 | right: 0; 114 | bottom: 0; 115 | padding: 0; 116 | } 117 | 118 | @media screen and (min-width: 750px) { 119 | .cart-item .loading-overlay { 120 | top: 0; 121 | padding-top: 5.5rem; 122 | bottom: auto; 123 | } 124 | } 125 | 126 | .loading-overlay:not(.hidden) ~ * { 127 | visibility: hidden; 128 | } 129 | 130 | .cart-item__error { 131 | font-size: 1.2rem; 132 | display: flex; 133 | } 134 | 135 | .cart-item__error-text { 136 | order: 1; 137 | } 138 | 139 | .cart-item__error-text + svg { 140 | width: 1.2rem; 141 | margin-right: 0.7rem; 142 | } 143 | 144 | .cart-item__error-text:empty + svg { 145 | display: none; 146 | } 147 | 148 | .product-option { 149 | color: rgba(var(--color-foreground), 0.7); 150 | } 151 | 152 | .product-option + .product-option { 153 | margin-top: 0.4rem; 154 | } 155 | 156 | .product-option * { 157 | display: inline; 158 | margin: 0; 159 | } 160 | 161 | .cart-items thead th { 162 | text-transform: uppercase; 163 | } 164 | 165 | @media screen and (max-width: 749px) { 166 | .cart-items, 167 | .cart-items thead, 168 | .cart-items tbody { 169 | display: block; 170 | width: 100%; 171 | } 172 | 173 | .cart-items thead tr { 174 | display: flex; 175 | justify-content: space-between; 176 | border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.2); 177 | margin-bottom: 4rem; 178 | } 179 | 180 | .cart-items .medium-up { 181 | display: none; 182 | } 183 | 184 | .cart-item { 185 | display: grid; 186 | grid-template: repeat(2, auto) / repeat(4, 1fr); 187 | gap: 1.5rem; 188 | margin-bottom: 3.5rem; 189 | } 190 | 191 | .cart-item:last-child { 192 | margin-bottom: 0; 193 | } 194 | 195 | .cart-item__media { 196 | grid-row: 1 / 3; 197 | } 198 | 199 | .cart-item__details { 200 | grid-column: 2 / 4; 201 | } 202 | 203 | .cart-item__quantity { 204 | grid-column: 2 / 4; 205 | } 206 | 207 | .cart-item__totals { 208 | display: flex; 209 | align-items: flex-end; 210 | justify-content: flex-end; 211 | } 212 | } 213 | 214 | @media screen and (min-width: 750px) { 215 | .cart-items { 216 | border-spacing: 0; 217 | border-collapse: separate; 218 | box-shadow: none; 219 | width: 100%; 220 | display: table; 221 | } 222 | 223 | .cart-items th { 224 | border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.2); 225 | } 226 | 227 | .cart-items th + th { 228 | padding-left: 4rem; 229 | } 230 | 231 | .cart-items td { 232 | vertical-align: top; 233 | padding-top: 4rem; 234 | } 235 | 236 | .cart-item { 237 | display: table-row; 238 | } 239 | 240 | .cart-item > td + td { 241 | padding-left: 4rem; 242 | } 243 | 244 | .cart-item__media { 245 | width: 7.5rem; 246 | } 247 | 248 | .cart-item quantity-input { 249 | margin-top: 0.7rem; 250 | } 251 | 252 | .cart-items .medium-down { 253 | display: none; 254 | } 255 | } 256 | 257 | @media screen and (min-width: 990px) { 258 | .cart-item .cart-item__quantity, 259 | .cart-items .cart-items__heading--wide { 260 | padding-left: 10rem; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/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 | background-color: rgb(var(--color-background)); 11 | border-color: rgba(var(--color-foreground), 0.2); 12 | border-style: solid; 13 | border-width: 0 0 0.1rem; 14 | padding: 2.5rem 3.5rem; 15 | position: absolute; 16 | right: 0; 17 | transform: translateY(-100%); 18 | visibility: hidden; 19 | width: 100%; 20 | z-index: -1; 21 | } 22 | 23 | @media screen and (min-width: 750px) { 24 | .cart-notification { 25 | border-width: 0 0.1rem 0.1rem; 26 | max-width: 36.8rem; 27 | right: 4rem; 28 | } 29 | } 30 | 31 | .cart-notification.animate { 32 | transition: transform var(--duration-short) ease, 33 | visibility 0s var(--duration-short) ease; 34 | } 35 | 36 | .cart-notification.active { 37 | transform: translateY(0); 38 | transition: transform var(--duration-default) ease, visibility 0s; 39 | visibility: visible; 40 | } 41 | 42 | .cart-notification__header { 43 | align-items: flex-start; 44 | display: flex; 45 | } 46 | 47 | .cart-notification__heading { 48 | align-items: center; 49 | display: flex; 50 | flex-grow: 1; 51 | margin-bottom: 0; 52 | margin-top: 0; 53 | } 54 | 55 | .cart-notification__heading .icon-checkmark { 56 | color: rgb(var(--color-foreground)); 57 | margin-right: 1rem; 58 | width: 1.3rem; 59 | } 60 | 61 | .cart-notification__close { 62 | margin-top: -2rem; 63 | margin-right: -3rem; 64 | } 65 | 66 | .cart-notification__links { 67 | text-align: center; 68 | } 69 | 70 | .cart-notification__links > * { 71 | margin-top: 1rem; 72 | } 73 | 74 | .cart-notification-product { 75 | align-items: flex-start; 76 | display: flex; 77 | padding-bottom: 3rem; 78 | padding-top: 2rem; 79 | } 80 | 81 | .cart-notification-product dl { 82 | margin-bottom: 0; 83 | margin-top: 0; 84 | } 85 | 86 | .cart-notification-product__image { 87 | border: 0.1rem solid rgba(var(--color-foreground), 0.03); 88 | margin-right: 1.5rem; 89 | } 90 | 91 | .cart-notification-product__name { 92 | margin-bottom: 0; 93 | margin-top: 0; 94 | } 95 | 96 | .cart-notification-product__option { 97 | color: rgba(var(--color-foreground), 0.7); 98 | margin-top: 1rem; 99 | } 100 | 101 | .cart-notification-product__option + .cart-notification-product__option { 102 | margin-top: 0.5rem; 103 | } 104 | 105 | .cart-notification-product__option > * { 106 | display: inline-block; 107 | margin: 0; 108 | } 109 | -------------------------------------------------------------------------------- /src/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: 7rem 0; 22 | } 23 | 24 | .cart__empty-text { 25 | margin: 4.5rem 0 5.5rem; 26 | } 27 | 28 | .cart__contents > * + * { 29 | margin-top: 2.5rem; 30 | } 31 | 32 | @media screen and (min-width: 990px) { 33 | .cart__warnings { 34 | padding: 10rem 0 15rem; 35 | } 36 | 37 | .cart__empty-text { 38 | margin: 5rem 0 6rem; 39 | } 40 | } 41 | 42 | cart-items { 43 | display: block; 44 | } 45 | 46 | .cart__items { 47 | position: relative; 48 | padding-bottom: 3rem; 49 | border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.2); 50 | } 51 | 52 | .cart__items--disabled { 53 | pointer-events: none; 54 | } 55 | 56 | .cart__footer { 57 | padding: 4rem 0 0; 58 | } 59 | 60 | .cart__footer-wrapper:last-child .cart__footer { 61 | padding-bottom: 5rem; 62 | } 63 | 64 | .cart__footer > div:only-child { 65 | margin-left: auto; 66 | } 67 | 68 | .cart__footer > * + * { 69 | margin-top: 4rem; 70 | } 71 | 72 | .cart__footer .discounts { 73 | margin-top: 1rem; 74 | } 75 | 76 | .cart__note { 77 | display: block; 78 | } 79 | 80 | .cart__note label { 81 | display: flex; 82 | align-items: flex-end; 83 | line-height: 1; 84 | height: 1.8rem; 85 | margin-bottom: 2rem; 86 | color: rgba(var(--color-foreground), 0.75); 87 | } 88 | 89 | .cart__note .field__input { 90 | padding: 1rem; 91 | } 92 | 93 | @media screen and (min-width: 750px) { 94 | .cart__items { 95 | grid-column-start: 1; 96 | grid-column-end: 3; 97 | padding-bottom: 4rem; 98 | margin-bottom: 4rem; 99 | } 100 | 101 | .cart__contents > * + * { 102 | margin-top: 0; 103 | } 104 | 105 | .cart__items + .cart__footer { 106 | grid-column: 2; 107 | } 108 | 109 | .cart__footer { 110 | display: flex; 111 | justify-content: space-between; 112 | border: 0; 113 | } 114 | 115 | .cart__footer-wrapper:last-child { 116 | padding-top: 0; 117 | } 118 | 119 | .cart__footer > * { 120 | width: 35rem; 121 | } 122 | 123 | .cart__footer > * + * { 124 | margin-left: 4rem; 125 | margin-top: 0; 126 | } 127 | } 128 | 129 | .cart__ctas button { 130 | width: 100%; 131 | } 132 | 133 | .cart__ctas > *:not(noscript:first-child) + * { 134 | margin-top: 1rem; 135 | } 136 | 137 | .cart__update-button { 138 | margin-bottom: 1rem; 139 | } 140 | 141 | .cart__dynamic-checkout-buttons { 142 | max-width: 36rem; 143 | margin: 0 auto; 144 | } 145 | 146 | .cart__blocks > * + * { 147 | margin-top: 1rem; 148 | } 149 | 150 | .cart__dynamic-checkout-buttons div[role='button'] { 151 | border-radius: 0 !important; 152 | } 153 | 154 | .cart-note__label { 155 | display: inline-block; 156 | margin-bottom: 1rem; 157 | line-height: 2; 158 | } 159 | 160 | .tax-note { 161 | margin: 2.2rem 0 1.6rem auto; 162 | text-align: center; 163 | display: block; 164 | } 165 | 166 | .cart__checkout-button { 167 | max-width: 36rem; 168 | } 169 | 170 | .cart__ctas { 171 | text-align: center; 172 | } 173 | 174 | @media screen and (min-width: 750px) { 175 | .cart-note { 176 | max-width: 35rem; 177 | } 178 | 179 | .cart__update-button { 180 | margin-bottom: 0; 181 | margin-right: 0.8rem; 182 | } 183 | 184 | .tax-note { 185 | margin-bottom: 2.2rem; 186 | text-align: right; 187 | } 188 | 189 | [data-shopify-buttoncontainer] { 190 | justify-content: flex-end; 191 | } 192 | 193 | .cart__ctas { 194 | display: flex; 195 | gap: 1rem; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/assets/component-collection-hero.css: -------------------------------------------------------------------------------- 1 | .collection-hero { 2 | margin-bottom: 2rem; 3 | } 4 | 5 | .collection-hero--with-image { 6 | background-color: rgba(var(--color-foreground), 0.04); 7 | } 8 | 9 | .collection-hero__inner { 10 | display: flex; 11 | flex-direction: column; 12 | padding-bottom: 2rem; 13 | margin-bottom: 2rem; 14 | } 15 | 16 | @media screen and (min-width: 750px) { 17 | .collection-hero.collection-hero--with-image { 18 | padding: 4rem 0 4rem; 19 | } 20 | } 21 | 22 | .collection-hero__text-wrapper { 23 | flex-basis: 100%; 24 | } 25 | 26 | .collection-hero--with-image .collection-hero__inner { 27 | margin-bottom: 4rem; 28 | } 29 | 30 | @media screen and (min-width: 750px) { 31 | .collection-hero { 32 | padding: 0 0 2rem; 33 | margin-bottom: 0; 34 | } 35 | 36 | .collection-hero--with-image { 37 | margin-bottom: 4.5rem; 38 | } 39 | 40 | .collection-hero__inner { 41 | align-items: center; 42 | flex-direction: row; 43 | padding-bottom: 0; 44 | margin-bottom: 0; 45 | } 46 | 47 | .collection-hero--with-image .collection-hero__inner { 48 | margin-bottom: 0; 49 | } 50 | } 51 | 52 | .collection-hero__title { 53 | margin: 5rem 0 0; 54 | } 55 | 56 | .collection-hero__title + .collection-hero__description { 57 | margin-top: 1.5rem; 58 | font-size: 1.6rem; 59 | line-height: 1.5; 60 | } 61 | 62 | @media screen and (min-width: 750px) { 63 | .collection-hero__title + .collection-hero__description { 64 | font-size: 1.8rem; 65 | margin-top: 2rem; 66 | } 67 | 68 | .collection-hero__description { 69 | max-width: 66.67%; 70 | } 71 | 72 | .collection-hero--with-image .collection-hero__description { 73 | max-width: 100%; 74 | } 75 | } 76 | 77 | .collection-hero--with-image .collection-hero__title { 78 | margin: 0; 79 | } 80 | 81 | .collection-hero--with-image .collection-hero__text-wrapper { 82 | padding: 5rem 0 4rem; 83 | } 84 | 85 | @media screen and (max-width: 749px) { 86 | .collection-hero__image-container { 87 | height: 20rem; 88 | } 89 | } 90 | 91 | @media screen and (min-width: 750px) { 92 | .collection-hero--with-image .collection-hero__text-wrapper { 93 | padding: 4rem 2rem 4rem 0; 94 | flex-basis: 50%; 95 | } 96 | 97 | .collection-hero__image-container { 98 | align-self: stretch; 99 | flex: 1 0 50%; 100 | margin-left: 3rem; 101 | min-height: 20rem; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/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 | } 10 | 11 | .media > .deferred-media__poster { 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | } 16 | 17 | .deferred-media__poster img { 18 | width: auto; 19 | height: 100%; 20 | } 21 | 22 | .deferred-media { 23 | overflow: hidden; 24 | } 25 | 26 | .deferred-media:not([loaded]) template { 27 | z-index: -1; 28 | } 29 | 30 | .deferred-media[loaded] > .deferred-media__poster { 31 | display: none; 32 | } 33 | 34 | .deferred-media__poster:focus { 35 | outline-offset: -0.3rem; 36 | } 37 | 38 | .deferred-media__poster-button { 39 | background-color: rgb(var(--color-background)); 40 | border: 0.1rem solid rgba(var(--color-foreground), 0.1); 41 | border-radius: 50%; 42 | color: rgb(var(--color-foreground)); 43 | display: flex; 44 | align-items: center; 45 | justify-content: center; 46 | height: 6.2rem; 47 | width: 6.2rem; 48 | position: absolute; 49 | left: 50%; 50 | top: 50%; 51 | transform: translate(-50%, -50%) scale(1); 52 | transition: transform var(--duration-short) ease, color var(--duration-short) ease; 53 | z-index: 1; 54 | } 55 | 56 | .deferred-media__poster-button:hover { 57 | transform: translate(-50%, -50%) scale(1.1); 58 | } 59 | 60 | .deferred-media__poster-button .icon { 61 | width: 2rem; 62 | height: 2rem; 63 | } 64 | 65 | .deferred-media__poster-button .icon-play { 66 | margin-left: 0.2rem; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/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: 1.5; 9 | } 10 | 11 | .discounts__discount svg { 12 | color: rgba(var(--color-button), var(--alpha-button-background)); 13 | } 14 | 15 | .discounts__discount--end { 16 | justify-content: flex-end; 17 | } 18 | 19 | .discounts__discount > .icon { 20 | color: rgb(var(--color-foreground)); 21 | width: 1.2rem; 22 | height: 1.2rem; 23 | margin-right: 0.7rem; 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/component-image-with-text.css: -------------------------------------------------------------------------------- 1 | .image-with-text { 2 | margin-top: 5rem; 3 | } 4 | 5 | .image-with-text:not(.color-scheme-background-1) { 6 | margin-bottom: 5rem; 7 | } 8 | 9 | @media screen and (min-width: 750px) { 10 | .image-with-text { 11 | margin-bottom: 5rem; 12 | } 13 | } 14 | 15 | .image-with-text .grid { 16 | margin-left: 0; 17 | margin-bottom: 0; 18 | } 19 | 20 | .image-with-text__grid { 21 | overflow: hidden; 22 | } 23 | 24 | @media screen and (min-width: 750px) { 25 | .image-with-text__grid--reverse { 26 | flex-direction: row-reverse; 27 | } 28 | } 29 | 30 | .image-with-text__media { 31 | background-color: transparent; 32 | min-height: 100%; 33 | } 34 | 35 | .image-with-text__media--small { 36 | height: 19.4rem; 37 | } 38 | 39 | .image-with-text__media--large { 40 | height: 43.5rem; 41 | } 42 | 43 | @media screen and (min-width: 750px) { 44 | .image-with-text__media--small { 45 | height: 31.4rem; 46 | } 47 | 48 | .image-with-text__media--large { 49 | height: 69.5rem; 50 | } 51 | } 52 | 53 | .image-with-text__media--placeholder { 54 | background-color: rgba(var(--color-foreground), 0.04); 55 | position: relative; 56 | overflow: hidden; 57 | } 58 | 59 | .image-with-text__media--placeholder.image-with-text__media--adapt { 60 | height: 20rem; 61 | } 62 | 63 | @media screen and (min-width: 750px) { 64 | .image-with-text__media--placeholder.image-with-text__media--adapt { 65 | height: 30rem; 66 | } 67 | } 68 | 69 | .image-with-text__media--placeholder > svg { 70 | position: absolute; 71 | left: 50%; 72 | max-width: 80rem; 73 | top: 50%; 74 | transform: translate(-50%, -50%); 75 | width: 100%; 76 | fill: currentColor; 77 | } 78 | 79 | .image-with-text__content { 80 | display: flex; 81 | flex-direction: column; 82 | align-items: flex-start; 83 | height: 100%; 84 | justify-content: center; 85 | padding: 4rem 4rem 5rem; 86 | } 87 | 88 | @media screen and (min-width: 750px) { 89 | .image-with-text__grid--reverse .image-with-text__content { 90 | margin-left: auto; 91 | } 92 | } 93 | 94 | @media screen and (min-width: 990px) { 95 | .image-with-text__content { 96 | padding: 6rem 7rem 7rem; 97 | } 98 | } 99 | 100 | .image-with-text__content > * + * { 101 | margin-top: 2rem; 102 | } 103 | 104 | .image-with-text__content > .image-with-text__text:empty ~ a { 105 | margin-top: 2rem; 106 | } 107 | 108 | .image-with-text__content > :first-child:is(.image-with-text__heading) { 109 | margin-top: 0; 110 | } 111 | 112 | .image-with-text__content :last-child:is(.image-with-text__heading) { 113 | margin-bottom: 0; 114 | } 115 | 116 | .image-with-text__content .button + .image-with-text__text { 117 | margin-top: 2rem; 118 | } 119 | 120 | .image-with-text__content .image-with-text__text + .button { 121 | margin-top: 3rem; 122 | } 123 | 124 | .image-with-text__heading { 125 | margin-bottom: 0; 126 | } 127 | 128 | .image-with-text__text p { 129 | margin-top: 0; 130 | margin-bottom: 1rem; 131 | } 132 | -------------------------------------------------------------------------------- /src/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 | background-color: rgb(var(--color-background)); 11 | } 12 | 13 | .list-menu--disclosure:focus { 14 | outline: none; 15 | } 16 | 17 | .list-menu__item--active { 18 | text-decoration: underline; 19 | text-underline-offset: 0.3rem; 20 | } 21 | 22 | .list-menu--disclosure.localization-selector { 23 | max-height: 18rem; 24 | overflow: auto; 25 | width: 10rem; 26 | padding: 0.5rem; 27 | } 28 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/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.3rem; 22 | } 23 | 24 | .list-social__link:hover .icon { 25 | transform: scale(1.07); 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/component-loading-overlay.css: -------------------------------------------------------------------------------- 1 | .loading-overlay { 2 | position: absolute; 3 | z-index: 1; 4 | width: 3rem; 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: 3rem; 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 | @keyframes dash { 47 | 0% { 48 | stroke-dashoffset: 280; 49 | } 50 | 50% { 51 | stroke-dashoffset: 75; 52 | transform: rotate(135deg); 53 | } 54 | 100% { 55 | stroke-dashoffset: 280; 56 | transform: rotate(450deg); 57 | } 58 | } 59 | 60 | .loading-overlay:not(.hidden) + .cart-item__price-wrapper, 61 | .loading-overlay:not(.hidden) ~ cart-remove-button { 62 | opacity: 50%; 63 | } 64 | 65 | .loading-overlay:not(.hidden) ~ cart-remove-button { 66 | pointer-events: none; 67 | cursor: default; 68 | } 69 | -------------------------------------------------------------------------------- /src/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: .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 | 44 | -------------------------------------------------------------------------------- /src/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 | flex-direction: row; 13 | align-items: flex-start; 14 | margin: 0 auto; 15 | max-width: 50rem; 16 | } 17 | } 18 | 19 | .newsletter-form__field-wrapper { 20 | width: 100%; 21 | } 22 | 23 | .newsletter-form__message { 24 | justify-content: center; 25 | margin-bottom: 0; 26 | } 27 | 28 | .newsletter-form__message--success { 29 | margin-top: 2rem; 30 | } 31 | 32 | @media screen and (min-width: 750px) { 33 | .newsletter-form__message { 34 | justify-content: flex-start; 35 | } 36 | 37 | .newsletter-form__message--success { 38 | position: absolute; 39 | left: 0; 40 | bottom: -65%; 41 | } 42 | } 43 | 44 | .newsletter-form__button { 45 | margin-left: 1.4rem; 46 | } 47 | 48 | @media screen and (max-width: 989px) { 49 | .newsletter-form__button { 50 | width: 100%; 51 | margin: 1.4rem 0 0 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/assets/component-pagination.css: -------------------------------------------------------------------------------- 1 | .pagination-wrapper { 2 | margin-top: 4rem; 3 | margin-bottom: 7rem; 4 | } 5 | 6 | .pagination-wrapper-small { 7 | margin-top: 1rem; 8 | margin-bottom: 7rem; 9 | } 10 | 11 | @media screen and (min-width: 990px) { 12 | .pagination-wrapper { 13 | margin-top: 5rem; 14 | margin-bottom: 10rem; 15 | } 16 | } 17 | 18 | .pagination__list { 19 | display: flex; 20 | flex-wrap: wrap; 21 | justify-content: center; 22 | } 23 | 24 | .pagination__list > li { 25 | flex: 1 0 4.4rem; 26 | max-width: 4.4rem; 27 | } 28 | 29 | .pagination__list > li:not(:last-child) { 30 | margin-right: 1rem; 31 | } 32 | 33 | .pagination__item { 34 | color: rgb(var(--color-foreground)); 35 | display: inline-flex; 36 | justify-content: center; 37 | align-items: center; 38 | position: relative; 39 | height: 4.4rem; 40 | width: 100%; 41 | padding: 0; 42 | text-decoration: none; 43 | } 44 | 45 | .pagination__item:hover { 46 | color: rgb(var(--color-foreground)); 47 | } 48 | 49 | a.pagination__item:hover::after { 50 | height: 0.2rem; 51 | } 52 | 53 | .pagination__item .icon-caret { 54 | height: 0.6rem; 55 | } 56 | 57 | .pagination__item--current { 58 | font-weight: 600; 59 | } 60 | 61 | .pagination__item--current::after { 62 | height: 0.1rem; 63 | } 64 | 65 | .pagination__item--current::after, 66 | .pagination__item:hover::after { 67 | content: ''; 68 | display: block; 69 | width: 2rem; 70 | position: absolute; 71 | bottom: 8px; 72 | left: 50%; 73 | transform: translateX(-50%); 74 | background-color: currentColor; 75 | } 76 | 77 | .pagination__item--next .icon { 78 | margin-left: -0.2rem; 79 | transform: rotate(90deg); 80 | } 81 | 82 | .pagination__item--next:hover .icon { 83 | transform: rotate(90deg) scale(1.07); 84 | } 85 | 86 | .pagination__item--prev .icon { 87 | margin-right: -0.2rem; 88 | transform: rotate(-90deg); 89 | } 90 | 91 | .pagination__item--prev:hover .icon { 92 | transform: rotate(-90deg) scale(1.07); 93 | } 94 | 95 | .pagination__item-arrow { 96 | color: rgba(var(--color-foreground), 0.75); 97 | } 98 | 99 | .pagination__item-arrow:hover .icon { 100 | color: rgb(var(--color-foreground)); 101 | } 102 | 103 | .pagination__item-arrow:hover::after { 104 | display: none; 105 | } 106 | -------------------------------------------------------------------------------- /src/assets/component-pickup-availability.css: -------------------------------------------------------------------------------- 1 | pickup-availability { 2 | display: block; 3 | } 4 | 5 | pickup-availability[available] { 6 | min-height: 12rem; 7 | } 8 | 9 | .pickup-availability-preview { 10 | align-items: flex-start; 11 | display: flex; 12 | gap: 0.2rem; 13 | } 14 | 15 | @media screen and (min-width: 750px) { 16 | .pickup-availability-preview { 17 | padding: 0 2rem 0 0; 18 | } 19 | } 20 | 21 | .pickup-availability-preview .icon { 22 | flex-shrink: 0; 23 | height: 1.8rem; 24 | } 25 | 26 | .pickup-availability-preview .icon-unavailable { 27 | height: 1.6rem; 28 | margin-top: 0.1rem; 29 | } 30 | 31 | .pickup-availability-button { 32 | background-color: transparent; 33 | color: rgba(var(--color-foreground), 0.75); 34 | letter-spacing: 0.06rem; 35 | padding: 0 0 0.2rem; 36 | text-decoration: underline; 37 | } 38 | 39 | .pickup-availability-button:hover { 40 | color: rgb(var(--color-foreground)); 41 | } 42 | 43 | .pickup-availability-info * { 44 | margin: 0 0 0.6rem; 45 | } 46 | 47 | pickup-availability-drawer { 48 | background-color: rgb(var(--color-background)); 49 | border: 0.1rem solid rgba(var(--color-foreground), 0.2); 50 | height: 100%; 51 | opacity: 0; 52 | overflow-y: auto; 53 | padding: 2rem; 54 | position: fixed; 55 | top: 0; 56 | right: 0; 57 | z-index: 4; 58 | transition: opacity var(--duration-default) ease, 59 | transform var(--duration-default) ease; 60 | transform: translateX(100%); 61 | width: 100%; 62 | } 63 | 64 | pickup-availability-drawer[open] { 65 | transform: translateX(0); 66 | opacity: 1; 67 | } 68 | 69 | @media screen and (min-width: 750px) { 70 | pickup-availability-drawer { 71 | transform: translateX(100%); 72 | width: 37.5rem; 73 | } 74 | 75 | pickup-availability-drawer[open] { 76 | opacity: 1; 77 | transform: translateX(0); 78 | animation: animateDrawerOpen var(--duration-default) ease; 79 | } 80 | } 81 | 82 | .pickup-availability-header { 83 | align-items: flex-start; 84 | display: flex; 85 | justify-content: space-between; 86 | margin-bottom: 1.2rem; 87 | } 88 | 89 | .pickup-availability-drawer-title { 90 | margin: 0.5rem 0 0; 91 | } 92 | 93 | .pickup-availability-header .icon { 94 | width: 2rem; 95 | } 96 | 97 | .pickup-availability-drawer-button { 98 | background-color: transparent; 99 | border: none; 100 | color: rgb(var(--color-foreground)); 101 | cursor: pointer; 102 | display: block; 103 | height: 4.4rem; 104 | padding: 1.2rem; 105 | width: 4.4rem; 106 | } 107 | 108 | .pickup-availability-drawer-button:hover { 109 | color: rgba(var(--color-foreground), 0.75); 110 | } 111 | 112 | .pickup-availability-variant { 113 | font-size: 1.3rem; 114 | line-height: 1.2; 115 | margin: 0 0 1.2rem; 116 | text-transform: capitalize; 117 | } 118 | 119 | .pickup-availability-variant > * + strong { 120 | margin-left: 1rem; 121 | } 122 | 123 | .pickup-availability-list__item { 124 | border-bottom: 0.1rem solid rgba(var(--color-foreground), 0.2); 125 | padding: 2rem 0; 126 | } 127 | 128 | .pickup-availability-list__item:first-child { 129 | border-top: 0.1rem solid rgba(var(--color-foreground), 0.2); 130 | } 131 | 132 | .pickup-availability-list__item > * { 133 | margin: 0; 134 | } 135 | 136 | .pickup-availability-list__item > * + * { 137 | margin-top: 1rem; 138 | } 139 | 140 | .pickup-availability-address { 141 | font-style: normal; 142 | font-size: 1.2rem; 143 | line-height: 1.5; 144 | } 145 | 146 | .pickup-availability-address p { 147 | margin: 0; 148 | } 149 | 150 | @keyframes animateDrawerOpen { 151 | @media screen and (max-width: 749px) { 152 | 0% { 153 | opacity: 0; 154 | transform: translateX(100%); 155 | } 156 | 157 | 100% { 158 | opacity: 1; 159 | transform: translateX(0); 160 | } 161 | } 162 | 163 | @media screen and (min-width: 750px) { 164 | 0% { 165 | opacity: 0; 166 | transform: translateX(100%); 167 | } 168 | 169 | 100% { 170 | opacity: 1; 171 | transform: translateX(0); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/assets/component-price.css: -------------------------------------------------------------------------------- 1 | .price { 2 | align-items: center; 3 | display: flex; 4 | flex-direction: row; 5 | flex-wrap: wrap; 6 | font-size: 1.6rem; 7 | letter-spacing: 0.1rem; 8 | line-height: 1.5; 9 | color: rgb(var(--color-foreground)); 10 | } 11 | 12 | .price.price--unavailable { 13 | visibility: hidden; 14 | } 15 | 16 | .price--end { 17 | justify-content: flex-end; 18 | } 19 | 20 | .price dl { 21 | margin: 0; 22 | display: flex; 23 | flex-direction: column; 24 | } 25 | 26 | .price dd { 27 | margin: 0 1rem 0 0; 28 | } 29 | 30 | .price .price__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: 1.5; 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 | .price--no-compare .price__compare { 64 | display: none; 65 | } 66 | 67 | .price--sold-out .price__badge-sold-out, 68 | .price--on-sale .price__badge-sale { 69 | display: inline-flex; 70 | } 71 | 72 | .price--on-sale .price__sale { 73 | display: flex; 74 | flex-direction: row; 75 | flex-wrap: wrap; 76 | } 77 | 78 | .price--center { 79 | display: flex; 80 | justify-content: center; 81 | } 82 | 83 | .price--on-sale .price-item--regular { 84 | text-decoration: line-through; 85 | color: rgba(var(--color-foreground), 0.75); 86 | } 87 | 88 | .unit-price { 89 | font-size: 1.1rem; 90 | letter-spacing: 0.04rem; 91 | line-height: 1.2; 92 | margin-top: 0.2rem; 93 | text-transform: uppercase; 94 | color: rgba(var(--color-foreground), 0.7); 95 | } 96 | -------------------------------------------------------------------------------- /src/assets/component-product-model.css: -------------------------------------------------------------------------------- 1 | .button.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 | } 7 | 8 | .button.product__xr-button:hover { 9 | box-shadow: none; 10 | } 11 | 12 | .product__xr-button[data-shopify-xr-hidden] { 13 | visibility: hidden; 14 | } 15 | 16 | @media screen and (max-width: 749px) { 17 | slider-component .product__xr-button:not([data-shopify-xr-hidden]) { 18 | display: none; 19 | } 20 | 21 | .active .product__xr-button:not([data-shopify-xr-hidden]) { 22 | display: block; 23 | } 24 | } 25 | 26 | @media screen and (min-width: 750px) { 27 | .product__media-wrapper > .button.product__xr-button { 28 | display: none; 29 | } 30 | 31 | .product__xr-button[data-shopify-xr-hidden] { 32 | display: none; 33 | } 34 | } 35 | 36 | .product__xr-button .icon { 37 | width: 1.4rem; 38 | margin-right: 1rem; 39 | } 40 | -------------------------------------------------------------------------------- /src/assets/component-rte.css: -------------------------------------------------------------------------------- 1 | .rte > p:first-child { 2 | margin-top: 0; 3 | } 4 | 5 | .rte > p:last-child { 6 | margin-bottom: 0; 7 | } 8 | 9 | .rte table { 10 | table-layout: fixed; 11 | } 12 | 13 | @media screen and (min-width: 750px) { 14 | .rte table td { 15 | padding-left: 1.2rem; 16 | padding-right: 1.2rem; 17 | } 18 | } 19 | 20 | .rte img { 21 | height: auto; 22 | max-width: 100%; 23 | } 24 | 25 | .rte ul { 26 | padding-left: 2rem; 27 | } 28 | 29 | .rte li { 30 | list-style: inherit; 31 | } 32 | 33 | .rte li:last-child { 34 | margin-bottom: 0; 35 | } 36 | 37 | .rte a { 38 | color: rgba(var(--color-link), var(--alpha-link)); 39 | text-underline-offset: 0.3rem; 40 | text-decoration-thickness: 0.1rem; 41 | transition: text-decoration-thickness var(--duration-short) ease; 42 | } 43 | 44 | .rte a:hover { 45 | color: rgb(var(--color-link)); 46 | text-decoration-thickness: 0.2rem; 47 | } 48 | 49 | .rte blockquote { 50 | display: inline-flex; 51 | } 52 | 53 | .rte blockquote > * { 54 | margin: -0.5rem 0 -0.5rem 0; 55 | } 56 | -------------------------------------------------------------------------------- /src/assets/component-search.css: -------------------------------------------------------------------------------- 1 | .search__input.field__input { 2 | padding-right: 5rem; 3 | } 4 | 5 | .search__button .icon { 6 | height: 1.8rem; 7 | } 8 | 9 | /* Remove extra spacing for search inputs in Safari */ 10 | input::-webkit-search-decoration { 11 | -webkit-appearance: none; 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/component-slider.css: -------------------------------------------------------------------------------- 1 | slider-component { 2 | position: relative; 3 | display: block; 4 | } 5 | 6 | @media screen and (max-width: 989px) { 7 | slider-component .slider { 8 | padding-bottom: 6rem; 9 | } 10 | 11 | .no-js slider-component .slider { 12 | padding-bottom: 3rem; 13 | } 14 | } 15 | 16 | .slider__slide { 17 | scroll-snap-align: start; 18 | flex-shrink: 0; 19 | } 20 | 21 | @media screen and (max-width: 749px) { 22 | .slider.slider--mobile { 23 | position: relative; 24 | flex-wrap: inherit; 25 | overflow-x: auto; 26 | scroll-snap-type: x mandatory; 27 | scroll-behavior: smooth; 28 | scroll-padding-left: 1rem; 29 | -webkit-overflow-scrolling: touch; 30 | } 31 | 32 | .slider.slider--mobile .slider__slide { 33 | margin-bottom: 0; 34 | padding-bottom: 0; 35 | } 36 | } 37 | 38 | @media screen and (max-width: 989px) { 39 | .slider.slider--tablet { 40 | position: relative; 41 | flex-wrap: inherit; 42 | overflow-x: auto; 43 | scroll-snap-type: x mandatory; 44 | scroll-behavior: smooth; 45 | scroll-padding-left: 1rem; 46 | -webkit-overflow-scrolling: touch; 47 | } 48 | 49 | .slider.slider--tablet .slider__slide { 50 | margin-bottom: 0; 51 | padding-bottom: 0; 52 | } 53 | } 54 | 55 | /* Scrollbar */ 56 | 57 | .slider { 58 | scrollbar-color: rgb(var(--color-foreground)) rgba(var(--color-foreground), 0.04); 59 | -ms-overflow-style: none; 60 | scrollbar-width: none; 61 | } 62 | 63 | .slider::-webkit-scrollbar { 64 | height: 0.4rem; 65 | width: 0.4rem; 66 | display: none; 67 | } 68 | 69 | .no-js .slider { 70 | -ms-overflow-style: auto; 71 | scrollbar-width: auto; 72 | } 73 | 74 | .no-js .slider::-webkit-scrollbar { 75 | display: initial; 76 | } 77 | 78 | .slider::-webkit-scrollbar-thumb { 79 | background-color: rgb(var(--color-foreground)); 80 | border-radius: 0.4rem; 81 | border: 0; 82 | } 83 | 84 | .slider::-webkit-scrollbar-track { 85 | background: rgba(var(--color-foreground), 0.04); 86 | border-radius: 0.4rem; 87 | } 88 | 89 | slider-component .slider-buttons { 90 | position: absolute; 91 | z-index: 2; 92 | right: 0; 93 | bottom: 0; 94 | } 95 | 96 | .slider-buttons.slider-buttons--overlay { 97 | border: 0.1rem solid rgba(var(--color-foreground), 0.08); 98 | background-color: rgb(var(--color-background)); 99 | } 100 | 101 | .slider-mobile-gutter .slider-buttons { 102 | right: 1.5rem; 103 | } 104 | 105 | .slider-counter { 106 | margin-right: 3rem; 107 | } 108 | 109 | .slider-buttons--overlay .slider-counter { 110 | margin-right: 0; 111 | padding: 0 1.4rem; 112 | } 113 | 114 | .slider-buttons { 115 | display: flex; 116 | align-items: center; 117 | } 118 | 119 | @media screen and (min-width: 990px) { 120 | .slider-buttons { 121 | display: none; 122 | } 123 | } 124 | 125 | @media screen and (min-width: 750px) { 126 | .slider--mobile + .slider-buttons { 127 | display: none; 128 | } 129 | } 130 | 131 | .slider-button { 132 | color: rgba(var(--color-foreground), 0.75); 133 | border: 0.1rem solid rgba(var(--color-foreground), 0.08); 134 | background-color: rgb(var(--color-background)); 135 | cursor: pointer; 136 | width: 44px; 137 | height: 44px; 138 | } 139 | 140 | .slider-button:not([disabled]):hover { 141 | color: rgb(var(--color-foreground)); 142 | border-color: rgb(var(--color-foreground)); 143 | z-index: 1; 144 | } 145 | 146 | .slider-button:first-of-type { 147 | margin-right: -1px; 148 | } 149 | 150 | .slider-buttons--overlay .slider-button { 151 | margin-top: -1px; 152 | margin-bottom: -1px; 153 | } 154 | 155 | .slider-buttons--overlay .slider-button + .slider-button { 156 | margin-right: -1px; 157 | } 158 | 159 | .slider-button .icon { 160 | height: 0.6rem; 161 | } 162 | 163 | .slider-button[disabled] .icon { 164 | color: rgba(var(--color-foreground), 0.3); 165 | } 166 | 167 | .slider-button--next .icon { 168 | margin-right: -0.2rem; 169 | transform: rotate(-90deg) translateX(0.15rem); 170 | } 171 | 172 | .slider-button--prev .icon { 173 | margin-left: -0.2rem; 174 | transform: rotate(90deg) translateX(-0.15rem); 175 | } 176 | 177 | .slider-button--next:not([disabled]):hover .icon { 178 | transform: rotate(-90deg) translateX(0.15rem) scale(1.07); 179 | } 180 | 181 | .slider-button--prev:not([disabled]):hover .icon { 182 | transform: rotate(90deg) translateX(-0.15rem) scale(1.07); 183 | } 184 | 185 | .slider-button:focus-visible { 186 | z-index: 1; 187 | } 188 | -------------------------------------------------------------------------------- /src/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 * { 13 | line-height: 1; 14 | } 15 | 16 | .totals > * + * { 17 | margin-left: 2rem; 18 | } 19 | 20 | .totals__subtotal-value { 21 | font-size: 1.8rem; 22 | } 23 | 24 | .cart__ctas + .totals { 25 | margin-top: 2rem; 26 | } 27 | 28 | @media all and (min-width: 750px) { 29 | .totals { 30 | justify-content: flex-end; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/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 | container, 27 | addressContainer: container.querySelector(selectors.addressContainer), 28 | toggleButtons: document.querySelectorAll(selectors.toggleAddressButton), 29 | cancelButtons: container.querySelectorAll(selectors.cancelAddressButton), 30 | deleteButtons: container.querySelectorAll(selectors.deleteAddressButton), 31 | countrySelects: container.querySelectorAll(selectors.addressCountrySelect) 32 | } : {}; 33 | } 34 | 35 | _setupCountries() { 36 | if (Shopify && Shopify.CountryProvinceSelector) { 37 | // eslint-disable-next-line no-new 38 | new Shopify.CountryProvinceSelector('AddressCountryNew', 'AddressProvinceNew', { 39 | hideElement: 'AddressProvinceContainerNew' 40 | }); 41 | this.elements.countrySelects.forEach((select) => { 42 | const formId = select.dataset.formId; 43 | // eslint-disable-next-line no-new 44 | new Shopify.CountryProvinceSelector(`AddressCountry_${formId}`, `AddressProvince_${formId}`, { 45 | hideElement: `AddressProvinceContainer_${formId}` 46 | }); 47 | }); 48 | } 49 | } 50 | 51 | _setupEventListeners() { 52 | this.elements.toggleButtons.forEach((element) => { 53 | element.addEventListener('click', this._handleAddEditButtonClick); 54 | }); 55 | this.elements.cancelButtons.forEach((element) => { 56 | element.addEventListener('click', this._handleCancelButtonClick); 57 | }); 58 | this.elements.deleteButtons.forEach((element) => { 59 | element.addEventListener('click', this._handleDeleteButtonClick); 60 | }); 61 | } 62 | 63 | _toggleExpanded(target) { 64 | target.setAttribute( 65 | attributes.expanded, 66 | (target.getAttribute(attributes.expanded) === 'false').toString() 67 | ); 68 | } 69 | 70 | _handleAddEditButtonClick = ({ currentTarget }) => { 71 | this._toggleExpanded(currentTarget); 72 | } 73 | 74 | _handleCancelButtonClick = ({ currentTarget }) => { 75 | this._toggleExpanded( 76 | currentTarget 77 | .closest(selectors.addressContainer) 78 | .querySelector(`[${attributes.expanded}]`) 79 | ) 80 | } 81 | 82 | _handleDeleteButtonClick = ({ currentTarget }) => { 83 | // eslint-disable-next-line no-alert 84 | if (confirm(currentTarget.getAttribute(attributes.confirmMessage))) { 85 | Shopify.postLink(currentTarget.dataset.target, { 86 | parameters: { _method: 'delete' }, 87 | }); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/assets/details-disclosure.js: -------------------------------------------------------------------------------- 1 | class DetailsDisclosure extends HTMLElement { 2 | constructor() { 3 | super(); 4 | this.mainDetailsToggle = this.querySelector('details'); 5 | 6 | this.addEventListener('keyup', this.onKeyUp); 7 | this.mainDetailsToggle.addEventListener('focusout', this.onFocusOut.bind(this)); 8 | } 9 | 10 | onKeyUp(event) { 11 | if(event.code.toUpperCase() !== 'ESCAPE') return; 12 | 13 | const openDetailsElement = event.target.closest('details[open]'); 14 | if (!openDetailsElement) return; 15 | 16 | const summaryElement = openDetailsElement.querySelector('summary'); 17 | openDetailsElement.removeAttribute('open'); 18 | summaryElement.focus(); 19 | } 20 | 21 | onFocusOut() { 22 | setTimeout(() => { 23 | if (!this.contains(document.activeElement)) this.close(); 24 | }) 25 | } 26 | 27 | close() { 28 | this.mainDetailsToggle.removeAttribute('open') 29 | } 30 | } 31 | 32 | customElements.define('details-disclosure', DetailsDisclosure); 33 | -------------------------------------------------------------------------------- /src/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( 8 | 'keyup', 9 | (event) => event.code.toUpperCase() === 'ESCAPE' && this.close() 10 | ); 11 | this.summaryToggle.addEventListener( 12 | 'click', 13 | this.onSummaryClick.bind(this) 14 | ); 15 | this.querySelector('button[type="button"]').addEventListener( 16 | 'click', 17 | this.close.bind(this) 18 | ); 19 | 20 | this.summaryToggle.setAttribute('role', 'button'); 21 | this.summaryToggle.setAttribute('aria-expanded', 'false'); 22 | } 23 | 24 | isOpen() { 25 | return this.detailsContainer.hasAttribute('open'); 26 | } 27 | 28 | onSummaryClick(event) { 29 | event.preventDefault(); 30 | event.target.closest('details').hasAttribute('open') 31 | ? this.close() 32 | : this.open(event); 33 | } 34 | 35 | onBodyClick(event) { 36 | if (!this.contains(event.target)) this.close(false); 37 | } 38 | 39 | open(event) { 40 | this.onBodyClickEvent = 41 | this.onBodyClickEvent || this.onBodyClick.bind(this); 42 | event.target.closest('details').setAttribute('open', true); 43 | document.body.addEventListener('click', this.onBodyClickEvent); 44 | 45 | trapFocus( 46 | this.detailsContainer.querySelector('[tabindex="-1"]'), 47 | this.detailsContainer.querySelector('input:not([type="hidden"])') 48 | ); 49 | } 50 | 51 | close(focusToggle = true) { 52 | removeTrapFocus(focusToggle ? this.summaryToggle : null); 53 | this.detailsContainer.removeAttribute('open'); 54 | document.body.removeEventListener('click', this.onBodyClickEvent); 55 | } 56 | } 57 | 58 | customElements.define('details-modal', DetailsModal); 59 | -------------------------------------------------------------------------------- /src/assets/disclosure.css: -------------------------------------------------------------------------------- 1 | .disclosure { 2 | position: relative; 3 | } 4 | 5 | .disclosure__button { 6 | align-items: center; 7 | cursor: pointer; 8 | display: flex; 9 | height: 4rem; 10 | padding: 0 1.5rem 0 1.5rem; 11 | font-size: 1.3rem; 12 | background-color: transparent; 13 | } 14 | 15 | .disclosure__list { 16 | border: 1px solid rgba(var(--color-foreground), 0.2); 17 | font-size: 1.4rem; 18 | margin-top: -0.5rem; 19 | min-height: 8.2rem; 20 | max-height: 19rem; 21 | max-width: 22rem; 22 | min-width: 12rem; 23 | width: max-content; 24 | overflow-y: auto; 25 | padding-bottom: 0.5rem; 26 | padding-top: 0.5rem; 27 | position: absolute; 28 | bottom: 100%; 29 | transform: translateY(-1rem); 30 | z-index: 2; 31 | background-color: rgb(var(--color-background)); 32 | } 33 | 34 | .disclosure__item { 35 | position: relative; 36 | } 37 | 38 | .disclosure__link { 39 | display: block; 40 | padding: 0.5rem 2.2rem; 41 | text-decoration: none; 42 | line-height: 1.8; 43 | } 44 | -------------------------------------------------------------------------------- /src/assets/newsletter-section.css: -------------------------------------------------------------------------------- 1 | .newsletter--narrow .newsletter__wrapper, 2 | .newsletter:not(.newsletter--narrow) .newsletter__wrapper.color-background-1 { 3 | margin-top: 5rem; 4 | margin-bottom: 5rem; 5 | } 6 | 7 | .newsletter__wrapper:not(.color-background-1) { 8 | padding-top: 5rem; 9 | padding-bottom: 5rem; 10 | } 11 | 12 | .newsletter__wrapper { 13 | padding-right: 4rem; 14 | padding-left: 4rem; 15 | } 16 | 17 | @media screen and (min-width: 750px) { 18 | .newsletter__wrapper { 19 | padding-right: 9rem; 20 | padding-left: 9rem; 21 | } 22 | } 23 | 24 | .newsletter__wrapper > * { 25 | margin-top: 0; 26 | margin-bottom: 0; 27 | } 28 | 29 | .newsletter__wrapper > * + * { 30 | margin-top: 2rem; 31 | } 32 | 33 | .newsletter__wrapper > * + .newsletter-form { 34 | margin-top: 3rem; 35 | } 36 | 37 | .newsletter__subheading { 38 | max-width: 70rem; 39 | margin-left: auto; 40 | margin-right: auto; 41 | } 42 | 43 | .newsletter__wrapper .newsletter-form__field-wrapper { 44 | max-width: 36rem; 45 | } 46 | 47 | .newsletter-form__field-wrapper .newsletter-form__message { 48 | margin-top: 1.5rem; 49 | } 50 | 51 | .newsletter__button { 52 | margin-top: 3rem; 53 | width: fit-content; 54 | } 55 | 56 | @media screen and (min-width: 750px) { 57 | .newsletter__button { 58 | flex-shrink: 0; 59 | margin: 0 0 0 1rem; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/assets/pickup-availability.js: -------------------------------------------------------------------------------- 1 | class PickupAvailability extends HTMLElement { 2 | constructor() { 3 | super(); 4 | 5 | if(!this.hasAttribute('available')) return; 6 | 7 | this.errorHtml = this.querySelector('template').content.firstElementChild.cloneNode(true); 8 | this.onClickRefreshList = this.onClickRefreshList.bind(this); 9 | this.fetchAvailability(this.dataset.variantId); 10 | } 11 | 12 | fetchAvailability(variantId) { 13 | const variantSectionUrl = `${this.dataset.baseUrl}variants/${variantId}/?section_id=pickup-availability`; 14 | 15 | fetch(variantSectionUrl) 16 | .then(response => response.text()) 17 | .then(text => { 18 | const sectionInnerHTML = new DOMParser() 19 | .parseFromString(text, 'text/html') 20 | .querySelector('.shopify-section'); 21 | this.renderPreview(sectionInnerHTML); 22 | }) 23 | .catch(e => { 24 | this.querySelector('button')?.removeEventListener('click', this.onClickRefreshList); 25 | this.renderError(); 26 | }); 27 | } 28 | 29 | onClickRefreshList(evt) { 30 | this.fetchAvailability(this.dataset.variantId); 31 | } 32 | 33 | renderError() { 34 | this.innerHTML = ''; 35 | this.appendChild(this.errorHtml); 36 | 37 | this.querySelector('button').addEventListener('click', this.onClickRefreshList); 38 | } 39 | 40 | renderPreview(sectionInnerHTML) { 41 | const drawer = document.querySelector('pickup-availability-drawer'); 42 | if (drawer) drawer.remove(); 43 | if (!sectionInnerHTML.querySelector('pickup-availability-preview')) { 44 | this.innerHTML = ""; 45 | this.removeAttribute('available'); 46 | return; 47 | } 48 | 49 | this.innerHTML = sectionInnerHTML.querySelector('pickup-availability-preview').outerHTML; 50 | this.setAttribute('available', ''); 51 | 52 | document.body.appendChild(sectionInnerHTML.querySelector('pickup-availability-drawer')); 53 | 54 | this.querySelector('button').addEventListener('click', (evt) => { 55 | document.querySelector('pickup-availability-drawer').show(evt.target); 56 | }); 57 | } 58 | } 59 | 60 | customElements.define('pickup-availability', PickupAvailability); 61 | 62 | class PickupAvailabilityDrawer extends HTMLElement { 63 | constructor() { 64 | super(); 65 | 66 | this.onBodyClick = this.handleBodyClick.bind(this); 67 | 68 | this.querySelector('button').addEventListener('click', () => { 69 | this.hide(); 70 | }); 71 | 72 | this.addEventListener('keyup', () => { 73 | if(event.code.toUpperCase() === 'ESCAPE') this.hide(); 74 | }); 75 | } 76 | 77 | handleBodyClick(evt) { 78 | const target = evt.target; 79 | if (target != this && !target.closest('pickup-availability-drawer') && target.id != 'ShowPickupAvailabilityDrawer') { 80 | this.hide(); 81 | } 82 | } 83 | 84 | hide() { 85 | this.removeAttribute('open'); 86 | document.body.removeEventListener('click', this.onBodyClick); 87 | document.body.classList.remove('overflow-hidden'); 88 | removeTrapFocus(this.focusElement); 89 | } 90 | 91 | show(focusElement) { 92 | this.focusElement = focusElement; 93 | this.setAttribute('open', ''); 94 | document.body.addEventListener('click', this.onBodyClick); 95 | document.body.classList.add('overflow-hidden'); 96 | trapFocus(this); 97 | } 98 | } 99 | 100 | customElements.define('pickup-availability-drawer', PickupAvailabilityDrawer); 101 | -------------------------------------------------------------------------------- /src/assets/product-form.js: -------------------------------------------------------------------------------- 1 | class ProductForm extends HTMLElement { 2 | constructor() { 3 | super(); 4 | 5 | this.form = this.querySelector('form'); 6 | this.form.addEventListener('submit', this.onSubmitHandler.bind(this)); 7 | this.cartNotification = document.querySelector('cart-notification'); 8 | } 9 | 10 | onSubmitHandler(evt) { 11 | evt.preventDefault(); 12 | this.cartNotification.setActiveElement(document.activeElement); 13 | 14 | const submitButton = this.querySelector('[type="submit"]'); 15 | 16 | submitButton.setAttribute('disabled', true); 17 | submitButton.classList.add('loading'); 18 | 19 | const body = JSON.stringify({ 20 | ...JSON.parse(serializeForm(this.form)), 21 | sections: this.cartNotification.getSectionsToRender().map((section) => section.id), 22 | sections_url: window.location.pathname 23 | }); 24 | 25 | fetch(`${routes.cart_add_url}`, { ...fetchConfig('javascript'), body }) 26 | .then((response) => response.json()) 27 | .then((parsedState) => { 28 | this.cartNotification.renderContents(parsedState); 29 | }) 30 | .catch((e) => { 31 | console.error(e); 32 | }) 33 | .finally(() => { 34 | submitButton.classList.remove('loading'); 35 | submitButton.removeAttribute('disabled'); 36 | }); 37 | } 38 | } 39 | 40 | customElements.define('product-form', ProductForm); 41 | -------------------------------------------------------------------------------- /src/assets/product-model.js: -------------------------------------------------------------------------------- 1 | class ProductModel extends DeferredMedia { 2 | constructor() { 3 | super(); 4 | } 5 | 6 | loadContent() { 7 | super.loadContent(); 8 | 9 | Shopify.loadFeatures([ 10 | { 11 | name: 'model-viewer-ui', 12 | version: '1.0', 13 | onLoad: this.setupModelViewerUI.bind(this), 14 | }, 15 | ]); 16 | } 17 | 18 | setupModelViewerUI(errors) { 19 | if (errors) return; 20 | 21 | this.modelViewerUI = new Shopify.ModelViewerUI(this.querySelector('model-viewer')); 22 | } 23 | } 24 | customElements.define('product-model', ProductModel); 25 | 26 | window.ProductModel = { 27 | loadShopifyXR() { 28 | Shopify.loadFeatures([ 29 | { 30 | name: 'shopify-xr', 31 | version: '1.0', 32 | onLoad: this.setupShopifyXR.bind(this), 33 | }, 34 | ]); 35 | }, 36 | 37 | setupShopifyXR(errors) { 38 | if (errors) return; 39 | 40 | if (!window.ShopifyXR) { 41 | document.addEventListener('shopify_xr_initialized', () => 42 | this.setupShopifyXR() 43 | ); 44 | return; 45 | } 46 | 47 | document.querySelectorAll('[id^="ProductJSON-"]').forEach((modelJSON) => { 48 | window.ShopifyXR.addModels(JSON.parse(modelJSON.textContent)); 49 | modelJSON.remove(); 50 | }); 51 | window.ShopifyXR.setupXRElements(); 52 | }, 53 | }; 54 | 55 | window.addEventListener('DOMContentLoaded', () => { window.ProductModel?.loadShopifyXR(); }); 56 | -------------------------------------------------------------------------------- /src/assets/section-blog-post.css: -------------------------------------------------------------------------------- 1 | .article-template > *:first-child:not(.article-template__hero-container) { 2 | margin-top: 5rem; 3 | } 4 | 5 | .article-template__hero-container { 6 | max-width: 130rem; 7 | margin: 0 auto; 8 | } 9 | 10 | @media screen and (min-width: 1320px) { 11 | .article-template__hero-container:first-child { 12 | margin-top: 5rem; 13 | } 14 | } 15 | 16 | .article-template__hero-medium { 17 | height: 15.6rem; 18 | } 19 | 20 | .article-template__hero-large { 21 | height: 19rem; 22 | } 23 | 24 | @media screen and (min-width: 750px) and (max-width: 989px) { 25 | .article-template__hero-medium { 26 | height: 34.9rem; 27 | } 28 | 29 | .article-template__hero-large { 30 | height: 42.3rem; 31 | } 32 | } 33 | 34 | @media screen and (min-width: 990px) { 35 | .article-template__hero-medium { 36 | height: 54.5rem; 37 | } 38 | 39 | .article-template__hero-large { 40 | height: 66rem; 41 | } 42 | } 43 | 44 | .article-template header { 45 | margin-top: 4.4rem; 46 | margin-bottom: 2rem; 47 | } 48 | 49 | @media screen and (min-width: 750px) { 50 | .article-template header { 51 | margin-top: 5rem; 52 | } 53 | } 54 | 55 | .article-template__title { 56 | margin: 0; 57 | } 58 | 59 | .article-template__title:not(:only-child) { 60 | margin-bottom: 1rem; 61 | } 62 | 63 | .article-template__link { 64 | font-size: 1.8rem; 65 | display: flex; 66 | justify-content: center; 67 | align-items: center; 68 | text-underline-offset: 0.3rem; 69 | } 70 | 71 | .article-template__link:hover { 72 | text-decoration-thickness: 0.2rem; 73 | } 74 | 75 | .article-template__link svg { 76 | width: 1.5rem; 77 | transform: rotate(180deg); 78 | margin-right: 1rem; 79 | } 80 | 81 | .article-template__content { 82 | margin-top: 3rem; 83 | margin-bottom: 3rem; 84 | } 85 | 86 | .article-template__social-sharing { 87 | display: flex; 88 | flex-direction: column; 89 | align-items: self-end; 90 | margin-top: 3rem; 91 | } 92 | 93 | .article-template__social-sharing .social-sharing { 94 | margin-left: -1.3rem; 95 | } 96 | 97 | .article-template__comment-wrapper { 98 | margin-top: 5rem; 99 | } 100 | 101 | @media screen and (min-width: 750px) { 102 | .article-template__comment-wrapper { 103 | margin-top: 6rem; 104 | } 105 | } 106 | 107 | .article-template__comment-wrapper h2 { 108 | margin-top: 0; 109 | } 110 | 111 | .article-template__comments { 112 | margin-bottom: 5rem; 113 | } 114 | 115 | @media screen and (min-width: 750px) { 116 | .article-template__comments { 117 | margin-bottom: 7rem; 118 | } 119 | } 120 | 121 | .article-template__comments-fields { 122 | margin-bottom: 4rem; 123 | } 124 | 125 | .article-template__comments-comment { 126 | color: rgba(var(--color-foreground), 0.75); 127 | background-color: rgb(var(--color-background)); 128 | margin-bottom: 1.5rem; 129 | padding: 2rem 2rem 1.5rem; 130 | } 131 | 132 | @media screen and (min-width: 750px) { 133 | .article-template__comments-comment { 134 | padding: 2rem 2.5rem; 135 | } 136 | } 137 | 138 | .article-template__comments-comment p { 139 | margin: 0 0 1rem; 140 | } 141 | 142 | .article-template__comment-fields > * { 143 | margin-bottom: 3rem; 144 | } 145 | 146 | @media screen and (min-width: 750px) { 147 | .article-template__comment-fields { 148 | display: grid; 149 | grid-template-columns: repeat(2, 1fr); 150 | grid-column-gap: 4rem; 151 | } 152 | } 153 | 154 | .article-template__comment-warning { 155 | margin: 2rem 0 2.5rem; 156 | } 157 | 158 | @media screen and (min-width: 990px) { 159 | .article-template__comments .pagination-wrapper { 160 | margin: 5rem 0 8rem; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/assets/section-collection-list.css: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 749px) { 2 | .collage-section + .collection-list-section .no-heading.no-mobile-link { 3 | margin-top: -7rem; 4 | } 5 | .collage-section + .collection-list-section .no-heading:not(.no-mobile-link) { 6 | margin-top: -1rem; 7 | } 8 | } 9 | 10 | @media screen and (min-width: 749px) { 11 | .collage-section + .collection-list-section .no-heading { 12 | margin-top: -4rem; 13 | } 14 | } 15 | 16 | .collection-list-title { 17 | margin: 0; 18 | } 19 | 20 | @media screen and (max-width: 749px) { 21 | .collection-list-wrapper.page-width { 22 | padding: 0; 23 | } 24 | 25 | .collection-list:not(.slider) { 26 | padding-left: 0; 27 | padding-right: 0; 28 | } 29 | 30 | .collection-list-section .collection-list:not(.slider) { 31 | padding-left: 1.5rem; 32 | padding-right: 1.5rem; 33 | } 34 | } 35 | 36 | @media screen and (max-width: 749px) { 37 | .collection-list-wrapper:not(.no-heading) .title-wrapper-with-link { 38 | margin-top: -1rem; 39 | } 40 | } 41 | 42 | @media screen and (min-width: 750px) { 43 | .collection-list-wrapper.no-heading { 44 | margin-top: 6rem; 45 | } 46 | } 47 | 48 | .collection-list__item:only-child { 49 | max-width: 100%; 50 | width: 100%; 51 | } 52 | 53 | .collection-list__item .card--light-border:hover { 54 | border: 0.1rem solid rgba(var(--color-foreground), 0.04); 55 | } 56 | 57 | .collection-list__item:only-child .media { 58 | height: 35rem; 59 | } 60 | 61 | @media screen and (max-width: 749px) { 62 | .collection-list .collection-list__item { 63 | width: calc(100% - 3rem); 64 | } 65 | 66 | .collection-list__item.grid__item { 67 | padding-bottom: 1rem; 68 | } 69 | 70 | .slider.collection-list--1-items { 71 | padding-bottom: 0; 72 | } 73 | } 74 | 75 | .collection-list.negative-margin--small { 76 | margin-bottom: -1rem; 77 | } 78 | 79 | @media screen and (min-width: 750px) and (max-width: 989px) { 80 | .slider.collection-list--1-items, 81 | .slider.collection-list--2-items, 82 | .slider.collection-list--3-items, 83 | .slider.collection-list--4-items { 84 | padding-bottom: 0; 85 | } 86 | } 87 | 88 | @media screen and (min-width: 750px) { 89 | .collection-list__item:only-child > *:not(.card--media) { 90 | height: 320px; 91 | } 92 | 93 | .collection-list__item:only-child .media { 94 | height: 47rem; 95 | } 96 | 97 | .collection-list__item a:hover { 98 | box-shadow: none; 99 | } 100 | 101 | .collection-list.grid--3-col-tablet .grid__item { 102 | max-width: 33.33%; 103 | } 104 | 105 | .collection-list--4-items .grid__item, 106 | .collection-list--7-items .grid__item:nth-child(n + 4), 107 | .collection-list--10-items .grid__item:nth-child(n + 7) { 108 | width: 50%; 109 | } 110 | } 111 | 112 | @media screen and (max-width: 989px) { 113 | .collection-list.slider .collection-list__item { 114 | max-width: 100%; 115 | } 116 | } 117 | 118 | .collection-list__item .card__text, 119 | .collection-list__item .card-colored { 120 | position: relative; 121 | } 122 | -------------------------------------------------------------------------------- /src/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 | 41 | .grecaptcha-badge { 42 | visibility: hidden; 43 | } 44 | -------------------------------------------------------------------------------- /src/assets/section-featured-blog.css: -------------------------------------------------------------------------------- 1 | .blog:not(.background-secondary) { 2 | margin: 5rem 0; 3 | } 4 | 5 | .blog.background-secondary { 6 | padding: 4rem 0 5rem; 7 | } 8 | 9 | .blog .placeholder { 10 | display: flex; 11 | flex-direction: column; 12 | align-items: center; 13 | height: 22rem; 14 | text-align: center; 15 | padding: 4rem 2rem 5rem; 16 | margin: 0 2rem; 17 | } 18 | 19 | @media screen and (min-width: 750px) { 20 | .blog .placeholder { 21 | margin: 0; 22 | } 23 | } 24 | 25 | @media screen and (max-width: 749px) { 26 | .blog:not(.no-heading) { 27 | margin-top: -1rem; 28 | } 29 | } 30 | 31 | @media screen and (min-width: 750px) { 32 | .blog.no-heading { 33 | margin-top: 6rem; 34 | } 35 | } 36 | 37 | .background-secondary .title-wrapper-with-link { 38 | margin-top: 0; 39 | } 40 | 41 | .blog__title { 42 | margin: 0; 43 | } 44 | 45 | .blog__posts.articles-wrapper { 46 | margin-bottom: 0; 47 | } 48 | 49 | @media screen and (min-width: 750px) { 50 | .blog__post:only-child { 51 | text-align: center; 52 | } 53 | } 54 | 55 | @media screen and (min-width: 990px) { 56 | .blog__posts.articles-wrapper { 57 | padding-bottom: 0; 58 | } 59 | } 60 | 61 | .blog__posts.articles-wrapper .article { 62 | scroll-snap-align: start; 63 | } 64 | 65 | @media screen and (min-width: 750px) { 66 | .blog__posts .article + .article { 67 | margin-left: 1rem; 68 | } 69 | } 70 | 71 | @media screen and (max-width: 749px) { 72 | .blog__post.article { 73 | width: calc(100% - 3rem); 74 | padding-left: 0.5rem; 75 | } 76 | } 77 | 78 | .background-secondary .article-card { 79 | background-color: rgb(var(--color-background)); 80 | } 81 | 82 | .blog__button { 83 | margin-top: 3rem; 84 | } 85 | 86 | @media screen and (min-width: 750px) { 87 | .blog__button { 88 | margin-top: 5rem; 89 | } 90 | } 91 | 92 | @media screen and (max-width: 749px) { 93 | .slider.blog__posts--1-items { 94 | padding-bottom: 0; 95 | } 96 | } 97 | 98 | @media screen and (min-width: 750px) and (max-width: 989px) { 99 | .slider.blog__posts--1-items, 100 | .slider.blog__posts--2-items { 101 | padding-bottom: 0; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/assets/section-image-banner.css: -------------------------------------------------------------------------------- 1 | .banner { 2 | display: flex; 3 | position: relative; 4 | flex-direction: column; 5 | min-height: initial; 6 | } 7 | 8 | @media screen and (max-width: 749px) { 9 | .banner:not(.banner--stacked) { 10 | flex-direction: row; 11 | flex-wrap: wrap; 12 | } 13 | } 14 | 15 | @media screen and (min-width: 750px) { 16 | .banner { 17 | min-height: 72rem; 18 | flex-direction: row; 19 | } 20 | } 21 | 22 | @media screen and (max-width: 749px) { 23 | .banner--stacked { 24 | height: auto; 25 | } 26 | 27 | .banner--stacked .banner__media { 28 | flex-direction: column; 29 | } 30 | } 31 | 32 | .banner__media { 33 | height: 100%; 34 | left: 0; 35 | top: 0; 36 | width: 100%; 37 | position: relative; 38 | } 39 | 40 | .banner__media-half { 41 | width: 50%; 42 | } 43 | 44 | .banner__media-half + .banner__media-half { 45 | right: 0; 46 | left: auto; 47 | } 48 | 49 | @media screen and (max-width: 749px) { 50 | .banner--stacked .banner__media-half { 51 | width: 100%; 52 | } 53 | 54 | .banner--stacked .banner__media-half + .banner__media-half { 55 | order: 1; 56 | } 57 | 58 | .banner:not(.banner--adapt):not(.banner--stacked) > .banner__media { 59 | height: 39rem; 60 | } 61 | } 62 | 63 | @media screen and (min-width: 750px) { 64 | .banner__media { 65 | position: absolute; 66 | height: 100%; 67 | } 68 | } 69 | 70 | .banner--adapt { 71 | height: auto; 72 | } 73 | 74 | @media screen and (max-width: 749px) { 75 | .banner--stacked:not(.banner--adapt) .banner__media { 76 | height: 39rem; 77 | } 78 | 79 | .banner::before { 80 | display: none !important; 81 | } 82 | 83 | .banner--stacked .banner__media-image-half { 84 | width: 100%; 85 | } 86 | } 87 | 88 | .banner__media .placeholder-svg { 89 | position: absolute; 90 | left: 0; 91 | top: 0; 92 | height: 100%; 93 | width: 100%; 94 | } 95 | 96 | .banner__content { 97 | padding: 0; 98 | display: flex; 99 | position: relative; 100 | width: 100%; 101 | justify-content: center; 102 | } 103 | 104 | @media screen and (min-width: 750px) { 105 | .banner__content { 106 | padding-bottom: 5rem; 107 | padding-top: 5rem; 108 | } 109 | } 110 | 111 | .banner__box { 112 | border: 0; 113 | padding: 4rem 3.5rem; 114 | position: relative; 115 | height: fit-content; 116 | align-items: center; 117 | text-align: center; 118 | width: 100%; 119 | } 120 | 121 | .banner__box > * + .banner__buttons { 122 | margin: 0 auto; 123 | margin-top: 2.3rem; 124 | transform: translateX(1rem); 125 | } 126 | 127 | .banner__box > * + .banner__buttons--multiple { 128 | display: flex; 129 | max-width: 45rem; 130 | flex-wrap: wrap; 131 | align-items: baseline; 132 | justify-content: center; 133 | } 134 | 135 | @media screen and (min-width: 750px) { 136 | .banner__box > * + .banner__buttons { 137 | margin-top: 2rem; 138 | } 139 | } 140 | 141 | .banner__content .button + .button { 142 | margin-top: 1.5rem; 143 | } 144 | 145 | .banner__content .button { 146 | height: auto; 147 | margin-right: 2rem; 148 | } 149 | 150 | .banner__box > * + .banner__text { 151 | margin-top: 1.5rem; 152 | } 153 | 154 | @media screen and (min-width: 750px) { 155 | .banner__box > * + .banner__text { 156 | margin-top: 2rem; 157 | } 158 | } 159 | 160 | .banner__box > * + * { 161 | margin-top: 1rem; 162 | } 163 | 164 | .banner__box > *:first-child { 165 | margin-top: 0; 166 | } 167 | 168 | @media screen and (max-width: 749px) { 169 | .banner__content .button { 170 | flex-grow: 1; 171 | } 172 | 173 | .banner--stacked .banner__box { 174 | width: 100%; 175 | } 176 | } 177 | 178 | @media screen and (min-width: 750px) { 179 | .banner__box { 180 | padding: 4rem; 181 | width: 54.8rem; 182 | } 183 | 184 | .banner__box > .banner__buttons:only-child .button { 185 | margin-top: 0; 186 | } 187 | } 188 | 189 | .banner__heading > *, 190 | .banner__text > * { 191 | word-wrap: break-word; 192 | } 193 | 194 | .banner__heading { 195 | margin-bottom: 0; 196 | } 197 | -------------------------------------------------------------------------------- /src/assets/section-main-blog.css: -------------------------------------------------------------------------------- 1 | .blog-articles { 2 | display: grid; 3 | grid-gap: 1rem; 4 | } 5 | 6 | @media screen and (min-width: 750px) { 7 | .blog-articles { 8 | grid-template-columns: 1fr 1fr; 9 | } 10 | 11 | .blog-articles > *:first-child, 12 | .blog-articles > *:nth-child(4), 13 | .blog-articles > *:last-child:nth-child(2), 14 | .blog-articles > *:last-child:nth-child(5) { 15 | grid-column: span 2; 16 | text-align: center; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/assets/section-multicolumn.css: -------------------------------------------------------------------------------- 1 | .multicolumn .title { 2 | margin: 0; 3 | } 4 | 5 | .multicolumn.no-heading .title { 6 | display: none; 7 | } 8 | 9 | @media screen and (max-width: 749px) { 10 | .multicolumn.no-heading.background-secondary { 11 | padding-top: 5rem; 12 | } 13 | } 14 | 15 | @media screen and (min-width: 750px) { 16 | .multicolumn.no-heading:not(.background-secondary) { 17 | margin-top: 6rem; 18 | } 19 | } 20 | 21 | .multicolumn.background-secondary .title-wrapper-with-link { 22 | margin-top: 0; 23 | } 24 | 25 | @media screen and (max-width: 749px) { 26 | .multicolumn .title-wrapper-with-link { 27 | margin-bottom: 3rem; 28 | } 29 | } 30 | 31 | .multicolumn-card__image-wrapper--third-width { 32 | width: 33%; 33 | } 34 | 35 | .multicolumn-card__image-wrapper--half-width { 36 | width: 50%; 37 | } 38 | 39 | .multicolumn-list__item.center 40 | .multicolumn-card__image-wrapper:not(.multicolumn-card__image-wrapper--full-width), 41 | .multicolumn-list__item:only-child { 42 | margin-left: auto; 43 | margin-right: auto; 44 | } 45 | 46 | .multicolumn .button { 47 | margin-top: 1.5rem; 48 | } 49 | 50 | @media screen and (min-width: 750px) { 51 | .multicolumn .button { 52 | margin-top: 4rem; 53 | } 54 | } 55 | 56 | .multicolumn-list { 57 | margin-bottom: 0; 58 | padding: 0; 59 | } 60 | 61 | .multicolumn-list__item:only-child { 62 | max-width: 72rem; 63 | } 64 | 65 | .multicolumn:not(.background-none) .multicolumn-card { 66 | background: rgba(var(--color-foreground), 0.04); 67 | height: 100%; 68 | } 69 | 70 | .multicolumn.background-secondary .multicolumn-card { 71 | background: rgb(var(--color-background)); 72 | } 73 | 74 | .multicolumn.background-secondary { 75 | padding: 4rem 0 5rem; 76 | } 77 | 78 | .multicolumn:not(.background-secondary) { 79 | margin: 5rem 0; 80 | } 81 | 82 | .multicolumn-list h3, 83 | .multicolumn-list p { 84 | margin: 0; 85 | } 86 | 87 | .multicolumn-card-spacing { 88 | padding-top: 2.5rem; 89 | margin-left: 2.5rem; 90 | margin-right: 2.5rem; 91 | } 92 | 93 | .multicolumn-card__info > :nth-child(2) { 94 | margin-top: 1rem; 95 | } 96 | 97 | .multicolumn-list__item.center .media--adapt, 98 | .multicolumn-list__item .media--adapt img { 99 | width: auto; 100 | } 101 | 102 | .multicolumn-list__item.center .media--adapt img { 103 | left: 50%; 104 | transform: translateX(-50%); 105 | } 106 | 107 | @media screen and (max-width: 749px) { 108 | .multicolumn .page-width { 109 | padding: 0; 110 | } 111 | 112 | .multicolumn-list { 113 | margin: 0; 114 | width: 100%; 115 | } 116 | 117 | .multicolumn-list__item { 118 | margin: 0 0 1rem; 119 | padding: 0; 120 | } 121 | 122 | .multicolumn-list:not(.slider) { 123 | padding-left: 1.5rem; 124 | padding-right: 1.5rem; 125 | } 126 | 127 | .multicolumn-list.slider .multicolumn-list__item { 128 | width: calc(100% - 3rem); 129 | } 130 | 131 | .multicolumn-list.slider .multicolumn-list__item + .multicolumn-list__item { 132 | padding-left: 0.5rem; 133 | } 134 | } 135 | 136 | @media screen and (min-width: 750px) { 137 | .multicolumn-list.slider, 138 | .multicolumn-list.grid--4-col-desktop { 139 | padding: 0; 140 | } 141 | 142 | .multicolumn-list__item, 143 | .grid--4-col-desktop .multicolumn-list__item { 144 | padding-bottom: 0; 145 | } 146 | 147 | .grid--2-col-tablet .multicolumn-list__item { 148 | margin-top: 1rem; 149 | max-width: 50%; 150 | } 151 | 152 | .background-none .grid--2-col-tablet .multicolumn-list__item { 153 | margin-top: 4rem; 154 | } 155 | 156 | .grid--2-col-tablet .multicolumn-list__item:nth-of-type(-n + 2) { 157 | margin-top: 0; 158 | } 159 | } 160 | 161 | @media screen and (min-width: 990px) { 162 | .grid--2-col-tablet.grid--4-col-desktop .multicolumn-list__item { 163 | max-width: 25%; 164 | } 165 | 166 | .grid--2-col-tablet.grid--4-col-desktop 167 | .multicolumn-list__item:nth-of-type(-n + 4) { 168 | margin-top: 0; 169 | } 170 | } 171 | 172 | .background-none .multicolumn-card-spacing { 173 | padding: 0; 174 | margin: 0; 175 | } 176 | 177 | .multicolumn-card__info { 178 | padding: 2.5rem 2.5rem; 179 | } 180 | 181 | .background-none .multicolumn-card__info { 182 | padding-top: 0; 183 | padding-left: 0; 184 | padding-right: 0; 185 | } 186 | 187 | .background-none .multicolumn-card__image-wrapper + .multicolumn-card__info { 188 | padding-top: 2.5rem; 189 | } 190 | 191 | .background-none .slider .multicolumn-card__info { 192 | padding-left: 0.5rem; 193 | } 194 | 195 | .background-none 196 | .slider 197 | .multicolumn-card__image-wrapper 198 | + .multicolumn-card__info { 199 | padding-left: 1.5rem; 200 | } 201 | 202 | .background-none 203 | .multicolumn-list:not(.slider) 204 | .center 205 | .multicolumn-card__info { 206 | padding-left: 2.5rem; 207 | padding-right: 2.5rem; 208 | } 209 | 210 | @media screen and (min-width: 750px) { 211 | .background-none .multicolumn-card__image-wrapper { 212 | margin-left: 1.5rem; 213 | margin-right: 1.5rem; 214 | } 215 | 216 | .background-none .multicolumn-list .multicolumn-card__info, 217 | .background-none 218 | .multicolumn-list:not(.slider) 219 | .center 220 | .multicolumn-card__info { 221 | padding-left: 1.5rem; 222 | padding-right: 1.5rem; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/assets/section-product-recommendations.css: -------------------------------------------------------------------------------- 1 | .product-recommendations { 2 | display: block; 3 | } 4 | 5 | .product-recommendations__heading { 6 | margin: 0; 7 | margin-bottom: 3rem; 8 | } 9 | 10 | .product-recommendations .grid__item { 11 | padding-bottom: 0; 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/section-rich-text.css: -------------------------------------------------------------------------------- 1 | .rich-text { 2 | margin: auto; 3 | max-width: 110rem; 4 | text-align: center; 5 | /* 1.5rem margin on left & right */ 6 | width: calc(100% - 3rem); 7 | } 8 | 9 | .rich-text.rich-text--full-width { 10 | max-width: initial; 11 | width: 100%; 12 | } 13 | 14 | .rich-text__blocks { 15 | margin: auto; 16 | /* 2.5rem margin on left & right */ 17 | width: calc(100% - 5rem); 18 | } 19 | 20 | .rich-text__blocks * { 21 | overflow-wrap: break-word; 22 | } 23 | 24 | .rich-text--full-width .rich-text__blocks { 25 | /* 4rem (1.5rem + 2.5rem) margin on left & right */ 26 | width: calc(100% - 8rem); 27 | } 28 | 29 | .rich-text:not(.rich-text--full-width), 30 | .rich-text--full-width.color-background-1 { 31 | margin-top: 5rem; 32 | margin-bottom: 5rem; 33 | } 34 | 35 | .rich-text:not(.color-background-1) { 36 | padding-top: 5rem; 37 | padding-bottom: 5rem; 38 | } 39 | 40 | @media screen and (min-width: 750px) { 41 | .rich-text { 42 | /* 5rem margin on left & right */ 43 | width: calc(100% - 10rem); 44 | } 45 | 46 | .rich-text__blocks { 47 | max-width: 50rem; 48 | } 49 | 50 | .rich-text--full-width .rich-text__blocks { 51 | /* 7.5rem (5rem + 2.5rem) margin on left & right */ 52 | width: calc(100% - 15rem); 53 | } 54 | } 55 | 56 | @media screen and (min-width: 990px) { 57 | .rich-text__blocks { 58 | max-width: 78rem; 59 | } 60 | } 61 | 62 | /* Blocks */ 63 | 64 | .rich-text__blocks > * { 65 | margin-top: 0; 66 | margin-bottom: 0; 67 | } 68 | 69 | .rich-text__blocks > * + * { 70 | margin-top: 2rem; 71 | } 72 | 73 | .rich-text__blocks > * + a { 74 | margin-top: 3rem; 75 | } 76 | -------------------------------------------------------------------------------- /src/assets/share.js: -------------------------------------------------------------------------------- 1 | class ShareButton extends DetailsDisclosure { 2 | constructor() { 3 | super(); 4 | 5 | this.elements = { 6 | shareButton: this.querySelector('button'), 7 | successMessage: this.querySelector('[id^="ShareMessage"]'), 8 | urlInput: this.querySelector('input') 9 | } 10 | if (navigator.share) { 11 | this.mainDetailsToggle.setAttribute('hidden', ''); 12 | this.elements.shareButton.classList.remove('hidden'); 13 | this.elements.shareButton.addEventListener('click', () => { navigator.share({ url: document.location.href, title: document.title }) }); 14 | } else { 15 | this.mainDetailsToggle.addEventListener('toggle', this.toggleDetails.bind(this)); 16 | this.mainDetailsToggle.querySelector('button').addEventListener('click', this.copyToClipboard.bind(this)); 17 | } 18 | } 19 | 20 | toggleDetails() { 21 | if (!this.mainDetailsToggle.open) 22 | this.elements.successMessage.classList.add('hidden'); 23 | } 24 | 25 | copyToClipboard() { 26 | navigator.clipboard.writeText(this.elements.urlInput.value).then(() => { 27 | this.elements.successMessage.classList.remove('hidden'); 28 | this.elements.successMessage.setAttribute('aria-hidden', false); 29 | 30 | setTimeout(() => { 31 | this.elements.successMessage.setAttribute('aria-hidden', true); 32 | }, 6000); 33 | }); 34 | } 35 | } 36 | 37 | customElements.define('share-button', ShareButton); 38 | -------------------------------------------------------------------------------- /src/assets/slider.js: -------------------------------------------------------------------------------- 1 | class SliderComponent extends HTMLElement { 2 | constructor() { 3 | super(); 4 | this.slider = this.querySelector('ul'); 5 | this.sliderItems = this.querySelectorAll('li'); 6 | this.pageCount = this.querySelector('.slider-counter--current'); 7 | this.pageTotal = this.querySelector('.slider-counter--total'); 8 | this.prevButton = this.querySelector('button[name="previous"]'); 9 | this.nextButton = this.querySelector('button[name="next"]'); 10 | 11 | if (!this.slider || !this.nextButton) return; 12 | 13 | const resizeObserver = new ResizeObserver(entries => this.initPages()); 14 | resizeObserver.observe(this.slider); 15 | 16 | this.slider.addEventListener('scroll', this.update.bind(this)); 17 | this.prevButton.addEventListener('click', this.onButtonClick.bind(this)); 18 | this.nextButton.addEventListener('click', this.onButtonClick.bind(this)); 19 | } 20 | 21 | initPages() { 22 | if (!this.sliderItems.length === 0) return; 23 | this.slidesPerPage = Math.floor(this.slider.clientWidth / this.sliderItems[0].clientWidth); 24 | this.totalPages = this.sliderItems.length - this.slidesPerPage + 1; 25 | this.update(); 26 | } 27 | 28 | update() { 29 | if (!this.pageCount || !this.pageTotal) return; 30 | this.currentPage = Math.round(this.slider.scrollLeft / this.sliderItems[0].clientWidth) + 1; 31 | 32 | if (this.currentPage === 1) { 33 | this.prevButton.setAttribute('disabled', true); 34 | } else { 35 | this.prevButton.removeAttribute('disabled'); 36 | } 37 | 38 | if (this.currentPage === this.totalPages) { 39 | this.nextButton.setAttribute('disabled', true); 40 | } else { 41 | this.nextButton.removeAttribute('disabled'); 42 | } 43 | 44 | this.pageCount.textContent = this.currentPage; 45 | this.pageTotal.textContent = this.totalPages; 46 | } 47 | 48 | onButtonClick(event) { 49 | event.preventDefault(); 50 | const slideScrollPosition = event.currentTarget.name === 'next' ? this.slider.scrollLeft + this.sliderItems[0].clientWidth : this.slider.scrollLeft - this.sliderItems[0].clientWidth; 51 | this.slider.scrollTo({ 52 | left: slideScrollPosition 53 | }); 54 | } 55 | } 56 | 57 | customElements.define('slider-component', SliderComponent); 58 | -------------------------------------------------------------------------------- /src/assets/variants.js: -------------------------------------------------------------------------------- 1 | class VariantSelects extends HTMLElement { 2 | constructor() { 3 | super(); 4 | this.addEventListener('change', this.onVariantChange); 5 | } 6 | 7 | onVariantChange() { 8 | this.updateOptions(); 9 | this.updateMasterId(); 10 | this.toggleAddButton(true, '', false); 11 | this.updatePickupAvailability(); 12 | 13 | if (!this.currentVariant) { 14 | this.toggleAddButton(true, '', true); 15 | this.setUnavailable(); 16 | } else { 17 | this.updateMedia(); 18 | this.updateURL(); 19 | this.updateVariantInput(); 20 | this.renderProductInfo(); 21 | } 22 | } 23 | 24 | updateOptions() { 25 | this.options = Array.from(this.querySelectorAll('select'), (select) => select.value); 26 | } 27 | 28 | updateMasterId() { 29 | this.currentVariant = this.getVariantData().find((variant) => { 30 | return !variant.options.map((option, index) => { 31 | return this.options[index] === option; 32 | }).includes(false); 33 | }); 34 | } 35 | 36 | updateMedia() { 37 | if (!this.currentVariant || !this.currentVariant?.featured_media) return; 38 | const newMedia = document.querySelector( 39 | `[data-media-id="${this.dataset.section}-${this.currentVariant.featured_media.id}"]` 40 | ); 41 | if (!newMedia) return; 42 | const parent = newMedia.parentElement; 43 | parent.prepend(newMedia); 44 | window.setTimeout(() => { parent.scroll(0, 0) }); 45 | } 46 | 47 | updateURL() { 48 | if (!this.currentVariant) return; 49 | window.history.replaceState({ }, '', `${this.dataset.url}?variant=${this.currentVariant.id}`); 50 | } 51 | 52 | updateVariantInput() { 53 | const productForms = document.querySelectorAll(`#product-form-${this.dataset.section}, #product-form-installment`); 54 | productForms.forEach((productForm) => { 55 | const input = productForm.querySelector('input[name="id"]'); 56 | input.value = this.currentVariant.id; 57 | input.dispatchEvent(new Event('change', { bubbles: true })); 58 | }); 59 | } 60 | 61 | updatePickupAvailability() { 62 | const pickUpAvailability = document.querySelector('pickup-availability'); 63 | if (!pickUpAvailability) return; 64 | 65 | if (this.currentVariant?.available) { 66 | pickUpAvailability.fetchAvailability(this.currentVariant.id); 67 | } else { 68 | pickUpAvailability.removeAttribute('available'); 69 | pickUpAvailability.innerHTML = ''; 70 | } 71 | } 72 | 73 | renderProductInfo() { 74 | fetch(`${this.dataset.url}?variant=${this.currentVariant.id}§ion_id=${this.dataset.section}`) 75 | .then((response) => response.text()) 76 | .then((responseText) => { 77 | const id = `price-${this.dataset.section}`; 78 | const html = new DOMParser().parseFromString(responseText, 'text/html') 79 | const destination = document.getElementById(id); 80 | const source = html.getElementById(id); 81 | 82 | if (source && destination) destination.innerHTML = source.innerHTML; 83 | 84 | document.getElementById(`price-${this.dataset.section}`)?.classList.remove('visibility-hidden'); 85 | this.toggleAddButton(!this.currentVariant.available, window.variantStrings.soldOut); 86 | }); 87 | } 88 | 89 | toggleAddButton(disable = true, text, modifyClass = true) { 90 | const addButton = document.getElementById(`product-form-${this.dataset.section}`)?.querySelector('[name="add"]'); 91 | 92 | if (!addButton) return; 93 | 94 | if (disable) { 95 | addButton.setAttribute('disabled', true); 96 | if (text) addButton.textContent = text; 97 | } else { 98 | addButton.removeAttribute('disabled'); 99 | addButton.textContent = window.variantStrings.addToCart; 100 | } 101 | 102 | if (!modifyClass) return; 103 | } 104 | 105 | setUnavailable() { 106 | const addButton = document.getElementById(`product-form-${this.dataset.section}`)?.querySelector('[name="add"]'); 107 | if (!addButton) return; 108 | addButton.textContent = window.variantStrings.unavailable; 109 | document.getElementById(`price-${this.dataset.section}`)?.classList.add('visibility-hidden'); 110 | } 111 | 112 | getVariantData() { 113 | this.variantData = this.variantData || JSON.parse(this.querySelector('[type="application/json"]').textContent); 114 | return this.variantData; 115 | } 116 | } 117 | 118 | customElements.define('variant-selects', VariantSelects); 119 | 120 | class VariantRadios extends VariantSelects { 121 | constructor() { 122 | super(); 123 | } 124 | 125 | updateOptions() { 126 | const fieldsets = Array.from(this.querySelectorAll('fieldset')); 127 | this.options = fieldsets.map((fieldset) => { 128 | return Array.from(fieldset.querySelectorAll('input')).find((radio) => radio.checked).value; 129 | }); 130 | } 131 | } 132 | 133 | customElements.define('variant-radios', VariantRadios); 134 | -------------------------------------------------------------------------------- /src/config/settings_data.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /src/layout/password.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {%- if settings.favicon != blank -%} 12 | 13 | {%- endif -%} 14 | 15 | {%- unless settings.type_header_font.system? -%} 16 | 17 | {%- endunless -%} 18 | 19 | {{ shop.name }} 20 | 21 | 22 | 23 | {% render 'meta-tags' %} 24 | 25 | {{ content_for_header }} 26 | 27 | {%- liquid 28 | assign body_font_bold = settings.type_body_font | font_modify: 'weight', 'bold' 29 | assign body_font_italic = settings.type_body_font | font_modify: 'style', 'italic' 30 | assign body_font_bold_italic = body_font_bold | font_modify: 'style', 'italic' 31 | %} 32 | 33 | {% style %} 34 | {{ settings.type_body_font | font_face: font_display: 'swap' }} 35 | {{ body_font_bold | font_face: font_display: 'swap' }} 36 | {{ body_font_italic | font_face: font_display: 'swap' }} 37 | {{ body_font_bold_italic | font_face: font_display: 'swap' }} 38 | {{ settings.type_header_font | font_face: font_display: 'swap' }} 39 | 40 | :root { 41 | --font-body-family: {{ settings.type_body_font.family }}, {{ settings.type_body_font.fallback_families }}; 42 | --font-body-style: {{ settings.type_body_font.style }}; 43 | --font-body-weight: {{ settings.type_body_font.weight }}; 44 | 45 | --font-heading-family: {{ settings.type_header_font.family }}, {{ settings.type_header_font.fallback_families }}; 46 | --font-heading-style: {{ settings.type_header_font.style }}; 47 | --font-heading-weight: {{ settings.type_header_font.weight }}; 48 | 49 | --color-base-text: {{ settings.colors_text.red }}, {{ settings.colors_text.green }}, {{ settings.colors_text.blue }}; 50 | --color-base-background-1: {{ settings.colors_background_1.red }}, {{ settings.colors_background_1.green }}, {{ settings.colors_background_1.blue }}; 51 | --color-base-background-2: {{ settings.colors_background_2.red }}, {{ settings.colors_background_2.green }}, {{ settings.colors_background_2.blue }}; 52 | --color-base-solid-button-labels: {{ settings.colors_solid_button_labels.red }}, {{ settings.colors_solid_button_labels.green }}, {{ settings.colors_solid_button_labels.blue }}; 53 | --color-base-outline-button-labels: {{ settings.colors_outline_button_labels.red }}, {{ settings.colors_outline_button_labels.green }}, {{ settings.colors_outline_button_labels.blue }}; 54 | --color-base-accent-1: {{ settings.colors_accent_1.red }}, {{ settings.colors_accent_1.green }}, {{ settings.colors_accent_1.blue }}; 55 | --color-base-accent-2: {{ settings.colors_accent_2.red }}, {{ settings.colors_accent_2.green }}, {{ settings.colors_accent_2.blue }}; 56 | --payment-terms-background-color: {{ settings.colors_background_1 }}; 57 | } 58 | {% endstyle %} 59 | 60 | {%- unless settings.type_body_font.system? -%} 61 | 62 | {%- endunless -%} 63 | {%- unless settings.type_header_font.system? -%} 64 | 65 | {%- endunless -%} 66 | 67 | {{ 'section-password.css' | asset_url | stylesheet_tag }} 68 | {{ 'base.css' | asset_url | stylesheet_tag }} 69 | {{ 'component-list-social.css' | asset_url | stylesheet_tag }} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | {{ 'accessibility.skip_to_text' | t }} 80 | 81 | 82 | {% section 'main-password-header' %} 83 | 84 |
85 | {{ content_for_layout }} 86 |
87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/scripts/sections/header.js: -------------------------------------------------------------------------------- 1 | import confetti from 'canvas-confetti'; 2 | 3 | console.log('section - scripts/sections/header.js'); 4 | 5 | confetti(); -------------------------------------------------------------------------------- /src/scripts/sections/image-banner.js: -------------------------------------------------------------------------------- 1 | console.log('section - scripts/sections/image-banner.js'); -------------------------------------------------------------------------------- /src/scripts/theme.js: -------------------------------------------------------------------------------- 1 | import {sum, prod} from './utils/operations.js'; 2 | const A = 4; 3 | const B = 5; 4 | 5 | console.log(sum(A, B)); 6 | const bar = ['a', 'b', 'c']; 7 | 8 | console.log('bar', bar); 9 | console.log('scripts/theme.js'); -------------------------------------------------------------------------------- /src/scripts/utils/operations.js: -------------------------------------------------------------------------------- 1 | const sum = (a, b) => { 2 | return a + b; 3 | }; 4 | const prod = (a, b) => { 5 | return a * b; 6 | }; 7 | export {sum, prod}; 8 | -------------------------------------------------------------------------------- /src/sections/announcement-bar.liquid: -------------------------------------------------------------------------------- 1 | {%- for block in section.blocks -%} 2 | {%- case block.type -%} 3 | {%- when 'announcement' -%} 4 |
5 | {%- if block.settings.text != blank -%} 6 | {%- if block.settings.link != blank -%} 7 | 8 | {%- endif -%} 9 |

10 | {{ block.settings.text | escape }} 11 | {%- if block.settings.link != blank -%} 12 | {% render 'icon-arrow' %} 13 | {%- endif -%} 14 |

15 | {%- if block.settings.link != blank -%} 16 |
17 | {%- endif -%} 18 | {%- endif -%} 19 |
20 | {%- endcase -%} 21 | {%- endfor -%} 22 | 23 | {% schema %} 24 | { 25 | "name": "t:sections.announcement-bar.name", 26 | "max_blocks": 12, 27 | "blocks": [ 28 | { 29 | "type": "announcement", 30 | "name": "t:sections.announcement-bar.blocks.announcement.name", 31 | "settings": [ 32 | { 33 | "type": "text", 34 | "id": "text", 35 | "default": "Welcome to our store", 36 | "label": "t:sections.announcement-bar.blocks.announcement.settings.text.label" 37 | }, 38 | { 39 | "type": "select", 40 | "id": "color_scheme", 41 | "options": [ 42 | { 43 | "value": "background-1", 44 | "label": "t:sections.announcement-bar.blocks.announcement.settings.color_scheme.options__1.label" 45 | }, 46 | { 47 | "value": "background-2", 48 | "label": "t:sections.announcement-bar.blocks.announcement.settings.color_scheme.options__2.label" 49 | }, 50 | { 51 | "value": "inverse", 52 | "label": "t:sections.announcement-bar.blocks.announcement.settings.color_scheme.options__3.label" 53 | }, 54 | { 55 | "value": "accent-1", 56 | "label": "t:sections.announcement-bar.blocks.announcement.settings.color_scheme.options__4.label" 57 | }, 58 | { 59 | "value": "accent-2", 60 | "label": "t:sections.announcement-bar.blocks.announcement.settings.color_scheme.options__5.label" 61 | } 62 | ], 63 | "default": "accent-1", 64 | "label": "t:sections.announcement-bar.blocks.announcement.settings.color_scheme.label" 65 | }, 66 | { 67 | "type": "url", 68 | "id": "link", 69 | "label": "t:sections.announcement-bar.blocks.announcement.settings.link.label" 70 | } 71 | ] 72 | } 73 | ], 74 | "default": { 75 | "blocks": [ 76 | { 77 | "type": "announcement" 78 | } 79 | ] 80 | } 81 | } 82 | {% endschema %} 83 | -------------------------------------------------------------------------------- /src/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": "spaced-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 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/sections/cart-live-region-text.liquid: -------------------------------------------------------------------------------- 1 | {{ 'sections.cart.new_subtotal' | t }}: {{ cart.total_price | money_with_currency }} 2 | -------------------------------------------------------------------------------- /src/sections/cart-notification-button.liquid: -------------------------------------------------------------------------------- 1 | {{ 'general.cart.view' | t: count: cart.item_count }} 2 | -------------------------------------------------------------------------------- /src/sections/cart-notification-product.liquid: -------------------------------------------------------------------------------- 1 | {%- if cart != empty -%} 2 | {%- for item in cart.items -%} 3 |
4 | {% if item.image %} 5 | {{ item.image.alt | escape }} 12 | {% endif %} 13 |
14 |

{{ item.product.title | escape }}

15 | {%- unless item.product.has_only_default_variant -%} 16 |
17 | {%- for option in item.options_with_values -%} 18 |
19 |
{{ option.name }}:
20 |
{{ option.value }}
21 |
22 | {%- endfor -%} 23 |
24 | {%- endunless -%} 25 |
26 |
27 | {%- endfor -%} 28 | {%- endif -%} 29 | -------------------------------------------------------------------------------- /src/sections/contact-form.liquid: -------------------------------------------------------------------------------- 1 | {{ 'section-contact-form.css' | asset_url | stylesheet_tag }} 2 | 3 |
4 |

{{ section.settings.heading | escape }}

5 | {%- form 'contact', id: 'ContactForm' -%} 6 | {%- if form.posted_successfully? -%} 7 |
{% render 'icon-success' %} {{ 'templates.contact.form.post_success' | t }}
8 | {%- elsif form.errors -%} 9 |
10 | 11 |
12 | 19 | {%- endif -%} 20 |
21 |
22 | 23 | 24 |
25 |
26 | 42 | 43 | {%- if form.errors contains 'email' -%} 44 | 45 | {{ 'accessibility.error' | t }} 46 | {% render 'icon-error' %}{{ form.errors.translated_fields['email'] | capitalize }} {{ form.errors.messages['email'] }} 47 | 48 | {%- endif -%} 49 |
50 |
51 |
52 | 53 | 54 |
55 |
56 | 65 | 66 |
67 |
68 | 71 |
72 | {%- endform -%} 73 |
74 | 75 | {% schema %} 76 | { 77 | "name": "t:sections.contact-form.name", 78 | "tag": "section", 79 | "class": "spaced-section", 80 | "settings": [ 81 | { 82 | "type": "text", 83 | "id": "heading", 84 | "default": "Contact form", 85 | "label": "Heading" 86 | } 87 | ], 88 | "presets": [ 89 | { 90 | "name": "t:sections.contact-form.presets.name" 91 | } 92 | ] 93 | } 94 | {% endschema %} 95 | -------------------------------------------------------------------------------- /src/sections/custom-liquid.liquid: -------------------------------------------------------------------------------- 1 | {{ section.settings.custom_liquid }} 2 | 3 | {% schema %} 4 | { 5 | "name": "t:sections.custom-liquid.name", 6 | "tag": "section", 7 | "class": "spaced-section", 8 | "settings": [ 9 | { 10 | "type": "liquid", 11 | "id": "custom_liquid", 12 | "label": "t:sections.custom-liquid.settings.custom_liquid.label" 13 | } 14 | ], 15 | "presets": [ 16 | { 17 | "name": "t:sections.custom-liquid.presets.name" 18 | } 19 | ] 20 | } 21 | {% endschema %} 22 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/sections/main-blog.liquid: -------------------------------------------------------------------------------- 1 | {{ 'component-article-card.css' | asset_url | stylesheet_tag }} 2 | {{ 'component-card.css' | asset_url | stylesheet_tag }} 3 | {{ 'section-main-blog.css' | asset_url | stylesheet_tag }} 4 | 5 | 6 | {%- paginate blog.articles by 6 -%} 7 | 8 |
9 |

{{ blog.title | escape }}

10 | 11 |
12 | {%- for article in blog.articles -%} 13 |
14 | {%- render 'article-card', article: article, show_image: section.settings.show_image -%} 15 |
16 | {%- endfor -%} 17 |
18 | 19 | {%- if paginate.pages > 1 -%} 20 | {%- render 'pagination', paginate: paginate -%} 21 | {%- endif -%} 22 |
23 | {%- endpaginate -%} 24 | 25 | {% schema %} 26 | { 27 | "name": "t:sections.main-blog.name", 28 | "tag": "section", 29 | "class": "spaced-section", 30 | "settings": [ 31 | { 32 | "type": "header", 33 | "content": "t:sections.main-blog.settings.header.content" 34 | }, 35 | { 36 | "type": "checkbox", 37 | "id": "show_image", 38 | "default": true, 39 | "label": "t:sections.main-blog.settings.show_image.label", 40 | "info": "t:sections.main-blog.settings.show_image.info" 41 | }, 42 | { 43 | "type": "paragraph", 44 | "content": "t:sections.main-blog.settings.paragraph.content" 45 | } 46 | ], 47 | "blocks": [ 48 | { 49 | "type": "title", 50 | "name": "t:sections.main-blog.blocks.title.name", 51 | "limit": 1, 52 | "settings": [ 53 | { 54 | "type": "checkbox", 55 | "id": "show_date", 56 | "default": true, 57 | "label": "t:sections.main-blog.blocks.title.settings.show_date.label" 58 | }, 59 | { 60 | "type": "checkbox", 61 | "id": "show_author", 62 | "default": false, 63 | "label": "t:sections.main-blog.blocks.title.settings.show_author.label" 64 | } 65 | ] 66 | }, 67 | { 68 | "type": "summary", 69 | "name": "t:sections.main-blog.blocks.summary.name", 70 | "limit": 1 71 | }, 72 | { 73 | "type": "link", 74 | "name": "t:sections.main-blog.blocks.link.name", 75 | "limit": 1 76 | } 77 | ] 78 | } 79 | {% endschema %} 80 | -------------------------------------------------------------------------------- /src/sections/main-cart-footer.liquid: -------------------------------------------------------------------------------- 1 | {{ 'component-cart.css' | asset_url | stylesheet_tag }} 2 | {{ 'component-totals.css' | asset_url | stylesheet_tag }} 3 | {{ 'component-price.css' | asset_url | stylesheet_tag }} 4 | {{ 'component-discounts.css' | asset_url | stylesheet_tag }} 5 | 6 | 80 | 81 | {% javascript %} 82 | class CartNote extends HTMLElement { 83 | constructor() { 84 | super(); 85 | 86 | this.addEventListener('change', debounce((event) => { 87 | const body = JSON.stringify({ note: event.target.value }); 88 | fetch(`${routes.cart_update_url}`, {...fetchConfig(), ...{ body }}); 89 | }, 300)) 90 | } 91 | } 92 | 93 | customElements.define('cart-note', CartNote); 94 | {% endjavascript %} 95 | 96 | {% schema %} 97 | { 98 | "name": "t:sections.main-cart-footer.name", 99 | "class": "cart__footer-wrapper", 100 | "settings": [ 101 | { 102 | "type": "checkbox", 103 | "id": "show_cart_note", 104 | "default": false, 105 | "label": "t:sections.main-cart-footer.settings.show_cart_note.label" 106 | } 107 | ], 108 | "blocks": [ 109 | { 110 | "type": "subtotal", 111 | "name": "t:sections.main-cart-footer.blocks.subtotal.name", 112 | "limit": 1 113 | }, 114 | { 115 | "type": "buttons", 116 | "name": "t:sections.main-cart-footer.blocks.buttons.name", 117 | "limit": 1 118 | }, 119 | { 120 | "type": "@app" 121 | } 122 | ] 123 | } 124 | {% endschema %} 125 | -------------------------------------------------------------------------------- /src/sections/main-collection-banner.liquid: -------------------------------------------------------------------------------- 1 | {{ 'component-collection-hero.css' | asset_url | stylesheet_tag }} 2 | 3 |
4 |
5 |
6 |

7 | {{ 'sections.collection_template.title' | t }}: 8 | {{- collection.title | escape -}} 9 |

10 | 11 | {%- if section.settings.show_collection_description -%} 12 |
{{ collection.description }}
13 | {%- endif -%} 14 |
15 | 16 | {%- if section.settings.show_collection_image and collection.image -%} 17 |
18 | {{ collection.title | escape }} 31 |
32 | {%- endif -%} 33 |
34 |
35 | 36 | {% schema %} 37 | { 38 | "name": "t:sections.main-collection-banner.name", 39 | "class": "spaced-section spaced-section--full-width", 40 | "settings": [ 41 | { 42 | "type": "paragraph", 43 | "content": "t:sections.main-collection-banner.settings.paragraph.content" 44 | }, 45 | { 46 | "type": "checkbox", 47 | "id": "show_collection_description", 48 | "default": false, 49 | "label": "t:sections.main-collection-banner.settings.show_collection_description.label" 50 | }, 51 | { 52 | "type": "checkbox", 53 | "id": "show_collection_image", 54 | "default": false, 55 | "label": "t:sections.main-collection-banner.settings.show_collection_image.label", 56 | "info": "t:sections.main-collection-banner.settings.show_collection_image.info" 57 | } 58 | ] 59 | } 60 | {% endschema %} 61 | -------------------------------------------------------------------------------- /src/sections/main-page.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |

9 | {{ page.title | escape }} 10 |

11 |
12 | {{ page.content }} 13 |
14 |
15 | 16 | {% schema %} 17 | { 18 | "name": "t:sections.main-page.name", 19 | "tag": "section", 20 | "class": "spaced-section" 21 | } 22 | {% endschema %} 23 | -------------------------------------------------------------------------------- /src/sections/page.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |

9 | {%- if section.settings.page.title != blank -%} 10 | {{ section.settings.page.title | escape }} 11 | {%- else -%} 12 | Page title 13 | {%- endif -%} 14 |

15 |
16 | {%- if section.settings.page.content != blank -%} 17 | {{ section.settings.page.content }} 18 | {%- else -%} 19 |
20 | {{ 'page' | placeholder_svg_tag: 'page-placeholder' }} 21 |
22 | {%- endif -%} 23 |
24 |
25 | 26 | {% schema %} 27 | { 28 | "name": "t:sections.page.name", 29 | "tag": "section", 30 | "class": "spaced-section", 31 | "settings": [ 32 | { 33 | "type": "page", 34 | "id": "page", 35 | "label": "t:sections.page.settings.page.label" 36 | } 37 | ], 38 | "presets": [ 39 | { 40 | "name": "t:sections.page.presets.name" 41 | } 42 | ] 43 | } 44 | {% endschema %} 45 | -------------------------------------------------------------------------------- /src/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 |

{{ 'products.product.pickup_availability.pick_up_available_at_html' | t: location_name: closest_location.location.name }}

17 |

{{ closest_location.pick_up_time }}

18 | 25 | {%- else -%} 26 |

{{ 'products.product.pickup_availability.pick_up_unavailable_at_html' | t: location_name: closest_location.location.name }}

27 | {%- if pick_up_availabilities.size > 1 -%} 28 | 29 | {%- endif -%} 30 | {%- endif -%} 31 |
32 |
33 | 34 | 35 |
36 |

{{ product_variant.product.title | escape }}

37 | 38 |
39 | 40 | {%- unless product_variant.product.has_only_default_variant -%} 41 |

42 | {%- for product_option in product_variant.product.options_with_values -%} 43 | {{ product_option.name | escape }}:  44 | {%- for value in product_option.values -%} 45 | {%- if product_option.selected_value == value -%} 46 | {{ value | escape }} 47 | {%- endif -%} 48 | {%- endfor -%} 49 | {%- unless forloop.last -%}, {%- endunless forloop.last -%} 50 | {%- endfor -%} 51 |

52 | {%- endunless -%} 53 | 54 | 75 |
76 | {%- endif -%} 77 | -------------------------------------------------------------------------------- /src/sections/product-recommendations.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% if recommendations.performed and recommendations.products_count > 0 %} 13 |

{{ section.settings.heading | escape }}

14 | 27 | {% endif %} 28 |
29 | 30 | {% javascript %} 31 | class ProductRecommendations extends HTMLElement { 32 | constructor() { 33 | super(); 34 | 35 | const handleIntersection = (entries, observer) => { 36 | if (!entries[0].isIntersecting) return; 37 | observer.unobserve(this); 38 | 39 | fetch(this.dataset.url) 40 | .then(response => response.text()) 41 | .then(text => { 42 | const html = document.createElement('div'); 43 | html.innerHTML = text; 44 | const recommendations = html.querySelector('product-recommendations'); 45 | if (recommendations && recommendations.innerHTML.trim().length) { 46 | this.innerHTML = recommendations.innerHTML; 47 | } 48 | }) 49 | .catch(e => { 50 | console.error(e); 51 | }); 52 | } 53 | 54 | new IntersectionObserver(handleIntersection.bind(this), {rootMargin: '0px 0px 200px 0px'}).observe(this); 55 | } 56 | } 57 | 58 | customElements.define('product-recommendations', ProductRecommendations); 59 | {% endjavascript %} 60 | 61 | {% schema %} 62 | { 63 | "name": "t:sections.product-recommendations.name", 64 | "tag": "section", 65 | "class": "spaced-section", 66 | "settings": [ 67 | { 68 | "type": "paragraph", 69 | "content": "t:sections.product-recommendations.settings.paragraph__1.content" 70 | }, 71 | { 72 | "type": "text", 73 | "id": "heading", 74 | "default": "You may also like", 75 | "label": "t:sections.product-recommendations.settings.heading.label" 76 | }, 77 | { 78 | "type": "header", 79 | "content": "t:sections.product-recommendations.settings.header__2.content" 80 | }, 81 | { 82 | "type": "select", 83 | "id": "image_ratio", 84 | "options": [ 85 | { 86 | "value": "adapt", 87 | "label": "t:sections.product-recommendations.settings.image_ratio.options__1.label" 88 | }, 89 | { 90 | "value": "portrait", 91 | "label": "t:sections.product-recommendations.settings.image_ratio.options__2.label" 92 | }, 93 | { 94 | "value": "square", 95 | "label": "t:sections.product-recommendations.settings.image_ratio.options__3.label" 96 | } 97 | ], 98 | "default": "adapt", 99 | "label": "t:sections.product-recommendations.settings.image_ratio.label" 100 | }, 101 | { 102 | "type": "checkbox", 103 | "id": "show_secondary_image", 104 | "default": false, 105 | "label": "t:sections.product-recommendations.settings.show_secondary_image.label" 106 | }, 107 | { 108 | "type": "checkbox", 109 | "id": "add_image_padding", 110 | "default": false, 111 | "label": "t:sections.product-recommendations.settings.add_image_padding.label" 112 | }, 113 | { 114 | "type": "checkbox", 115 | "id": "show_vendor", 116 | "default": false, 117 | "label": "t:sections.product-recommendations.settings.show_vendor.label" 118 | } 119 | ] 120 | } 121 | {% endschema %} 122 | -------------------------------------------------------------------------------- /src/sections/rich-text.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 | {%- for block in section.blocks -%} 10 | {%- case block.type -%} 11 | {%- when 'heading' -%} 12 |

{{ block.settings.heading | escape }}

13 | {%- when 'text' -%} 14 |
{{ block.settings.text }}
15 | {%- when 'button' -%} 16 | 17 | {{ block.settings.button_label | escape }} 18 | 19 | {%- endcase -%} 20 | {%- endfor -%} 21 |
22 |
23 | 24 | {% schema %} 25 | { 26 | "name": "t:sections.rich-text.name", 27 | "tag": "section", 28 | "class": "spaced-section spaced-section--full-width", 29 | "settings": [ 30 | { 31 | "type": "select", 32 | "id": "color_scheme", 33 | "options": [ 34 | { 35 | "value": "accent-1", 36 | "label": "t:sections.rich-text.settings.color_scheme.options__1.label" 37 | }, 38 | { 39 | "value": "accent-2", 40 | "label": "t:sections.rich-text.settings.color_scheme.options__2.label" 41 | }, 42 | { 43 | "value": "background-1", 44 | "label": "t:sections.rich-text.settings.color_scheme.options__3.label" 45 | }, 46 | { 47 | "value": "background-2", 48 | "label": "t:sections.rich-text.settings.color_scheme.options__4.label" 49 | }, 50 | { 51 | "value": "inverse", 52 | "label": "t:sections.rich-text.settings.color_scheme.options__5.label" 53 | } 54 | ], 55 | "default": "background-1", 56 | "label": "t:sections.rich-text.settings.color_scheme.label" 57 | }, 58 | { 59 | "type": "checkbox", 60 | "id": "full_width", 61 | "default": true, 62 | "label": "t:sections.rich-text.settings.full_width.label" 63 | } 64 | ], 65 | "blocks": [ 66 | { 67 | "type": "heading", 68 | "name": "t:sections.rich-text.blocks.heading.name", 69 | "limit": 1, 70 | "settings": [ 71 | { 72 | "type": "text", 73 | "id": "heading", 74 | "default": "Talk about your brand", 75 | "label": "t:sections.rich-text.blocks.heading.settings.heading.label" 76 | }, 77 | { 78 | "type": "select", 79 | "id": "heading_size", 80 | "options": [ 81 | { 82 | "value": "small", 83 | "label": "t:sections.rich-text.blocks.heading.settings.heading_size.options__1.label" 84 | }, 85 | { 86 | "value": "medium", 87 | "label": "t:sections.rich-text.blocks.heading.settings.heading_size.options__2.label" 88 | } 89 | ], 90 | "default": "medium", 91 | "label": "t:sections.rich-text.blocks.heading.settings.heading_size.label" 92 | } 93 | ] 94 | }, 95 | { 96 | "type": "text", 97 | "name": "t:sections.rich-text.blocks.text.name", 98 | "limit": 1, 99 | "settings": [ 100 | { 101 | "type": "richtext", 102 | "id": "text", 103 | "default": "

Share information about your brand with your customers. Describe a product, make announcements, or welcome customers to your store.

", 104 | "label": "t:sections.rich-text.blocks.text.settings.text.label" 105 | } 106 | ] 107 | }, 108 | { 109 | "type": "button", 110 | "name": "t:sections.rich-text.blocks.button.name", 111 | "limit": 1, 112 | "settings": [ 113 | { 114 | "type": "text", 115 | "id": "button_label", 116 | "default": "Button label", 117 | "label": "t:sections.rich-text.blocks.button.settings.button_label.label" 118 | }, 119 | { 120 | "type": "url", 121 | "id": "button_link", 122 | "label": "t:sections.rich-text.blocks.button.settings.button_link.label" 123 | }, 124 | { 125 | "type": "checkbox", 126 | "id": "button_style_secondary", 127 | "default": false, 128 | "label": "t:sections.rich-text.blocks.button.settings.button_style_secondary.label" 129 | } 130 | ] 131 | } 132 | ], 133 | "presets": [ 134 | { 135 | "name": "t:sections.rich-text.presets.name", 136 | "blocks": [ 137 | { 138 | "type": "heading" 139 | }, 140 | { 141 | "type": "text" 142 | }, 143 | { 144 | "type": "button" 145 | } 146 | ] 147 | } 148 | ] 149 | } 150 | {% endschema %} 151 | -------------------------------------------------------------------------------- /src/snippets/article-card.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders an article card for a given blog with settings to either show the image or not. 3 | 4 | Accepts: 5 | - blog: {Object} Blog object 6 | - article: {Object} Article object 7 | - show_image: {String} The setting either show the article image or not. If it's not included it will show the image by default 8 | 9 | Usage: 10 | {% render 'article-card' blog: blog, article: article, show_image: section.settings.show_image %} 11 | {% endcomment %} 12 | 13 |
14 | 15 | {%- if show_image == true and article.image -%} 16 |
17 |
18 | {{ article.image.src.alt | escape }} 33 |
34 |
35 | {%- endif -%} 36 | 37 |
38 | {%- for block in section.blocks -%} 39 | {%- case block.type -%} 40 | {%- when 'title'-%} 41 |
42 |

43 | {{ article.title | escape }} 44 |

45 | {%- if block.settings.show_date -%} 46 | 47 | {{- article.published_at | time_tag: format: 'month_year' -}} 48 | 49 | {%- endif -%} 50 | {%- if block.settings.show_author -%} 51 | {{ article.author -}} 52 | {%- endif -%} 53 |
54 | 55 | {%- when 'summary'-%} 56 | {%- if article.excerpt.size > 0 or article.content.size > 0 -%} 57 |

58 | {%- if article.excerpt.size > 0 -%} 59 | {{ article.excerpt | strip_html | truncatewords: 30 }} 60 | {%- else -%} 61 | {{ article.content | strip_html | truncatewords: 30 }} 62 | {%- endif -%} 63 |

64 | {%- endif -%} 65 | 66 | {%- when 'link'-%} 67 |
68 | 69 | {{ 'blogs.article.read_more' | t }} 70 | 71 | 72 | {%- if article.comments_count > 0 and blog.comments_enabled? -%} 73 | {{ 'blogs.article.comments' | t: count: article.comments_count }} 74 | {%- endif -%} 75 |
76 | {%- endcase -%} 77 | {%- endfor -%} 78 |
79 |
80 |
81 | -------------------------------------------------------------------------------- /src/snippets/cart-notification.liquid: -------------------------------------------------------------------------------- 1 | 2 |
3 | 19 |
20 |
21 | {% style %} 22 | .cart-notification { 23 | display: none; 24 | } 25 | {% endstyle %} 26 | -------------------------------------------------------------------------------- /src/snippets/icon-3d-model.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-account.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-arrow.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-caret.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-cart-empty.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-cart.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-checkmark.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-clipboard.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-close-small.liquid: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/snippets/icon-close.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-discount.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-error.liquid: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/snippets/icon-facebook.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-filter.liquid: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/snippets/icon-hamburger.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-instagram.liquid: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/snippets/icon-minus.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-padlock.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-pinterest.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-play.liquid: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/snippets/icon-plus.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-remove.liquid: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/snippets/icon-share.liquid: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/snippets/icon-snapchat.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-success.liquid: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/snippets/icon-tick.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-tiktok.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-tumblr.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-twitter.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-unavailable.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-vimeo.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-youtube.liquid: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/snippets/icon-zoom.liquid: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/snippets/meta-tags.liquid: -------------------------------------------------------------------------------- 1 | {%- liquid 2 | assign og_title = page_title | default: shop.name 3 | assign og_url = canonical_url | default: shop.url 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 == 'collection' 12 | assign og_type = 'product.group' 13 | elsif request.page_type == 'password' 14 | assign og_url = shop.url 15 | endif 16 | %} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {%- if page_image -%} 25 | 26 | 27 | 28 | 29 | {%- endif -%} 30 | 31 | {%- if request.page_type == 'product' -%} 32 | 33 | 34 | {%- endif -%} 35 | 36 | {%- if settings.social_twitter_link != blank -%} 37 | 38 | {%- endif -%} 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/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 | - class: {String} (optional) Appended to container element's class attribute 13 | {% endcomment %} 14 | 15 | 16 | 17 | 18 | {%- if paginate.parts.size > 0 -%} 19 |
20 | 53 |
54 | {%- endif -%} 55 | -------------------------------------------------------------------------------- /src/snippets/price.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders a list of product's price (regular, sale) 3 | 4 | Accepts: 5 | - product: {Object} Product Liquid object (optional) 6 | - use_variant: {Boolean} Renders selected or first variant price instead of overall product pricing (optional) 7 | - show_badges: {Boolean} Renders 'Sale' and 'Sold Out' tags if the product matches the condition (optional) 8 | - price_class: {String} Adds a price class to the price element (optional) 9 | 10 | Usage: 11 | {% render 'price', product: product %} 12 | {% endcomment %} 13 | {%- liquid 14 | if use_variant 15 | assign target = product.selected_or_first_available_variant 16 | else 17 | assign target = product 18 | endif 19 | 20 | assign compare_at_price = target.compare_at_price 21 | assign price = target.price | default: 1999 22 | assign available = target.available | default: false 23 | assign money_price = price | money 24 | 25 | if target == product and product.price_varies 26 | assign money_price = 'products.product.price.from_price_html' | t: price: money_price 27 | endif 28 | -%} 29 | 30 |
35 |
36 | {%- comment -%} 37 | Explanation of description list: 38 | - div.price__regular: Displayed when there are no variants on sale 39 | - div.price__sale: Displayed when a variant is a sale 40 | - div.price__availability: Displayed when the product is sold out 41 | {%- endcomment -%} 42 |
43 |
44 | {{ 'products.product.price.regular_price' | t }} 45 |
46 |
47 | 48 | {{ money_price }} 49 | 50 |
51 |
52 |
53 |
54 | {{ 'products.product.price.regular_price' | t }} 55 |
56 |
57 | 58 | {{ compare_at_price | money }} 59 | 60 |
61 |
62 | {{ 'products.product.price.sale_price' | t }} 63 |
64 |
65 | 66 | {{ money_price }} 67 | 68 |
69 |
70 | 71 |
{{ 'products.product.price.unit_price' | t }}
72 |
73 | {{- product.selected_or_first_available_variant.unit_price | money -}} 74 | 75 |  {{ 'accessibility.unit_price_separator' | t }}  76 | 77 | {%- if product.selected_or_first_available_variant.unit_price_measurement.reference_value != 1 -%} 78 | {{- product.selected_or_first_available_variant.unit_price_measurement.reference_value -}} 79 | {%- endif -%} 80 | {{ product.selected_or_first_available_variant.unit_price_measurement.reference_unit }} 81 | 82 |
83 |
84 |
85 | {%- if show_badges -%} 86 | 89 | 90 | 93 | {%- endif -%} 94 |
95 | -------------------------------------------------------------------------------- /src/snippets/product-card-placeholder.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders a product card placeholder 3 | 4 | Usage: 5 | {% render 'product-card-placeholder' %} 6 | {% endcomment %} 7 | 8 |
9 | {{ 'onboarding.product_title' | t }} 10 | 11 |
12 |

{{ 'onboarding.product_title' | t }}

13 |
14 | 15 |
16 |
17 | {{ 'accessibility.vendor' | t }} 18 |
{{ 'products.product.vendor' | t }}
19 | {% render 'price' %} 20 |
21 |
22 |
23 | -------------------------------------------------------------------------------- /src/snippets/social-sharing.liquid: -------------------------------------------------------------------------------- 1 | 46 | -------------------------------------------------------------------------------- /src/styles/sections/header.css: -------------------------------------------------------------------------------- 1 | .header__test { 2 | @apply border-purple-600 border-8; 3 | } -------------------------------------------------------------------------------- /src/styles/sections/image-banner.css: -------------------------------------------------------------------------------- 1 | .image-banner__test { 2 | @apply border-green-600 border-8; 3 | } -------------------------------------------------------------------------------- /src/styles/theme.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss/base"; 2 | @import "tailwindcss/components"; 3 | @import "tailwindcss/utilities"; 4 | 5 | @import './utils/base.css'; 6 | @import './utils/autocomplete.css'; 7 | 8 | .theme__test { 9 | @apply border-red-600 border-8; 10 | } -------------------------------------------------------------------------------- /src/styles/utils/autocomplete.css: -------------------------------------------------------------------------------- 1 | .autocomplete { 2 | color: #777; 3 | } -------------------------------------------------------------------------------- /src/styles/utils/base.css: -------------------------------------------------------------------------------- 1 | .special_input { 2 | appearance: none; 3 | } -------------------------------------------------------------------------------- /src/templates/404.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/templates/article.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-article", 5 | "blocks": { 6 | "featured_image": { 7 | "type": "featured_image" 8 | }, 9 | "title": { 10 | "type": "title" 11 | }, 12 | "content": { 13 | "type": "content" 14 | }, 15 | "social_sharing": { 16 | "type": "social_sharing" 17 | } 18 | }, 19 | "block_order": [ 20 | "featured_image", 21 | "title", 22 | "content", 23 | "social_sharing" 24 | ] 25 | } 26 | }, 27 | "order": ["main"] 28 | } 29 | -------------------------------------------------------------------------------- /src/templates/blog.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-blog", 5 | "blocks": { 6 | "title": { 7 | "type": "title" 8 | }, 9 | "summary": { 10 | "type": "summary" 11 | }, 12 | "link": { 13 | "type": "link" 14 | } 15 | }, 16 | "block_order": [ 17 | "title", 18 | "summary", 19 | "link" 20 | ] 21 | } 22 | }, 23 | "order": [ 24 | "main" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/templates/cart.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "cart-items": { 4 | "type": "main-cart-items" 5 | }, 6 | "cart-footer": { 7 | "type": "main-cart-footer", 8 | "blocks": { 9 | "subtotal": { 10 | "type": "subtotal" 11 | }, 12 | "buttons": { 13 | "type": "buttons" 14 | } 15 | }, 16 | "block_order": [ 17 | "subtotal", 18 | "buttons" 19 | ] 20 | } 21 | }, 22 | "order": [ 23 | "cart-items", 24 | "cart-footer" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/templates/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "banner": { 4 | "type": "main-collection-banner" 5 | }, 6 | "product-grid": { 7 | "type": "main-collection-product-grid" 8 | } 9 | }, 10 | "order": [ 11 | "banner", 12 | "product-grid" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/templates/customers/activate_account.liquid: -------------------------------------------------------------------------------- 1 | {{ 'customer.css' | asset_url | stylesheet_tag }} 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

13 | {{ 'customer.activate_account.title' | t }} 14 |

15 |

16 | {{ 'customer.activate_account.subtext' | t }} 17 |

18 | {%- form 'activate_customer_password' -%} 19 | {%- if form.errors -%} 20 |

21 | 24 | {{ 'templates.contact.form.error_heading' | t }} 25 |

26 | 40 | {%- endif -%} 41 | 42 |
43 | 54 | 57 | {%- if form.errors contains 'password' -%} 58 | 59 | 62 | {{ form.errors.translated_fields['password'] | capitalize }} {{ form.errors.messages['password'] }} 63 | 64 | {%- endif -%} 65 |
66 | 67 |
68 | 79 | 82 | {%- if form.errors contains 'password_confirmation' -%} 83 | 84 | 87 | {{ form.errors.translated_fields['password_confirmation'] | capitalize }} {{ form.errors.messages['password_confirmation'] }} 88 | 89 | {%- endif -%} 90 |
91 | 92 | 95 | 98 | {%- endform -%} 99 |
100 | -------------------------------------------------------------------------------- /src/templates/customers/register.liquid: -------------------------------------------------------------------------------- 1 | {{ 'customer.css' | asset_url | stylesheet_tag }} 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

13 | {{ 'customer.register.title' | t }} 14 |

15 | {%- form 'create_customer', novalidate: 'novalidate' -%} 16 | {%- if form.errors -%} 17 |

18 | 21 | {{ 'templates.contact.form.error_heading' | t }} 22 |

23 | 37 | {%- endif -%} 38 |
39 | 47 | 50 |
51 |
52 | 60 | 63 |
64 |
65 | 80 | 83 |
84 | {%- if form.errors contains 'email' -%} 85 | 86 | 89 | {{ form.errors.translated_fields['email'] | capitalize }} {{ form.errors.messages['email'] }}. 90 | 91 | {%- endif -%} 92 |
93 | 104 | 107 |
108 | {%- if form.errors contains 'password' -%} 109 | 110 | 113 | {{ form.errors.translated_fields['password'] | capitalize }} {{ form.errors.messages['password'] }}. 114 | 115 | {%- endif -%} 116 | 119 | {%- endform -%} 120 |
121 | -------------------------------------------------------------------------------- /src/templates/customers/reset_password.liquid: -------------------------------------------------------------------------------- 1 | {{ 'customer.css' | asset_url | stylesheet_tag }} 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

13 | {{ 'customer.reset_password.title' | t }} 14 |

15 |

16 | {{ 'customer.reset_password.subtext' | t: email: email }} 17 |

18 | {%- form 'reset_customer_password' -%} 19 | {%- if form.errors -%} 20 |

21 | {{ 'accessibility.error' | t }} 22 | 25 | {{ 'templates.contact.form.error_heading' | t }} 26 |

27 | 41 | {%- endif -%} 42 | 43 |
44 | 55 | 58 | {%- if form.errors contains 'password' -%} 59 | 60 | 63 | {{ form.errors.translated_fields['password'] | capitalize }} {{ form.errors.messages['password'] }} 64 | 65 | {%- endif -%} 66 |
67 | 68 |
69 | 80 | 83 | {%- if form.errors contains 'password_confirmation' -%} 84 | 85 | 88 | {{ form.errors.translated_fields['password_confirmation'] | capitalize }} {{ form.errors.messages['password_confirmation'] }} 89 | 90 | {%- endif -%} 91 |
92 | 93 | 96 | {%- endform -%} 97 |
98 | -------------------------------------------------------------------------------- /src/templates/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "image_banner": { 4 | "type": "image-banner", 5 | "settings": { 6 | "desktop_text_box_position": "flex-end" 7 | }, 8 | "blocks": { 9 | "heading": { 10 | "type": "heading", 11 | "settings": { 12 | "heading": "Talk about your brand" 13 | } 14 | }, 15 | "button": { 16 | "type": "buttons", 17 | "settings": { 18 | "button_label_1": "Shop all", 19 | "button_link_1": "shopify:\/\/collections\/all", 20 | "button_label_2": "" 21 | } 22 | } 23 | }, 24 | "block_order": [ 25 | "heading", 26 | "button" 27 | ] 28 | }, 29 | "featured_products": { 30 | "type": "featured-collection", 31 | "settings": { 32 | "title": "Featured products" 33 | } 34 | }, 35 | "image_text": { 36 | "type": "image-with-text", 37 | "blocks": { 38 | "heading": { 39 | "type": "heading" 40 | }, 41 | "text": { 42 | "type": "text" 43 | }, 44 | "button": { 45 | "type": "button" 46 | } 47 | }, 48 | "block_order": [ 49 | "heading", 50 | "text", 51 | "button" 52 | ] 53 | } 54 | }, 55 | "order": [ 56 | "image_banner", 57 | "featured_products", 58 | "image_text" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /src/templates/list-collections.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-list-collections" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/templates/page.contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-page" 5 | }, 6 | "form": { 7 | "type": "contact-form", 8 | "settings": { 9 | "heading": "" 10 | } 11 | } 12 | }, 13 | "order": [ 14 | "main", 15 | "form" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/templates/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-page" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/templates/password.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": "password", 3 | "sections": { 4 | "main": { 5 | "type": "newsletter", 6 | "settings": { 7 | "full_width": false 8 | }, 9 | "blocks": { 10 | "heading": { 11 | "type": "heading", 12 | "settings": { 13 | "heading": "Opening soon" 14 | } 15 | }, 16 | "paragraph": { 17 | "type": "paragraph", 18 | "settings": { 19 | "paragraph": "

Be the first to know when we launch.

" 20 | } 21 | }, 22 | "email_form": { 23 | "type": "email_form" 24 | } 25 | }, 26 | "block_order": [ 27 | "heading", 28 | "paragraph", 29 | "email_form" 30 | ] 31 | } 32 | }, 33 | "order": [ 34 | "main" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /src/templates/product.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-product", 5 | "blocks": { 6 | "vendor": { 7 | "type": "text", 8 | "settings": { 9 | "text_style": "uppercase", 10 | "text": "{{ product.vendor }}" 11 | } 12 | }, 13 | "title": { 14 | "type": "title" 15 | }, 16 | "subtitle": { 17 | "type": "text", 18 | "settings": { 19 | "text": "{{ product.metafields.descriptors.subtitle.value }}", 20 | "text_style": "subtitle" 21 | } 22 | }, 23 | "price": { 24 | "type": "price" 25 | }, 26 | "variant_picker": { 27 | "type": "variant_picker" 28 | }, 29 | "quantity_selector": { 30 | "type": "quantity_selector" 31 | }, 32 | "buy_buttons": { 33 | "type": "buy_buttons" 34 | }, 35 | "description": { 36 | "type": "description" 37 | }, 38 | "share": { 39 | "type": "share" 40 | } 41 | }, 42 | "block_order": [ 43 | "vendor", 44 | "title", 45 | "subtitle", 46 | "price", 47 | "variant_picker", 48 | "quantity_selector", 49 | "buy_buttons", 50 | "description", 51 | "share" 52 | ] 53 | }, 54 | "product-recommendations": { 55 | "type": "product-recommendations" 56 | } 57 | }, 58 | "order": [ 59 | "main", 60 | "product-recommendations" 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /src/templates/search.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-search" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/translation.yml: -------------------------------------------------------------------------------- 1 | source_language: en 2 | target_languages: [bg-BG, cs, da, de, el, es, fi, fr, hr-HR, hu, id, it, ja, ko, lt-LT, nb, nl, pl, pt-BR, pt-PT, ro-RO, ru, sk-SK, sl-SI, sv, th, tr, vi, zh-CN, zh-TW] 3 | components: 4 | - name: buyer 5 | audience: buyer 6 | scheme: 'online-store-theme' 7 | paths: 8 | - locales/{{language}}.json 9 | - name: merchant 10 | target_languages: [cs, da, de, es, fi, fr, it, ja, ko, nb, nl, pl, pt-BR, pt-PT, sv, th, tr, vi, zh-CN, zh-TW] 11 | audience: merchant 12 | scheme: 'online-store-theme' 13 | paths: 14 | - locales/{{language}}.schema.json 15 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: ['./src/**/*.liquid', './src/**/*.html', './src/**/*.js'], 4 | darkMode: false, // or 'media' or 'class' 5 | theme: { 6 | extend: {}, 7 | }, 8 | variants: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | }; 13 | --------------------------------------------------------------------------------