├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .prettierrc.json ├── .shopifyignore ├── LICENSE ├── README.md ├── assets ├── manifest.json.liquid ├── service-worker.js └── styles.css ├── config ├── settings_data.json └── settings_schema.json ├── layout └── theme.liquid ├── locales └── en.default.json ├── package.json ├── postcss.config.js ├── sections ├── main-404.liquid └── main-section.liquid ├── shopify.theme.toml ├── snippets ├── application.liquid ├── development-screen-indicator.liquid ├── image.liquid └── typography.liquid ├── styles.css ├── tailwind.config.ts ├── templates ├── 404.json ├── article.json ├── blog.json ├── cart.json ├── collection.json ├── customers │ ├── account.json │ ├── activate_account.json │ ├── addresses.json │ ├── login.json │ ├── order.json │ ├── register.json │ └── reset_password.json ├── gift_card.liquid ├── index.json ├── list-collections.json ├── page.json ├── password.json ├── product.json └── search.json └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | charset = utf-8 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | theme-check: 7 | name: Theme Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: Theme Check 12 | uses: shopify/theme-check-action@v1 13 | with: 14 | token: ${{ github.token }} 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # Dependencies 4 | node_modules 5 | pnpm-lock.yaml 6 | package-lock.json 7 | yarn.lock 8 | bun.lockb 9 | 10 | # Editor directories and files 11 | .idea 12 | dist 13 | .vscode 14 | 15 | # Misc 16 | .DS_Store 17 | .DS_Store? 18 | ._* 19 | Thumbs.db 20 | logs 21 | *.log 22 | .env 23 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "overrides": [ 5 | { 6 | "files": "*.liquid", 7 | "options": { 8 | "singleQuote": false, 9 | "singleLineLinkTags": true, 10 | "liquidSingleQuote": true, 11 | "bracketSameLine": true 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.shopifyignore: -------------------------------------------------------------------------------- 1 | # See https://shopify.dev/themes/tools/cli#excluding-files-from-shopify-cli for more about ignoring files while using Shopify CLI 2 | 3 | .DS_Store 4 | .DS_Store? 5 | .Spotlight-V100 6 | .Trashes 7 | ehthumbs.db 8 | Thumbs.db 9 | node_modules 10 | package.json 11 | package-lock.json 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Odestry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Theme PWA 2 | 3 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat&colorA=338fbb&colorB=1c1c1c&logoColor=ffffff)](https://github.com/odestry/.github/blob/main/CONTRIBUTING.md) 4 | [![CI](https://img.shields.io/github/actions/workflow/status/odestry/theme-pwa/ci.yml?style=flat&label=CI&colorA=338fbb&colorB=1c1c1c&logoColor=ffffff)](https://github.com/odestry/theme-pwa/blob/main/.github/workflows/ci.yml) 5 | [![Discord Shield](https://img.shields.io/discord/983602196493004820?style=flat&colorA=338fbb&colorB=1c1c1c&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/blanklob-community-983602196493004820) 6 | 7 | [Usage](#usage) | 8 | [Tools](#tools) | 9 | [Contributing](#contributing) | 10 | [About](#about) | 11 | [License](#license) 12 | 13 | An example theme with built-in support for Progressive Web Apps (PWAs) and service workers. To ensure optimal functionality, make sure to configure all necessary files within the settings and assets folders. Please be aware that some browsers, such as Safari, do not support PWAs. 14 | 15 | ## Usage 16 | 17 | To get started clone the template by clicking the **Use this template** button or by running the following command: 18 | 19 | ```bash 20 | git clone https://github.com/odestry/theme-pwa.git 21 | ``` 22 | 23 | ### Running the development server 24 | 25 | To run the development server, you'll need to have [Shopify CLI](#shopify-cli) as well as [Node.js](https://nodejs.org) installed on your machine. 26 | 27 | 1. Install the dependencies 28 | 29 | ```bash 30 | npm install 31 | ``` 32 | 33 | 2. Connect to your store 34 | 35 | To connect your store update the `shopify.theme.toml` file with your store's information. 36 | 37 | ```toml 38 | [environments.development] 39 | store = "john-apparel.myshopify.com" 40 | ``` 41 | 42 | 3. Start the development server 43 | 44 | ```bash 45 | npm run dev 46 | ``` 47 | 48 | After authenticating, this will start a local development server running at `https://localhost:9292` that you can use to preview your changes. 49 | 50 | ## Tools 51 | 52 | There are a number of really useful tools that the Shopify Themes team uses during development. This theme is already set up to work with these tools. 53 | 54 | ### Shopify CLI 55 | 56 | [Shopify CLI](https://github.com/Shopify/shopify-cli) helps you build Shopify themes faster and is used to automate and enhance your local development workflow. It comes bundled with a suite of commands for developing Shopify themes—everything from working with themes on a Shopify store (e.g. creating, publishing, deleting themes) or launching a development server for local theme development. 57 | 58 | You can follow this [quick start guide for theme developers](https://github.com/Shopify/shopify-cli#quick-start-guide-for-theme-developers) to get started. 59 | 60 | ### Tailwind CSS 61 | 62 | [Tailwind CSS](https://tailwindcss.com) is a utility-first CSS framework for rapidly building custom storefront interfaces. It's a great way to build Shopify themes and sections quickly. You can find the configuration file at `tailwind.config.ts`. We use Vite to compile Tailwind CSS. 63 | 64 | ### Theme Check 65 | 66 | We recommend using [Theme Check](https://github.com/shopify/theme-check) as a way to validate and lint your Shopify themes. 67 | 68 | Theme Check is a command line tool that runs a series of tests against your theme code to surface errors, deprecations, and potential bugs. It's a great way to ensure your theme is up to date with the latest best practices and that you're not using any deprecated Liquid or JSON fields. 69 | 70 | You can also run it from a terminal with the following Shopify CLI command: 71 | 72 | ```bash 73 | shopify theme check 74 | ``` 75 | 76 | ### Continuous Integration 77 | 78 | This theme uses [GitHub Actions](https://github.com/features/actions) to maintain the quality of the theme. [This is a starting point](https://github.com/odestry/theme-pwa/blob/main/.github/workflows/ci.yml) and what we suggest to use in order to ensure you're building better themes. Feel free to build off of it! 79 | 80 | #### Shopify/lighthouse-ci-action 81 | 82 | We love fast websites! Which is why we created [Shopify/lighthouse-ci-action](https://github.com/Shopify/lighthouse-ci-action). This runs a series of [Google Lighthouse](https://developers.google.com/web/tools/lighthouse) audits for the home, product and collections pages on a store to ensure code that gets added doesn't degrade storefront performance over time. 83 | 84 | #### Shopify/theme-check-action 85 | 86 | This theme runs [Theme Check](https://github.com/Shopify/theme-check-action) on every commit via [Shopify/theme-check-action](https://github.com/Shopify/theme-check-action). 87 | 88 | ## Contributing 89 | 90 | We'd love your help! Please read our [contributing guide](https://github.com/odestry/.github/blob/main/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements. 91 | 92 | ## About 93 | 94 | Odestry isn't just a community; it's a group of dedicated folks all aiming to build better commerce together. Our mission is to create a supportive and open space where anyone, regardless of experience, can connect, learn, and grow. Here, you'll get inspired, have real talks, and find plenty of resources and open source tools to help you build. Whether you're looking to network, find opportunities, or just have meaningful conversations, join us and be part of a community that values authenticity, collaboration, and innovation. [Learn more](https://odestry.com). 95 | 96 | ## License 97 | 98 | Copyright (c) 2024-present Odestry. See [LICENSE](/LICENSE.md) for further details. 99 | -------------------------------------------------------------------------------- /assets/manifest.json.liquid: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": {{ settings.application_name | json }}, 3 | "name": {{ settings.application_name | json }}, 4 | "description": {{ settings.application_name | json }}, 5 | "categories": ["shopping"], 6 | {% if settings.application_theme_color != blank %} 7 | "theme_color": {{ settings.application_theme_color | json }}, 8 | {% endif %} 9 | {% if settings.application_background_color != blank %} 10 | "background_color": {{ settings.application_background_color | json }}, 11 | {% endif %} 12 | "display": "standalone", 13 | "orientation": "any", 14 | "dir": "auto", 15 | {% if settings.application_icon != blank %} 16 | "icons": [ 17 | {% if settings.application_maskable_icon != blank %} 18 | { 19 | "src": {{ settings.application_icon | image_url: width: 48, height: 48 | json }}, 20 | "sizes": "48x48", 21 | "purpose": "maskable" 22 | }, 23 | { 24 | "src": {{ settings.application_icon | image_url: width: 72, height: 72 | json }}, 25 | "sizes": "72x72", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": {{ settings.application_icon | image_url: width: 96, height: 96 | json }}, 30 | "sizes": "96x96", 31 | "purpose": "maskable" 32 | }, 33 | { 34 | "src": {{ settings.application_icon | image_url: width: 144, height: 144 | json }}, 35 | "sizes": "144x144", 36 | "purpose": "maskable" 37 | }, 38 | { 39 | "src": {{ settings.application_icon | image_url: width: 168, height: 168 | json }}, 40 | "sizes": "168x168", 41 | "purpose": "maskable" 42 | }, 43 | { 44 | "src": {{ settings.application_icon | image_url: width: 192, height: 192 | json }}, 45 | "sizes": "192x192", 46 | "purpose": "maskable" 47 | }, 48 | { 49 | "src": {{ settings.application_icon | image_url: width: 256, height: 256 | json }}, 50 | "sizes": "256x256", 51 | "purpose": "maskable" 52 | }, 53 | { 54 | "src": {{ settings.application_icon | image_url: width: 512, height: 512 | json }}, 55 | "sizes": "512x512", 56 | "purpose": "maskable" 57 | }, 58 | {% endif %} 59 | { 60 | "src": {{ settings.application_icon | image_url: width: 48, height: 48 | json }}, 61 | "sizes": "48x48" 62 | }, 63 | { 64 | "src": {{ settings.application_icon | image_url: width: 72, height: 72 | json }}, 65 | "sizes": "72x72" 66 | }, 67 | { 68 | "src": {{ settings.application_icon | image_url: width: 96, height: 96 | json }}, 69 | "sizes": "96x96" 70 | }, 71 | { 72 | "src": {{ settings.application_icon | image_url: width: 144, height: 144 | json }}, 73 | "sizes": "144x144" 74 | }, 75 | { 76 | "src": {{ settings.application_icon | image_url: width: 168, height: 168 | json }}, 77 | "sizes": "168x168" 78 | }, 79 | { 80 | "src": {{ settings.application_icon | image_url: width: 192, height: 192 | json }}, 81 | "sizes": "192x192" 82 | }, 83 | { 84 | "src": {{ settings.application_icon | image_url: width: 256, height: 256 | json }}, 85 | "sizes": "256x256" 86 | }, 87 | { 88 | "src": {{ settings.application_icon | image_url: width: 512, height: 512 | json }}, 89 | "sizes": "512x512" 90 | } 91 | ], 92 | {% endif %} 93 | "related_applications": [ 94 | { 95 | "platform": "webapp", 96 | "url": {{ 'manifest.json' | asset_url | prepend: 'https:' | json }} 97 | } 98 | ], 99 | "shortcuts" : [ 100 | {% if settings.application_shortcut_name and settings.application_shortcut_link %} 101 | { 102 | "name": {{ settings.application_shortcut_name | json }}, 103 | "short_name": {{ settings.application_shortcut_name | json }}, 104 | {% if settings.application_shortcut_icon != blank %} 105 | "icons": [ 106 | { 107 | "src": {{ settings.application_shortcut_icon | image_url: width: 512, height: 512 | json }}, 108 | "sizes": "512x512" 109 | } 110 | ], 111 | {% endif %} 112 | "url": {{ settings.application_shortcut_link | append: '/?utm_source=homescreen' | json }} 113 | } 114 | {% endif %} 115 | ], 116 | "start_url": "/?utm_source=pwa", 117 | "scope": "/", 118 | "id": "/?utm_source=pwa" 119 | } 120 | -------------------------------------------------------------------------------- /assets/service-worker.js: -------------------------------------------------------------------------------- 1 | // Define a name for our cache 2 | const CACHE_NAME = 'v1_cache'; 3 | 4 | const urlsToCache = [ 5 | '/', 6 | // Add more URLs and assets that you want to cache... 7 | ]; 8 | 9 | // Installation event. This is triggered as soon as the worker executes, 10 | // and it's only called once per service worker. This is the perfect time 11 | // to cache our static assets. 12 | self.addEventListener('install', event => { 13 | event.waitUntil( 14 | // Open the cache 15 | caches.open(CACHE_NAME) 16 | .then((cache) => { 17 | // Add all URLs to the cache 18 | return cache.addAll(urlsToCache) 19 | .then(() => { 20 | // Skip waiting forces the waiting service worker to become the active service worker. 21 | self.skipWaiting() 22 | }); 23 | }) 24 | // If something went wrong with the cache, log it. 25 | .catch(error => console.log('Failed to cache', error)) 26 | ); 27 | }); 28 | 29 | // Activate event. This is a good time to clean up old caches. 30 | self.addEventListener('activate', event => { 31 | const cacheWhitelist = [CACHE_NAME]; 32 | 33 | event.waitUntil( 34 | // Get all cache keys (cache names) 35 | caches.keys() 36 | .then(cacheNames => { 37 | // Return a single promise that resolves when all old caches are deleted. 38 | return Promise.all( 39 | cacheNames.map(cacheName => { 40 | // If this cache name isn't present in the whitelist, delete it. 41 | if (cacheWhitelist.indexOf(cacheName) === -1) { 42 | return caches.delete(cacheName); 43 | } 44 | }) 45 | ); 46 | }) 47 | .then(() => { 48 | // Enables the newly installed service worker to take control of the page 49 | // and start handling fetch events. 50 | self.clients.claim() 51 | }) 52 | ); 53 | }); 54 | 55 | // Fetch event. This triggers every time a page controlled by this service worker makes a request. 56 | self.addEventListener('fetch', event => { 57 | event.respondWith( 58 | // Check if the request is for something we have in our cache 59 | caches.match(event.request) 60 | .then(response => { 61 | // If the response is in the cache, return it, else fetch it from the network. 62 | if (response) { 63 | return response; 64 | } 65 | return fetch(event.request); 66 | }) 67 | ); 68 | }); 69 | -------------------------------------------------------------------------------- /assets/styles.css: -------------------------------------------------------------------------------- 1 | *,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}html{-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;text-rendering:optimizeLegibility;font-variant-ligatures:none}details>summary{list-style-type:none}details summary::-webkit-details-marker{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.static{position:static}.fixed{position:fixed}.sticky{position:sticky}.bottom-2{bottom:.5rem}.left-2{left:.5rem}.top-0{top:0}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.aspect-auto{aspect-ratio:auto}.aspect-square{aspect-ratio:1 / 1}.h-12{height:3rem}.h-8{height:2rem}.h-auto{height:auto}.min-h-screen{min-height:100vh}.w-8{width:2rem}.w-full{width:100%}.max-w-prose{max-width:65ch}.max-w-xl{max-width:36rem}.scroll-m-20{scroll-margin:5rem}.list-none{list-style-type:none}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-4{gap:1rem}.scroll-smooth{scroll-behavior:smooth}.rounded-full{border-radius:9999px}.rounded-sm{border-radius:.125rem}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity))}.object-cover{-o-object-fit:cover;object-fit:cover}.p-3{padding:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-\[calc\(var\(--spacing\)\*0\.75\)\]{padding-top:calc(var(--spacing) * .75);padding-bottom:calc(var(--spacing) * .75)}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-medium{font-weight:500}.italic{font-style:italic}.tracking-tight{letter-spacing:-.025em}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.no-underline{text-decoration-line:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.outline-none{outline:2px solid transparent;outline-offset:2px}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width: 640px){.sm\:block{display:block}.sm\:hidden{display:none}}@media (min-width: 768px){.md\:block{display:block}.md\:hidden{display:none}.md\:aspect-auto{aspect-ratio:auto}.md\:gap-5{gap:1.25rem}.md\:py-16{padding-top:4rem;padding-bottom:4rem}}@media (min-width: 1024px){.lg\:block{display:block}.lg\:hidden{display:none}.lg\:h-\[30rem\]{height:30rem}.lg\:py-20{padding-top:5rem;padding-bottom:5rem}.lg\:py-4{padding-top:1rem;padding-bottom:1rem}.lg\:py-8{padding-top:2rem;padding-bottom:2rem}.lg\:py-\[--spacing\]{padding-top:var(--spacing);padding-bottom:var(--spacing)}.lg\:text-5xl{font-size:3rem;line-height:1}.lg\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width: 1280px){.xl\:block{display:block}.xl\:hidden{display:none}}@media (min-width: 1536px){.\32xl\:block{display:block}.\32xl\:hidden{display:none}} 2 | -------------------------------------------------------------------------------- /config/settings_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "current": { 3 | "application_icon": "shopify:\/\/shop_images\/favicon.png", 4 | "content_for_index": [ 5 | 6 | ] 7 | }, 8 | "presets": { 9 | "Default": { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /config/settings_schema.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "theme_info", 4 | "theme_name": "Theme PWA", 5 | "theme_version": "0.1.0", 6 | "theme_author": "Odestry", 7 | "theme_documentation_url": "https:\/\/odestry.blanklob.com", 8 | "theme_support_url": "https:\/\/odestry.blanklob.com" 9 | }, 10 | { 11 | "name": "Application", 12 | "settings": [ 13 | { 14 | "type": "checkbox", 15 | "id": "enable_application_mode", 16 | "label": "Enable Application Mode", 17 | "info": "This will enable progressive web application in your store.", 18 | "default": true 19 | }, 20 | { 21 | "type": "text", 22 | "id": "application_name", 23 | "label": "Application name", 24 | "default": "Odestry" 25 | }, 26 | { 27 | "type": "image_picker", 28 | "id": "application_icon", 29 | "label": "Application icon", 30 | "info": "This icon will be used for the application icon on the home screen." 31 | }, 32 | { 33 | "type": "image_picker", 34 | "id": "application_maskable_icon", 35 | "label": "Application maskable icon", 36 | "info": "This icon needs to support a variety of shapes and must not have a background." 37 | }, 38 | { 39 | "type": "header", 40 | "content": "Colors" 41 | }, 42 | { 43 | "type": "color", 44 | "id": "application_theme_color", 45 | "label": "Application color", 46 | "default": "#1c1c1c" 47 | }, 48 | { 49 | "type": "color", 50 | "id": "application_background_color", 51 | "label": "Background color", 52 | "default": "#ffffff" 53 | }, 54 | { 55 | "type": "header", 56 | "content": "Splash screen" 57 | }, 58 | { 59 | "type": "image_picker", 60 | "id": "application_splash_screen", 61 | "label": "Splash screen", 62 | "info": "This image will be used as the splash screen when the application is launched." 63 | }, 64 | { 65 | "type": "header", 66 | "content": "Installation button" 67 | }, 68 | { 69 | "type": "paragraph", 70 | "content": "This enable a button to your store to install the application on the home screen of your customer's device." 71 | }, 72 | { 73 | "type": "checkbox", 74 | "label": "Show installation button", 75 | "id": "application_show_button", 76 | "default": true 77 | }, 78 | { 79 | "type": "header", 80 | "content": "Application shortcut" 81 | }, 82 | { 83 | "type": "paragraph", 84 | "content": "This will add a shortcut to your application on the home screen of your customer's device." 85 | }, 86 | { 87 | "type": "text", 88 | "id": "application_shortcut_name", 89 | "label": "Shortcut name", 90 | "default": "Best Sellers" 91 | }, 92 | { 93 | "type": "url", 94 | "id": "application_shortcut_link", 95 | "label": "Shortcut link" 96 | }, 97 | { 98 | "type": "image_picker", 99 | "id": "application_shortcut_icon", 100 | "label": "Shortcut icon", 101 | "info": "This icon will be used for the application shortcut on the home screen." 102 | } 103 | ] 104 | }, 105 | { 106 | "name": "Typography", 107 | "settings": [ 108 | { 109 | "type": "paragraph", 110 | "content": "Use typography to present your design and content as clearly and efficiently as possible." 111 | }, 112 | { 113 | "type": "header", 114 | "content": "Headings" 115 | }, 116 | { 117 | "type": "font_picker", 118 | "id": "font_heading", 119 | "default": "inter_n7", 120 | "label": "Font", 121 | "info": "Selecting a different font can affect the speed of your store. [Learn more about system fonts.](https:\/\/help.shopify.com\/en\/manual\/online-store\/os\/store-speed\/improving-speed#fonts)" 122 | }, 123 | { 124 | "type": "header", 125 | "content": "Body" 126 | }, 127 | { 128 | "type": "font_picker", 129 | "id": "font_body", 130 | "default": "inter_n5", 131 | "label": "Font", 132 | "info": "Selecting a different font can affect the speed of your store. [Learn more about system fonts.](https:\/\/help.shopify.com\/en\/manual\/online-store\/os\/store-speed\/improving-speed#fonts)" 133 | } 134 | ] 135 | }, 136 | { 137 | "name": "Cart", 138 | "settings": [ 139 | { 140 | "type": "select", 141 | "id": "cart_type", 142 | "options": [ 143 | { 144 | "value": "drawer", 145 | "label": "Drawer" 146 | }, 147 | { 148 | "value": "page", 149 | "label": "Page" 150 | } 151 | ], 152 | "default": "page", 153 | "label": "Cart type" 154 | } 155 | ] 156 | }, 157 | { 158 | "name": "Currency", 159 | "settings": [ 160 | { 161 | "type": "header", 162 | "content": "Currency codes" 163 | }, 164 | { 165 | "type": "checkbox", 166 | "id": "currency_code_enabled", 167 | "label": "Show currency codes", 168 | "info": "Cart and checkout prices always show currency codes. Example: $1.00 USD.", 169 | "default": true 170 | } 171 | ] 172 | } 173 | ] 174 | -------------------------------------------------------------------------------- /layout/theme.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% if shop.brand.logo != blank %} 10 | 11 | {% endif %} 12 | 13 | {% render 'application' %} 14 | 15 | {{ page_title }} 16 | 17 | {% if page_description %} 18 | 19 | {% endif %} 20 | 21 | {% render 'typography' %} 22 | 23 | {{ 'styles.css' | asset_url | stylesheet_tag }} 24 | 25 | {{ content_for_header }} 26 | 27 | 28 |
29 | 30 | {{ shop.name }} 31 | 32 | 33 | View on Github 34 | 35 |
36 | 37 |
41 | {{ content_for_layout }} 42 |
43 | 44 | 47 | 48 | {% render 'development-screen-indicator' %} 49 | 50 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /locales/en.default.json: -------------------------------------------------------------------------------- 1 | { 2 | "gift_cards": { 3 | "issued": { 4 | "title": "Here's your {{ value }} gift card for {{ shop }}!", 5 | "subtext": "Your gift card", 6 | "gift_card_code": "Gift card code", 7 | "shop_link": "Continue shopping", 8 | "remaining_html": "Remaining {{ balance }}", 9 | "add_to_apple_wallet": "Add to Apple Wallet", 10 | "qr_image_alt": "QR code — scan to redeem gift card", 11 | "copy_code": "Copy code", 12 | "expired": "Expired", 13 | "copy_code_success": "Code copied successfully", 14 | "print_gift_card": "Print" 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "theme-pwa", 3 | "version": "1.0.2", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "run-p -sr shopify:dev watch", 8 | "shopify:dev": "shopify theme dev -e dev", 9 | "pull": "shopify theme pull -d -o sections/*.json templates/*.json templates/customers/*.json config/settings_data.json", 10 | "watch": "vite build -w", 11 | "build": "vite build --minify", 12 | "release": "bumpp" 13 | }, 14 | "devDependencies": { 15 | "@shopify/cli": "latest", 16 | "@shopify/prettier-plugin-liquid": "latest", 17 | "@shopify/theme": "latest", 18 | "autoprefixer": "latest", 19 | "bumpp": "latest", 20 | "npm-run-all": "latest", 21 | "postcss": "latest", 22 | "prettier": "latest", 23 | "tailwindcss": "latest", 24 | "vite": "latest" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss').Postcss} */ 2 | 3 | export default { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /sections/main-404.liquid: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | {{ section.settings.heading }} 5 |

6 |

7 | {{ section.settings.text }} 8 |

9 | {% if section.settings.button_label != blank %} 10 | 13 | {{ section.settings.button_label }} 14 | 15 | {% endif %} 16 |
17 |
18 | 19 | {% schema %} 20 | { 21 | "name": "Main 404", 22 | "settings": [ 23 | { 24 | "type": "text", 25 | "id": "heading", 26 | "label": "Heading", 27 | "default": "404 Page Not Found" 28 | }, 29 | { 30 | "type": "text", 31 | "id": "text", 32 | "label": "Text", 33 | "default": "The page you are looking for does not exist." 34 | }, 35 | { 36 | "type": "text", 37 | "id": "button_label", 38 | "label": "Button Label", 39 | "default": "Go to Home" 40 | } 41 | ] 42 | } 43 | {% endschema %} 44 | -------------------------------------------------------------------------------- /sections/main-section.liquid: -------------------------------------------------------------------------------- 1 |
4 |
5 | {% liquid 6 | assign image = section.settings.image | default: page_image 7 | render 'image' with image, class: 'aspect-square md:aspect-auto lg:h-[30rem]', preload: true 8 | %} 9 |

10 | {{ section.settings.heading }} 11 |

12 |

13 | {{ section.settings.text }} 14 |

15 | {% if section.settings.button_label != blank %} 16 | 21 | {% endif %} 22 |
23 |
24 | 25 | {% schema %} 26 | { 27 | "name": "Main Section", 28 | "settings": [ 29 | { 30 | "type": "image_picker", 31 | "id": "image", 32 | "label": "Image" 33 | }, 34 | { 35 | "type": "text", 36 | "id": "heading", 37 | "label": "Heading", 38 | "default": "A demo of PWA Shopify theme built by Odestry" 39 | }, 40 | { 41 | "type": "text", 42 | "id": "text", 43 | "label": "Text", 44 | "default": "Start building your theme by editing this section file." 45 | }, 46 | { 47 | "type": "text", 48 | "id": "button_label", 49 | "label": "Button Label", 50 | "default": "Install Application" 51 | }, 52 | { 53 | "type": "range", 54 | "id": "spacing", 55 | "min": 0, 56 | "max": 80, 57 | "step": 4, 58 | "unit": "px", 59 | "label": "Vertical Spacing", 60 | "default": 16 61 | } 62 | ], 63 | "presets": [ 64 | { 65 | "name": "Main Section" 66 | } 67 | ] 68 | } 69 | {% endschema %} 70 | -------------------------------------------------------------------------------- /shopify.theme.toml: -------------------------------------------------------------------------------- 1 | [environments.development] 2 | store = "odestry.myshopify.com" 3 | 4 | -------------------------------------------------------------------------------- /snippets/application.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders progressive web app related meta tags and links. 3 | 4 | Usage: 5 | {% render 'application' %} 6 | {% endcomment %} 7 | 8 | {% if settings.enable_application_mode %} 9 | 10 | 11 | 12 | 13 | 14 | {% if settings.application_splash_screen != blank %} 15 | 16 | {% endif %} 17 | 18 | 19 | 20 | 21 | 22 | {% unless request.design_mode %} 23 | 70 | {% endunless %} 71 | {% endif %} 72 | -------------------------------------------------------------------------------- /snippets/development-screen-indicator.liquid: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Renders a development screen size indicator in the bottom left corner of the screen. 3 | 4 | Usage: 5 | {% render 'development-screen-indicator' %} 6 | {% endcomment %} 7 | 8 | 16 | -------------------------------------------------------------------------------- /snippets/image.liquid: -------------------------------------------------------------------------------- 1 | {% comment %}theme-check-disable ImgLazyLoading, RemoteAsset{% endcomment %} 2 | {% comment %} 3 | Renders an image tag with all necessary attributes. 4 | 5 | Accepts: 6 | - image: {object} Image object (optional) 7 | - class: {string} Extra classes (optional) 8 | - lazyload: {boolean} Lazyload the image (optional) (default: false) 9 | - alt: {string} Custom image ALT (optional) (default: image.alt) 10 | - placeholder: {string} Placeholder name when image is empty (optional) (default: 'hero-apparel-1') 11 | 12 | Usage: 13 | {% render 'image' with product.featured_image %} 14 | {% endcomment %} 15 | 16 | {% liquid 17 | assign class = 'aspect-auto h-auto w-full object-cover rounded-sm ' | append: class 18 | assign alt = alt | default: image.alt | escape 19 | 20 | assign lazyload = lazyload | default: false 21 | 22 | assign priority = 'auto' 23 | assign decoding = 'auto' 24 | 25 | if lazyload 26 | assign loading = 'lazy' 27 | assign decoding = 'async' 28 | else 29 | assign loading = 'eager' 30 | assign priority = 'high' 31 | endif 32 | %} 33 | 34 | 35 | {% if image and image != blank %} 36 | {{ 37 | image 38 | | image_url: width: image.width 39 | | image_tag: 40 | alt: alt, 41 | class: class, 42 | widths: '100,200,300,400,500,600,700,800,1000,1200,1400,1600,1800,2000', 43 | loading: loading, 44 | fetchpriority: priority, 45 | decoding: decoding 46 | }} 47 | {% else %} 48 | {% liquid 49 | assign placeholder = placeholder | default: 'hero-apparel-1' 50 | assign placeholder_image = placeholder | placeholder_svg_tag | base64_encode | prepend: 'data:image/svg+xml;base64,' 51 | %} 52 | 53 | {{ placeholder }} 63 | {% endif %} 64 | 65 | -------------------------------------------------------------------------------- /snippets/typography.liquid: -------------------------------------------------------------------------------- 1 | {% comment %}theme-check-disable AssetPreload{% endcomment %} 2 | {% comment %} 3 | Renders typography styles and preloads fonts. 4 | 5 | Usage: 6 | {% render 'typography' %} 7 | {% endcomment %} 8 | 9 | {% comment %} We check if its a system font, or a web-safe font {% endcomment %} 10 | {% unless settings.font_heading.system? %} 11 | 12 | {% endunless %} 13 | 14 | {% unless settings.font_body.system? %} 15 | 16 | {% endunless %} 17 | 18 | {% style %} 19 | {% unless settings.font_heading.system? %} 20 | {% liquid 21 | assign font_heading_medium = settings.font_heading | font_modify: 'weight', '500' 22 | assign font_heading_semi_bold = settings.font_heading | font_modify: 'weight', '600' 23 | assign font_heading_bold = settings.font_heading | font_modify: 'weight', 'bold' 24 | assign font_heading_bold_italic = font_heading_bold | font_modify: 'style', 'italic' 25 | %} 26 | 27 | {{ settings.font_heading | font_face: font_display: 'swap' }} 28 | {{ font_heading_medium | font_face: font_display: 'swap' }} 29 | {{ font_heading_semi_bold | font_face: font_display: 'swap' }} 30 | {{ font_heading_bold | font_face: font_display: 'swap' }} 31 | {{ font_heading_bold_italic | font_face: font_display: 'swap' }} 32 | {% endunless %} 33 | 34 | {% unless settings.font_body.system? %} 35 | {% liquid 36 | assign font_body_semi_bold = settings.font_body | font_modify: 'weight', '500' 37 | assign font_body_bold = settings.font_body | font_modify: 'weight', 'bold' 38 | assign font_body_italic = settings.font_body | font_modify: 'style', 'italic' 39 | assign font_body_bold_italic = font_body_bold | font_modify: 'style', 'italic' 40 | %} 41 | 42 | {{ settings.font_body | font_face: font_display: 'swap' }} 43 | {{ font_body_semi_bold | font_face: font_display: 'swap' }} 44 | {{ font_body_bold | font_face: font_display: 'swap' }} 45 | {{ font_body_italic | font_face: font_display: 'swap' }} 46 | {{ font_body_bold_italic | font_face: font_display: 'swap' }} 47 | {% endunless %} 48 | {% endstyle %} 49 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | html { 7 | text-size-adjust: 100%; 8 | text-rendering: optimizeLegibility; 9 | font-variant-ligatures: none; 10 | } 11 | 12 | details > summary { 13 | @apply list-none; 14 | } 15 | 16 | details summary::-webkit-details-marker { 17 | @apply hidden; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | export default { 4 | content: ['./*/*.{liquid,json,js}'], 5 | theme: {}, 6 | plugins: [] 7 | } satisfies Config 8 | -------------------------------------------------------------------------------- /templates/404.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/article.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/blog.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/cart.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/customers/account.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/customers/activate_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/customers/addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/customers/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/customers/order.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/customers/register.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/customers/reset_password.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/gift_card.liquid: -------------------------------------------------------------------------------- 1 | {% layout none %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% if shop.brand.logo != blank %} 12 | 13 | {% endif %} 14 | 15 | {% render 'application' %} 16 | 17 | 18 | 19 | {% assign formatted_initial_value = gift_card.initial_value | money_without_trailing_zeros | strip_html %} 20 | 21 | {{ 'gift_cards.issued.title' | t: value: formatted_initial_value, shop: shop.name }} 22 | 23 | 24 | 25 | {% render 'typography' %} 26 | 27 | {{ 'styles.css' | asset_url | stylesheet_tag }} 28 | 29 | {{ content_for_header }} 30 | 31 | 32 |
36 | {{ content_for_layout }} 37 |
38 | 39 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /templates/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-section" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/list-collections.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/password.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/product.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /templates/search.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "main": { 4 | "type": "main-404" 5 | } 6 | }, 7 | "order": [ 8 | "main" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | 3 | export default defineConfig({ 4 | build: { 5 | outDir: 'assets', 6 | emptyOutDir: false, 7 | minify: false, 8 | rollupOptions: { 9 | input: 'styles.css', 10 | output: { 11 | dir: 'assets', 12 | assetFileNames: '[name][extname]', 13 | } 14 | } 15 | } 16 | }) 17 | --------------------------------------------------------------------------------