├── .babelrc
├── .codeclimate.yml
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── postcss.config.js
├── slater.png
├── src
├── assets
│ ├── index.js
│ ├── index.js.map
│ └── main.css
├── config-sample.yml
├── config
│ ├── settings_data.json
│ └── settings_schema.json
├── icons
│ ├── icon-amazon_payments.svg
│ ├── icon-american_express.svg
│ ├── icon-apple_pay.svg
│ ├── icon-arrow-down.svg
│ ├── icon-bitcoin.svg
│ ├── icon-cart.svg
│ ├── icon-cirrus.svg
│ ├── icon-close.svg
│ ├── icon-dankort.svg
│ ├── icon-diners_club.svg
│ ├── icon-discover.svg
│ ├── icon-dogecoin.svg
│ ├── icon-dwolla.svg
│ ├── icon-facebook.svg
│ ├── icon-forbrugsforeningen.svg
│ ├── icon-hamburger.svg
│ ├── icon-instagram.svg
│ ├── icon-interac.svg
│ ├── icon-jcb.svg
│ ├── icon-litecoin.svg
│ ├── icon-lock.svg
│ ├── icon-maestro.svg
│ ├── icon-master.svg
│ ├── icon-minus.svg
│ ├── icon-paypal.svg
│ ├── icon-pinterest.svg
│ ├── icon-plus.svg
│ ├── icon-rss.svg
│ ├── icon-search.svg
│ ├── icon-shopify-logo.svg
│ ├── icon-snapchat.svg
│ ├── icon-tumblr.svg
│ ├── icon-twitter.svg
│ ├── icon-vimeo.svg
│ ├── icon-visa.svg
│ └── icon-youtube.svg
├── layout
│ ├── gift_card.liquid
│ ├── password.liquid
│ └── theme.liquid
├── locales
│ └── en.default.json
├── scripts
│ ├── app.js
│ ├── compiled.bundle.js
│ ├── components
│ │ ├── accountLogin.js
│ │ ├── cartDrawer.js
│ │ ├── cartDrawerItem.js
│ │ ├── header.js
│ │ ├── hero.js
│ │ ├── input-text.js
│ │ └── product.js
│ ├── index.js
│ ├── lib
│ │ └── router.js
│ ├── pages
│ │ └── product.js
│ ├── sections
│ │ └── product.js
│ ├── slate
│ │ ├── sections.js
│ │ ├── utils.js
│ │ └── variants.js
│ ├── slater
│ │ ├── cart.js
│ │ ├── currency.js
│ │ ├── images.js
│ │ ├── product-selector.js
│ │ └── utils.js
│ ├── templates
│ │ ├── customers-addresses.js
│ │ └── customers-login.js
│ └── util
│ │ └── theme-provider.js
├── sections
│ ├── collection-list.liquid
│ ├── featured-collection.liquid
│ ├── featured-product.liquid
│ ├── footer.liquid
│ ├── header.liquid
│ ├── product.liquid
│ └── s-cart.liquid
├── snippets
│ ├── account-address-form.liquid
│ ├── account-address.liquid
│ ├── account-titles.liquid
│ ├── add-to-cart-form.liquid
│ ├── button.liquid
│ ├── cart-drawer.liquid
│ ├── form-input-select.liquid
│ ├── form-input-text.liquid
│ ├── form-input-textarea.liquid
│ ├── head-meta.liquid
│ ├── icon-chevron.liquid
│ ├── navigation.liquid
│ ├── no-blocks.liquid
│ ├── pagination.liquid
│ ├── search.liquid
│ ├── social-sharing.liquid
│ └── theme-provider.liquid
├── styles
│ ├── main.css
│ └── slater
│ │ ├── components
│ │ ├── accounts.css
│ │ ├── buttons.css
│ │ ├── cart-drawer.css
│ │ └── page-transition.css
│ │ ├── lib
│ │ ├── breakpoint.css
│ │ ├── colors.css
│ │ ├── containers.css
│ │ ├── forms.css
│ │ ├── lists.css
│ │ ├── spacing.css
│ │ ├── typography.css
│ │ ├── var.css
│ │ └── visibilty.css
│ │ └── theme.css
└── templates
│ ├── 404.liquid
│ ├── article.liquid
│ ├── blog.liquid
│ ├── cart.liquid
│ ├── collection.liquid
│ ├── customers
│ ├── account.liquid
│ ├── activate_account.liquid
│ ├── addresses.liquid
│ ├── login.liquid
│ ├── order.liquid
│ ├── register.liquid
│ └── reset_password.liquid
│ ├── gift_card.liquid
│ ├── index.liquid
│ ├── list-collections.liquid
│ ├── page.contact.liquid
│ ├── page.liquid
│ ├── page.styles.liquid
│ ├── password.liquid
│ ├── product.liquid
│ └── search.liquid
├── theme.lock
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "transform-class-properties",
4 | "transform-object-rest-spread",
5 | "lodash",
6 | "syntax-dynamic-import",
7 | ['module-resolver', {
8 | 'root': ['.'],
9 | 'alias': {
10 | 'slater': './src/scripts/slater',
11 | 'components': './src/scripts/components',
12 | 'lib': './src/scripts/lib',
13 | 'pages': './src/scripts/pages',
14 | 'templates': './src/scripts/templates'
15 | }
16 | }]
17 | ],
18 | "presets": [
19 | "es2015"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | # other configuration excluded from example...
2 | exclude_patterns:
3 | - "src/assets/*.js"
4 | - "src/assets/*.css"
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This file contains a list of files and folders to be ignored when
2 | # committing to a git repository. Ignored files are both Slate project
3 | # specific files as well as commonly ignored files on any project.
4 |
5 | # For more information on this .gitignore files, see GitHub's
6 | # documentation: https://help.github.com/articles/ignoring-files/
7 |
8 | # Project #
9 | ###################
10 | node_modules
11 | dist
12 | upload
13 | deploy.log
14 | build/*
15 | build
16 | config.yml
17 | src/assets/index.js
18 | src/assets/main.css
19 |
20 | # Compiled source #
21 | ###################
22 | *.com
23 | *.class
24 | *.dll
25 | *.exe
26 | *.o
27 | *.so
28 |
29 | # Packages #
30 | ############
31 | # it's better to unpack these files and commit the raw source
32 | # git has its own built in compression methods
33 | *.7z
34 | *.dmg
35 | *.gz
36 | *.iso
37 | *.jar
38 | *.rar
39 | *.tar
40 | *.zip
41 |
42 | # Logs and databases #
43 | ######################
44 | *.log
45 | *.sql
46 | *.sqlite
47 |
48 | # OS generated files #
49 | ######################
50 | .DS_Store
51 | .DS_Store?
52 | ._*
53 | .Spotlight-V100
54 | .Trashes
55 | ehthumbs.db
56 | Thumbs.db
57 |
58 | # Custom #
59 | ######################
60 | *.sass-cache*
61 | _site
62 | /.idea
63 | secrets.json
64 | .jekyll-metadata
65 | .ruby-version
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## slater theme is deprected and now maintained in slater
2 |
3 | Note, the slater-theme is now bundled with Slater-CLI
4 |
5 | [slater](https://github.com/the-couch/slater)
6 |
7 | This theme is no longer maintained here
8 |
9 |
10 | -----------
11 |
12 |
13 | # SLATER
14 | Slater than [Slate](https://github.com/Shopify/slate). Minimaler than minimal.
15 |
16 |
17 |
18 | ## Up and Running
19 |
20 | Clone the repo to your local environment
21 |
22 | `git clone git@github.com:the-couch/slater-theme.git theme`
23 |
24 | `cd theme`
25 |
26 | `npm install` or `yarn install`
27 |
28 | -----
29 |
30 | ### Initial build
31 |
32 | We need to upload our initial build of the theme, we can do this by running `npm run build` to compile the theme one time, from there we can zip the `src` archive and upload it to our Shopify environment.
33 |
34 | -----
35 |
36 | ### Development
37 |
38 | Duplicate the `config-sample.yml` and grab the creds from your shopify theme.
39 |
40 | In two separate windows run the watch command (this is to allow us to use our own build process on top of Shopify Slate).
41 |
42 | `npm run watch:assets` - watches js and css
43 |
44 | `npm run start` - manages theme refresh/upload
45 |
46 | ## Custom Routing
47 |
48 | We're using operator to extend pjax style routing throughout the theme. This can be used to fetch/cache/handle transitions between routes. This can also help us init/code split specific javascript to specific routes (for example a flickity slideshow).
49 |
50 | The code for handling this is found in `lib/router`
51 |
52 | We use the `afterRender` and `beforeRender` methods for handling page transitions
53 |
54 | To block a custom route add the `no-ajax` class to the url element.
55 |
56 | ## Why
57 | I think slate is awesome, but I don't like that it's a closed box, I love the flexibility of things like react-create-app because it let's you actually eject the project and extend it. So this is a bit of a hack but I think y'all enjoy it. If you don't like it though, you don't have to use it ;)
58 |
59 | ## Why why
60 | Slate is a really big step forward to Shopify theme development. However, we generally prefer Webpack to Gulp, vanilla js to jQuery, CSS to SCSS, functional to object-oriented, etc. We've removed the boilerplate in favor of *nothing*, and will be releasing packages to replace the functionality of Slate in the near future.
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slater",
3 | "version": "0.3.1",
4 | "scripts": {
5 | "start": "slater watch",
6 | "deploy:production": "slater deploy --env production",
7 | "dev": "concurrently 'slater watch' 'npm run watch:js' 'npm run watch:css'",
8 | "build": "npm run build:js && npm run build:css",
9 | "watch:assets": "concurrently 'npm run watch:js' 'npm run watch:css'",
10 | "build:theme": "theme upload",
11 | "watch:theme": "theme watch",
12 | "build:js": "NODE_ENV=production webpack --progress",
13 | "watch:js": "NODE_ENV=development webpack -w",
14 | "build:css": "NODE_ENV=production postcss styles/main.css -o src/assets/main.css",
15 | "watch:css": "NODE_ENV=development postcss styles/main.css -w -o src/assets/main.css"
16 | },
17 | "author": "iamkevingreen",
18 | "license": "MIT",
19 | "devDependencies": {
20 | "@slater/cli": "^0.6.2",
21 | "babel-core": "^6.25.0",
22 | "babel-eslint": "^7.2.3",
23 | "babel-loader": "^7.0.0",
24 | "babel-plugin-lodash": "^3.2.11",
25 | "babel-plugin-module-resolver": "^2.7.1",
26 | "babel-plugin-syntax-dynamic-import": "^6.18.0",
27 | "babel-plugin-transform-class-properties": "^6.24.1",
28 | "babel-plugin-transform-object-assign": "^6.22.0",
29 | "babel-plugin-transform-object-rest-spread": "^6.23.0",
30 | "babel-preset-es2015": "^6.24.1",
31 | "concurrently": "^3.5.0",
32 | "cssnano": "^3.10.0",
33 | "lodash-webpack-plugin": "^0.11.4",
34 | "postcss": "^6.0.2",
35 | "postcss-calc": "^6.0.0",
36 | "postcss-cli": "^4.1.0",
37 | "postcss-cssnext": "^2.11.0",
38 | "postcss-discard-comments": "^2.0.4",
39 | "postcss-import": "^10.0.0",
40 | "postcss-nested": "^2.0.2",
41 | "standard": "^10.0.2",
42 | "standard-loader": "^6.0.1",
43 | "uglifyjs-webpack-plugin": "^1.2.2",
44 | "webpack": "^3.0.0",
45 | "webpack-dashboard": "^1.0.0-5"
46 | },
47 | "dependencies": {
48 | "form-serialize": "^0.7.2",
49 | "mitt": "^1.1.2",
50 | "operator": "^1.2.0",
51 | "picoapp": "^2.0.2",
52 | "svbstrate": "^4.1.0",
53 | "unfetch": "^3.0.0",
54 | "w2t": "0.0.2"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const p = process.env.NODE_ENV === 'production'
2 |
3 | module.exports = {
4 | plugins: [
5 | require('postcss-import'),
6 | require('postcss-nested'),
7 | require('postcss-cssnext'),
8 | require('postcss-calc'),
9 | require('postcss-discard-comments'),
10 | require('postcss-reporter'),
11 | p ? require('cssnano') : ''
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/slater.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-couch/slater-theme/d2cce8b040ab7bf5dfd79a248e950b85f9074fc5/slater.png
--------------------------------------------------------------------------------
/src/config-sample.yml:
--------------------------------------------------------------------------------
1 | # This file contains the information needed for Shopify to authenticate
2 | # requests and edit/update your remote theme files.
3 | #
4 | # 1. Set up a private app (https://help.shopify.com/api/guides/api-credentials#generate-private-app-credentials)
5 | # with "Read and write" permissions for "Theme templates and theme assets".
6 | # 2. Replace the required variables for each environment below.
7 | #
8 | # password, theme_id, and store variables are required.
9 | #
10 | # For more information on this config file:
11 | # Configuration variables | http://shopify.github.io/themekit/configuration/
12 | # Ignore patterns | http://shopify.github.io/themekit/ignores/
13 |
14 | ---
15 |
16 | development:
17 | password:
18 | theme_id:
19 | store: your-shop.myshopify.com
20 | ignore_files:
21 | #- settings_data.json # Uncomment this line to avoid resetting theme settings
22 |
23 | production:
24 | password:
25 | theme_id: "live"
26 | store: your-shop.myshopify.com
27 | ignore_files:
28 | #- settings_data.json # Uncomment this line to avoid resetting theme settings
29 |
--------------------------------------------------------------------------------
/src/config/settings_data.json:
--------------------------------------------------------------------------------
1 | {
2 | "current": "Default",
3 | "presets": {
4 | "Default": {
5 | "sections": {
6 | "collection-list": {
7 | "type": "collection-list",
8 | "blocks": {
9 | "collection-list-0": {
10 | "type": "collection"
11 | },
12 | "collection-list-1": {
13 | "type": "collection"
14 | },
15 | "collection-list-2": {
16 | "type": "collection"
17 | }
18 | },
19 | "block_order": [
20 | "collection-list-0",
21 | "collection-list-1",
22 | "collection-list-2"
23 | ]
24 | },
25 | "featured-collection": {
26 | "type": "featured-collection",
27 | "settings": {
28 | "collection": "frontpage"
29 | }
30 | },
31 | "featured-product": {
32 | "type": "featured-product"
33 | }
34 | },
35 | "content_for_index": [
36 | "collection-list",
37 | "featured-collection",
38 | "featured-product"
39 | ]
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/config/settings_schema.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "theme_info",
4 | "theme_name": "Slater",
5 | "theme_version": "1.0.0",
6 | "theme_author": "The Couch",
7 | "theme_documentation_url": "https://github.com/the-couch/slater-theme",
8 | "theme_support_url": "https://github.com/the-couch/slater-theme"
9 | },
10 | {
11 | "name": "Typography",
12 | "settings": [
13 | {
14 | "type": "select",
15 | "id": "type_base_size",
16 | "label": "Base size",
17 | "default": "16px",
18 | "options": [
19 | {
20 | "value": "14px",
21 | "label": "14px"
22 | },
23 | {
24 | "value": "15px",
25 | "label": "15px"
26 | },
27 | {
28 | "value": "16px",
29 | "label": "16px"
30 | },
31 | {
32 | "value": "17px",
33 | "label": "17px"
34 | }
35 | ]
36 | }
37 | ]
38 | },
39 | {
40 | "name": "Colors",
41 | "settings": [
42 | {
43 | "type": "header",
44 | "content": "General colors"
45 | },
46 | {
47 | "type": "color",
48 | "id": "color_primary",
49 | "label": "Primary color",
50 | "default": "#000",
51 | "info": "Used for text links, and primary buttons"
52 | },
53 | {
54 | "type": "color",
55 | "id": "color_body_text",
56 | "label": "Text color",
57 | "default": "#000",
58 | "info": "Color used for content and paragraph text"
59 | }
60 | ]
61 | },
62 | {
63 | "name": "Social media",
64 | "settings": [
65 | {
66 | "type": "header",
67 | "content": "Social sharing options"
68 | },
69 | {
70 | "type": "checkbox",
71 | "id": "social_sharing_blog",
72 | "label": "Enable sharing for blog articles",
73 | "default": true
74 | },
75 | {
76 | "type": "checkbox",
77 | "id": "share_facebook",
78 | "label": "Share on Facebook",
79 | "default": true
80 | },
81 | {
82 | "type": "checkbox",
83 | "id": "share_twitter",
84 | "label": "Tweet on Twitter",
85 | "default": true
86 | },
87 | {
88 | "type": "checkbox",
89 | "id": "share_pinterest",
90 | "label": "Pin on Pinterest",
91 | "default": true
92 | },
93 | {
94 | "type": "header",
95 | "content": "Sharing links"
96 | },
97 | {
98 | "type": "text",
99 | "id": "social_twitter_link",
100 | "label": "Twitter link"
101 | },
102 | {
103 | "type": "text",
104 | "id": "social_facebook_link",
105 | "label": "Facebook link"
106 | },
107 | {
108 | "type": "text",
109 | "id": "social_pinterest_link",
110 | "label": "Pinterest link"
111 | },
112 | {
113 | "type": "text",
114 | "id": "social_instagram_link",
115 | "label": "Instagram link"
116 | },
117 | {
118 | "type": "text",
119 | "id": "social_snapchat_link",
120 | "label": "Snapchat link"
121 | },
122 | {
123 | "type": "text",
124 | "id": "social_tumblr_link",
125 | "label": "Tumblr link"
126 | },
127 | {
128 | "type": "text",
129 | "id": "social_youtube_link",
130 | "label": "Youtube link"
131 | },
132 | {
133 | "type": "text",
134 | "id": "social_vimeo_link",
135 | "label": "Vimeo link"
136 | }
137 | ]
138 | },
139 | {
140 | "name": "Cart",
141 | "settings": [
142 | {
143 | "type": "checkbox",
144 | "id": "cart_notes_enable",
145 | "label": "Enable cart notes",
146 | "default": true
147 | }
148 | ]
149 | },
150 | {
151 | "name": "Favicon",
152 | "settings": [
153 | {
154 | "type": "image_picker",
155 | "id": "favicon",
156 | "label": "Favicon image",
157 | "info": "Will be scaled down to 32 x 32px"
158 | }
159 | ]
160 | }
161 | ]
162 |
--------------------------------------------------------------------------------
/src/icons/icon-amazon_payments.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-american_express.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-apple_pay.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-arrow-down.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-bitcoin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-cart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-cirrus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-close.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-dankort.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-diners_club.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-discover.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-dogecoin.svg:
--------------------------------------------------------------------------------
1 | icon-dogecoin
--------------------------------------------------------------------------------
/src/icons/icon-dwolla.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/icon-facebook.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-forbrugsforeningen.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/icon-hamburger.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-instagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-interac.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-jcb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-litecoin.svg:
--------------------------------------------------------------------------------
1 | icon-litecoin
--------------------------------------------------------------------------------
/src/icons/icon-lock.svg:
--------------------------------------------------------------------------------
1 | Lock icon
--------------------------------------------------------------------------------
/src/icons/icon-maestro.svg:
--------------------------------------------------------------------------------
1 | Shape
--------------------------------------------------------------------------------
/src/icons/icon-master.svg:
--------------------------------------------------------------------------------
1 | Shape
--------------------------------------------------------------------------------
/src/icons/icon-minus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-paypal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-pinterest.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-plus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-rss.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-shopify-logo.svg:
--------------------------------------------------------------------------------
1 | Shopify logo
--------------------------------------------------------------------------------
/src/icons/icon-snapchat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-tumblr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-twitter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-vimeo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-visa.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/icon-youtube.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layout/gift_card.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {% if settings.favicon != blank %}
12 |
13 | {% endif %}
14 |
15 | {%- assign formatted_initial_value = gift_card.initial_value | money_without_trailing_zeros: gift_card.currency -%}
16 | {%- assign formatted_initial_value_stripped = formatted_initial_value | strip_html -%}
17 | {{ 'gift_cards.issued.title' | t: value: formatted_initial_value_stripped, shop: shop.name }}
18 |
19 |
20 |
21 | {{ 'main.css' | asset_url | stylesheet_tag }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
62 |
63 | {{ content_for_header }}
64 |
65 |
66 |
67 |
68 | {{ content_for_layout }}
69 |
70 |
71 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/src/layout/password.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {% if settings.favicon != blank %}
12 |
13 | {% endif %}
14 |
15 | {{ shop.name }}
16 |
17 | {% if page_description %}
18 |
19 | {% endif %}
20 |
21 | {% include 'social-meta-tags' %}
22 |
23 | {{ 'theme.scss.css' | asset_url | stylesheet_tag }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {{ content_for_header }}
32 |
33 |
34 |
35 |
42 |
43 |
44 | {{ content_for_layout }}
45 |
46 |
47 |
56 |
57 |
58 |
{{ 'general.password_page.login_form_heading' | t }}
59 | {% form 'storefront_password' %}
60 | {{ form.errors | default_errors }}
61 |
{{ 'general.password_page.login_form_password_label' | t }}
62 |
66 |
67 | {{ 'general.password_page.login_form_submit' | t }}
68 |
69 | {% endform %}
70 |
{{ 'general.password_page.admin_link_html' | t }}
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/layout/theme.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {% if settings.favicon != blank %}
10 |
11 | {% endif %}
12 |
13 | {% capture seo_title %}
14 | {{ page_title }}
15 | {% if current_tags %}
16 | {%- assign meta_tags = current_tags | join: ', ' %} – {{ 'general.meta.tags' | t: tags: meta_tags -}}
17 | {% endif %}
18 | {% if current_page != 1 %}
19 | – {{ 'general.meta.page' | t: page: current_page }}
20 | {% endif %}
21 | {% unless page_title contains shop.name %}
22 | – {{ shop.name }}
23 | {% endunless %}
24 | {% endcapture %}
25 | {{ seo_title }}
26 |
27 | {% include 'head-meta' %}
28 |
29 | {% if template.directory == 'customers' %}
30 |
31 | {% endif %}
32 |
33 |
43 |
44 | {{ 'index.css' | asset_url | stylesheet_tag }}
45 |
46 | {{ content_for_header }}
47 |
48 |
49 |
50 |
53 |
54 |
55 | {% section 'header' %}
56 |
57 |
58 | {{ content_for_layout }}
59 | {% section 'footer' %}
60 |
61 |
62 |
63 |
64 | {% include 'cart-drawer' %}
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/scripts/app.js:
--------------------------------------------------------------------------------
1 | import picoapp from 'picoapp'
2 |
3 | import header from './components/header.js'
4 | import product from './components/product.js'
5 | import cartDrawer from './components/cartDrawer.js'
6 | import cartDrawerItem from './components/cartDrawerItem.js'
7 | import accountLogin from './components/accountLogin.js'
8 |
9 | const state = {
10 | cartOpen: false
11 | }
12 |
13 | const actions = {
14 | toggleCart: open => state => ({ cartOpen: !state.cartOpen })
15 | }
16 |
17 | const components = {
18 | header,
19 | product,
20 | cartDrawer,
21 | cartDrawerItem,
22 | accountLogin
23 | }
24 |
25 | export default picoapp(components, state, actions)
26 |
--------------------------------------------------------------------------------
/src/scripts/components/accountLogin.js:
--------------------------------------------------------------------------------
1 | import { component } from 'picoapp'
2 |
3 | export default component(({ node: outer, state }) => {
4 | const login = outer.querySelector('.js-login-dialog')
5 | const recover = outer.querySelector('.js-recover-dialog')
6 | const recoverLink = outer.querySelector('.js-recover-trigger')
7 | const cancelRecoverLink = outer.querySelector('.js-recover-cancel')
8 |
9 | /* eslint-disable */
10 | const recoverIsTarget = window.location.hash.match(/\#recover/) ? true : false
11 | /* eslint-enable */
12 |
13 | const successMessage = outer.querySelector('.js-recover-success') !== null
14 |
15 | if (recoverIsTarget || successMessage) {
16 | login.style.display = 'none'
17 | recover.style.display = 'block'
18 | } else {
19 | login.style.display = 'block'
20 | }
21 |
22 | recoverLink.addEventListener('click', (e) => {
23 | e.preventDefault()
24 | login.style.display = 'none'
25 | recover.style.display = 'block'
26 | })
27 |
28 | cancelRecoverLink.addEventListener('click', (e) => {
29 | e.preventDefault()
30 | recover.style.display = 'none'
31 | login.style.display = 'block'
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/src/scripts/components/cartDrawer.js:
--------------------------------------------------------------------------------
1 | import { fetchCart } from '../slater/cart'
2 | import { getSizedImageUrl, imageSize } from '../slater/images'
3 | import { formatMoney } from '../slater/currency'
4 | import { component } from 'picoapp'
5 | import app from '../app.js'
6 |
7 | const X = ` `
8 |
9 | function createItem ({
10 | variant_id: id,
11 | product_title: title,
12 | line_price: price,
13 | variant_title: color,
14 | image,
15 | url,
16 | quantity,
17 | ...item
18 | }) {
19 | const img = image ? getSizedImageUrl(
20 | image.replace('_' + imageSize(image), ''), '200x' // TODO hacky af
21 | ) : 'https://source.unsplash.com/R9OS29xJb-8/2000x1333'
22 |
23 | return `
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
${title}
32 |
${formatMoney(price)}
33 |
34 |
-
35 |
${quantity}
36 |
+
37 |
38 | ${color ? `
${color.split(':')[0]}
` : ``}
39 |
40 |
41 |
${X}
42 |
43 |
44 |
45 | `
46 | }
47 |
48 | function renderItems (items) {
49 | return items.length > 0 ? (
50 | items.reduce((markup, item) => {
51 | markup += createItem(item)
52 | return markup
53 | }, '')
54 | ) : (
55 | ``
56 | )
57 | }
58 |
59 | export default component(({ node, state, actions }) => {
60 | const overlay = node.querySelector('.js-overlay')
61 | const closeButton = node.querySelector('.js-close')
62 | const subtotal = node.querySelector('.js-subtotal')
63 | const itemsRoot = node.querySelector('.js-items')
64 | const loading = itemsRoot.innerHTML
65 |
66 | const render = (cart) => {
67 | itemsRoot.innerHTML = renderItems(cart.items)
68 | subtotal.innerHTML = formatMoney(cart.total_price)
69 | }
70 |
71 | const open = (cart) => {
72 | node.classList.add('is-active')
73 | itemsRoot.innerHTML = loading
74 | setTimeout(() => {
75 | node.classList.add('is-visible')
76 | setTimeout(render(cart), 10)
77 | app.mount()
78 | }, 50)
79 | }
80 |
81 | const close = () => {
82 | node.classList.remove('is-visible')
83 | setTimeout(() => {
84 | node.classList.remove('is-active')
85 | app.hydrate({cartOpen: false})
86 | }, 400)
87 | }
88 |
89 | render(state.cart)
90 |
91 | overlay.addEventListener('click', close)
92 | closeButton.addEventListener('click', close)
93 |
94 | return {
95 | onStateChange ({cart, cartOpen}) {
96 | console.log('state is changing?', cartOpen)
97 | cartOpen ? open(cart) : null
98 | }
99 | }
100 | })
101 |
102 | //
103 | // export default outer => {
104 | // let isOpen = false
105 | //
106 | // const overlay = outer.querySelector('.js-overlay')
107 | // const closeButton = outer.querySelector('.js-close')
108 | // const subtotal = outer.querySelector('.js-subtotal')
109 | // const itemsRoot = outer.querySelector('.js-items')
110 | // const loading = itemsRoot.innerHTML
111 | //
112 | // function render () {
113 | // fetchCart().then(cart => {
114 | // itemsRoot.innerHTML = renderItems(cart.items)
115 | // subtotal.innerHTML = formatMoney(cart.total_price)
116 | // setTimeout(() => {
117 | // scripts.mount()
118 | // }, 0)
119 | // })
120 | // }
121 | //
122 | // function open () {
123 | // outer.classList.add('is-active')
124 | //
125 | // itemsRoot.innerHTML = loading
126 | //
127 | // setTimeout(() => {
128 | // outer.classList.add('is-visible')
129 | // isOpen = true
130 | // setTimeout(render, 10)
131 | // }, 50)
132 | // }
133 | //
134 | // function close () {
135 | // outer.classList.remove('is-visible')
136 | //
137 | // setTimeout(() => {
138 | // outer.classList.remove('is-active')
139 | // isOpen = false
140 | // }, 400)
141 | // }
142 | //
143 | // on('updated', ({ cart }) => {
144 | // isOpen ? render() : open()
145 | // })
146 | // on('addon', ({ cart }) => {
147 | // isOpen ? render() : open()
148 | // })
149 | // overlay.addEventListener('click', close)
150 | // closeButton.addEventListener('click', close)
151 | //
152 | // return {
153 | // open,
154 | // close: close
155 | // }
156 | // }
157 |
--------------------------------------------------------------------------------
/src/scripts/components/cartDrawerItem.js:
--------------------------------------------------------------------------------
1 | import { removeAddon, updateAddon } from '../slater/cart.js'
2 | import { component } from 'picoapp'
3 |
4 | export default component(({ node: item, state }) => {
5 | const button = item.getElementsByTagName('button')[0]
6 | const decrease = item.querySelector('.js-remove-single')
7 | const increase = item.querySelector('.js-add-single')
8 | const currentQty = item.querySelector('.js-single-quantity').innerHTML
9 | const id = item.getAttribute('data-id')
10 |
11 | button.addEventListener('click', e => {
12 | e.preventDefault()
13 | removeAddon(id)
14 | })
15 |
16 | decrease.addEventListener('click', e => {
17 | e.preventDefault()
18 | updateAddon(id, parseInt(currentQty) - 1)
19 | })
20 |
21 | increase.addEventListener('click', e => {
22 | e.preventDefault()
23 | updateAddon(id, parseInt(currentQty) + 1)
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/src/scripts/components/header.js:
--------------------------------------------------------------------------------
1 | // import { on, fetchCart } from '../slater/cart'
2 | import { component } from 'picoapp'
3 | // import Cart from './cart-drawer.js'
4 |
5 | export default component(({ node: header, state, actions }) => {
6 | const cartCount = header.querySelector('.js-cart-count')
7 | const cartToggles = header.querySelectorAll('.js-cart-drawer-toggle')
8 | cartCount.innerHTML = state.cart.items.length >= 1 ? state.cart.item_count : null
9 |
10 | for (let toggle of cartToggles) {
11 | toggle.addEventListener('click', e => {
12 | console.log('yo click', state)
13 | e.preventDefault()
14 | actions.toggleCart(true)
15 | console.log('yo click', state)
16 | })
17 | }
18 |
19 | return {
20 | onStateChange (state) {
21 | cartCount.innerHTML = state.cart.item_count
22 | }
23 | }
24 | // const cartCount = header.querySelector('.js-cart-count')
25 | // const cart = fetchCart()
26 | // cart.then(res => {
27 | // /* eslint-disable */
28 | // res ? cartCount.innerHTML = res.item_count : null
29 | // /* eslint-enable */
30 | // })
31 | // on('updated', ({ cart }) => {
32 | // cartCount.innerHTML = cart.item_count
33 | // })
34 | // on('addon', ({ cart }) => {
35 | // cartCount.innerHTML = cart.item_count
36 | // })
37 | // /**
38 | // // * Cart opening
39 | // // */
40 | // const cartToggles = header.querySelectorAll('.js-cart-drawer-toggle')
41 | // for (let toggle of cartToggles) {
42 | // toggle.addEventListener('click', e => {
43 | // console.log('clicked?')
44 | // e.preventDefault()
45 | // Cart.open()
46 | // // const cartDrawer = scripts.cache.get('cart-drawer')
47 | // // cartDrawer.open()
48 | // })
49 | // }
50 | })
51 |
--------------------------------------------------------------------------------
/src/scripts/components/hero.js:
--------------------------------------------------------------------------------
1 | export default props => {
2 | console.log('hero fired', props)
3 | }
4 |
--------------------------------------------------------------------------------
/src/scripts/components/input-text.js:
--------------------------------------------------------------------------------
1 | export default wrapper => {
2 | const input = wrapper.getElementsByTagName('input')[0]
3 |
4 | function handleAddRemove (e) {
5 | e.target.value ? add() : remove()
6 | }
7 |
8 | function add () {
9 | wrapper.classList.add('has-value')
10 | }
11 |
12 | function remove () {
13 | wrapper.classList.remove('has-value')
14 | }
15 |
16 | input.addEventListener('change', handleAddRemove)
17 | input.addEventListener('blur', handleAddRemove)
18 | input.addEventListener('focus', add)
19 |
20 | return {
21 | unmount () {
22 | input.removeEventListener('change', handleAddRemove)
23 | input.removeEventListener('blur', handleAddRemove)
24 | input.removeEventListener('focus', add)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/scripts/components/product.js:
--------------------------------------------------------------------------------
1 | import { addVariant } from '../slater/cart.js'
2 | import { component } from 'picoapp'
3 | import wait from 'w2t'
4 |
5 | export default component(({ node: el, state }) => {
6 | const { selectedOrFirstAvailableVariant, product } = JSON.parse(el.querySelector('.js-product-json').innerHTML)
7 | let currentVariant = product.variants.filter(v => v.id === selectedOrFirstAvailableVariant)[0]
8 |
9 | const form = el.getElementsByTagName('form')[0]
10 | const submit = form.querySelector('.js-submit-cart')
11 | const quantity = form.querySelector('.js-quantity').value
12 | console.log('subby', form)
13 |
14 | form.addEventListener('submit', e => {
15 | e.preventDefault()
16 | submit.children[0].innerHTML = 'Adding..'
17 | wait(1000, [
18 | addVariant(currentVariant, quantity).then(({ item, cart }) => {
19 | submit.children[0].innerHTML = 'Add to Cart'
20 | }).catch(e => {
21 | alert(e)
22 | })
23 | ])
24 | })
25 | })
26 |
27 | //
28 | // export default el => {
29 | // const { selectedOrFirstAvailableVariant, product } = JSON.parse(el.querySelector('.js-product-json').innerHTML)
30 | //
31 | // let currentVariant = product.variants.filter(v => v.id === selectedOrFirstAvailableVariant)[0]
32 | //
33 | // /**
34 | // * Adding products to cart
35 | // */
36 | // const form = el.getElementsByTagName('form')[0]
37 | // const submit = form.querySelector('.js-submit-cart')
38 | // const quantity = form.querySelector('.js-quantity').value
39 | //
40 | // form.addEventListener('submit', e => {
41 | // e.preventDefault()
42 | // console.log('add to cart')
43 | //
44 | // submit.disabled = true
45 | // addVariant(currentVariant, quantity).then(({ item, cart }) => {
46 | // submit.disabled = false
47 | // }).catch(e => {
48 | // submit.disabled = false
49 | // /* eslint-disable */
50 | // alert(e)
51 | // /* eslint-enable */
52 | // })
53 | // })
54 | // }
55 |
--------------------------------------------------------------------------------
/src/scripts/index.js:
--------------------------------------------------------------------------------
1 | import operator from 'operator'
2 | import app from './app.js'
3 | import wait from 'w2t'
4 | import { fetchCart } from './slater/cart'
5 |
6 | import '../styles/main.css'
7 |
8 | let root = document.getElementById('pageTransition')
9 |
10 | const animateRoute = () => {
11 | return new Promise(res => {
12 | root.classList.add('cover')
13 | setTimeout(() => {
14 | root.classList.remove('cover')
15 | res()
16 | }, 600)
17 | })
18 | }
19 |
20 | const router = operator('#root', [
21 | state => {
22 | return wait(20, [
23 | animateRoute()
24 | ])
25 | }
26 | ])
27 |
28 | router.on('before', state => {
29 | // const pageTransition = document.getElementById('pageTransition')
30 | // pageTransition.classList.add('cover')
31 | return Promise.all([
32 | app.unmount(),
33 | new Promise(r => {
34 | document.body.classList.add('moving')
35 | setTimeout(r, 800)
36 | })
37 | ])
38 | })
39 |
40 | router.on('after', ({ title, pathname }) => {
41 | document.title = title
42 | window.history.pushState({}, '', pathname)
43 | })
44 |
45 | document.addEventListener('DOMContentLoaded', e => {
46 | Promise.all([
47 | fetchCart()
48 | // checkout.hydrate()
49 | ]).then(([ cart ]) => {
50 | app.hydrate({ cart: cart })
51 | app.mount()
52 | })
53 | })
54 |
55 | console.groupCollapsed('Slater credits 🍝 taco')
56 | console.log('Development by The Couch https://thecouch.nyc')
57 | console.groupEnd()
58 |
--------------------------------------------------------------------------------
/src/scripts/lib/router.js:
--------------------------------------------------------------------------------
1 | import operator from 'operator.js'
2 | // import * as scripts from 'micromanager'
3 |
4 | const router = operator({
5 | transitionSpeed: 400,
6 | routes: {}
7 | })
8 |
9 | // router.addRoute('*', () => {
10 | // const cache = scripts.cache.dump()
11 | // let modules = []
12 | //
13 | // for (let key in cache) {
14 | // modules.push(cache[key])
15 | // }
16 | //
17 | // return Promise.all(modules.map(mod => {
18 | // return mod.leave ? mod.leave() : mod
19 | // }))
20 | // })
21 |
22 | router.on('afterRender', requestedRoute => {
23 | // const cartDrawer = scripts.cache.get('cart-drawer')
24 | // cartDrawer && cartDrawer.close()
25 |
26 | const pageTransition = document.getElementById('pageTransition')
27 | setTimeout(() => {
28 | pageTransition.classList.remove('cover')
29 | }, 600)
30 | })
31 |
32 | router.on('beforeRender', requestedRoute => {
33 | const root = document.getElementById('root')
34 | const pageTransition = document.getElementById('pageTransition')
35 |
36 | root.classList.add('moving')
37 | pageTransition.classList.add('cover')
38 | setTimeout(() => {
39 | console.log('waiting game')
40 | }, 700)
41 | })
42 |
43 | export default router
44 |
--------------------------------------------------------------------------------
/src/scripts/pages/product.js:
--------------------------------------------------------------------------------
1 | import ProductSelector from 'slater/product-selector.js'
2 |
3 | export default el => {
4 | const selector = ProductSelector()
5 |
6 | selector.on('update', variant => {
7 | console.log(variant)
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/src/scripts/sections/product.js:
--------------------------------------------------------------------------------
1 | // /**
2 | // * Product Template Script
3 | // * ------------------------------------------------------------------------------
4 | // * A file that contains scripts highly couple code to the Product template.
5 | // *
6 | // * @namespace product
7 | // */
8 | //
9 | /* eslint-disable */
10 |
11 | console.log('hi')
12 |
13 | export default props => {
14 | console.log('product woo', props)
15 | }
16 |
17 | // theme.Product = (function() {
18 | //
19 | // var selectors = {
20 | // addToCart: '[data-add-to-cart]',
21 | // addToCartText: '[data-add-to-cart-text]',
22 | // comparePrice: '[data-compare-price]',
23 | // comparePriceText: '[data-compare-text]',
24 | // originalSelectorId: '[data-product-select]',
25 | // priceWrapper: '[data-price-wrapper]',
26 | // productFeaturedImage: '[data-product-featured-image]',
27 | // productJson: '[data-product-json]',
28 | // productPrice: '[data-product-price]',
29 | // productThumbs: '[data-product-single-thumbnail]',
30 | // singleOptionSelector: '[data-single-option-selector]'
31 | // };
32 | //
33 | // /**
34 | // * Product section constructor. Runs on page load as well as Theme Editor
35 | // * `section:load` events.
36 | // * @param {string} container - selector for the section container DOM element
37 | // */
38 | // function Product(container) {
39 | // this.$container = $(container);
40 | // var sectionId = this.$container.attr('data-section-id');
41 | //
42 | // this.settings = {};
43 | // this.namespace = '.product';
44 | //
45 | // // Stop parsing if we don't have the product json script tag when loading
46 | // // section in the Theme Editor
47 | // if (!$(selectors.productJson, this.$container).html()) {
48 | // return;
49 | // }
50 | //
51 | // this.productSingleObject = JSON.parse($(selectors.productJson, this.$container).html());
52 | // this.settings.imageSize = slate.Image.imageSize($(selectors.productFeaturedImage, this.$container).attr('src'));
53 | //
54 | // slate.Image.preload(this.productSingleObject.images, this.settings.imageSize);
55 | //
56 | // this.initVariants();
57 | // }
58 | //
59 | // Product.prototype = $.extend({}, Product.prototype, {
60 | //
61 | // /**
62 | // * Handles change events from the variant inputs
63 | // */
64 | // initVariants: function() {
65 | // var options = {
66 | // $container: this.$container,
67 | // enableHistoryState: this.$container.data('enable-history-state') || false,
68 | // singleOptionSelector: selectors.singleOptionSelector,
69 | // originalSelectorId: selectors.originalSelectorId,
70 | // product: this.productSingleObject
71 | // };
72 | //
73 | // this.variants = new slate.Variants(options);
74 | //
75 | // this.$container.on('variantChange' + this.namespace, this.updateAddToCartState.bind(this));
76 | // this.$container.on('variantImageChange' + this.namespace, this.updateProductImage.bind(this));
77 | // this.$container.on('variantPriceChange' + this.namespace, this.updateProductPrices.bind(this));
78 | // },
79 | //
80 | // /**
81 | // * Updates the DOM state of the add to cart button
82 | // *
83 | // * @param {boolean} enabled - Decides whether cart is enabled or disabled
84 | // * @param {string} text - Updates the text notification content of the cart
85 | // */
86 | // updateAddToCartState: function(evt) {
87 | // var variant = evt.variant;
88 | //
89 | // if (variant) {
90 | // $(selectors.priceWrapper, this.$container).removeClass('hide');
91 | // } else {
92 | // $(selectors.addToCart, this.$container).prop('disabled', true);
93 | // $(selectors.addToCartText, this.$container).html(theme.strings.unavailable);
94 | // $(selectors.priceWrapper, this.$container).addClass('hide');
95 | // return;
96 | // }
97 | //
98 | // if (variant.available) {
99 | // $(selectors.addToCart, this.$container).prop('disabled', false);
100 | // $(selectors.addToCartText, this.$container).html(theme.strings.addToCart);
101 | // } else {
102 | // $(selectors.addToCart, this.$container).prop('disabled', true);
103 | // $(selectors.addToCartText, this.$container).html(theme.strings.soldOut);
104 | // }
105 | // },
106 | //
107 | // /**
108 | // * Updates the DOM with specified prices
109 | // *
110 | // * @param {string} productPrice - The current price of the product
111 | // * @param {string} comparePrice - The original price of the product
112 | // */
113 | // updateProductPrices: function(evt) {
114 | // var variant = evt.variant;
115 | // var $comparePrice = $(selectors.comparePrice, this.$container);
116 | // var $compareEls = $comparePrice.add(selectors.comparePriceText, this.$container);
117 | //
118 | // $(selectors.productPrice, this.$container)
119 | // .html(slate.Currency.formatMoney(variant.price, theme.moneyFormat));
120 | //
121 | // if (variant.compare_at_price > variant.price) {
122 | // $comparePrice.html(slate.Currency.formatMoney(variant.compare_at_price, theme.moneyFormat));
123 | // $compareEls.removeClass('hide');
124 | // } else {
125 | // $comparePrice.html('');
126 | // $compareEls.addClass('hide');
127 | // }
128 | // },
129 | //
130 | // /**
131 | // * Updates the DOM with the specified image URL
132 | // *
133 | // * @param {string} src - Image src URL
134 | // */
135 | // updateProductImage: function(evt) {
136 | // var variant = evt.variant;
137 | // var sizedImgUrl = slate.Image.getSizedImageUrl(variant.featured_image.src, this.settings.imageSize);
138 | //
139 | // $(selectors.productFeaturedImage, this.$container).attr('src', sizedImgUrl);
140 | // },
141 | //
142 | // /**
143 | // * Event callback for Theme Editor `section:unload` event
144 | // */
145 | // onUnload: function() {
146 | // this.$container.off(this.namespace);
147 | // }
148 | // });
149 | //
150 | // return Product;
151 | // })();
152 |
--------------------------------------------------------------------------------
/src/scripts/slate/sections.js:
--------------------------------------------------------------------------------
1 |
2 | /* eslint-disable */
3 |
4 | slate.Sections = function Sections() {
5 | this.constructors = {};
6 | this.instances = [];
7 |
8 | $(document)
9 | .on('shopify:section:load', this._onSectionLoad.bind(this))
10 | .on('shopify:section:unload', this._onSectionUnload.bind(this))
11 | .on('shopify:section:select', this._onSelect.bind(this))
12 | .on('shopify:section:deselect', this._onDeselect.bind(this))
13 | .on('shopify:section:reorder', this._onReorder.bind(this))
14 | .on('shopify:block:select', this._onBlockSelect.bind(this))
15 | .on('shopify:block:deselect', this._onBlockDeselect.bind(this));
16 | };
17 |
18 | slate.Sections.prototype = $.extend({}, slate.Sections.prototype, {
19 | _createInstance: function(container, constructor) {
20 | var $container = $(container);
21 | var id = $container.attr('data-section-id');
22 | var type = $container.attr('data-section-type');
23 |
24 | constructor = constructor || this.constructors[type];
25 |
26 | if (typeof constructor === 'undefined') {
27 | return;
28 | }
29 |
30 | var instance = $.extend(new constructor(container), {
31 | id: id,
32 | type: type,
33 | container: container
34 | });
35 |
36 | this.instances.push(instance);
37 | },
38 |
39 | _onSectionLoad: function(evt) {
40 | var container = $('[data-section-id]', evt.target)[0];
41 | if (container) {
42 | this._createInstance(container);
43 | }
44 | },
45 |
46 | _onSectionUnload: function(evt) {
47 | var instance = slate.utils.findInstance(this.instances, 'id', evt.detail.sectionId);
48 |
49 | if (!instance) {
50 | return;
51 | }
52 |
53 | if (typeof instance.onUnload === 'function') {
54 | instance.onUnload(evt);
55 | }
56 |
57 | this.instances = slate.utils.removeInstance(this.instances, 'id', evt.detail.sectionId);
58 | },
59 |
60 | _onSelect: function(evt) {
61 | var instance = slate.utils.findInstance(this.instances, 'id', evt.detail.sectionId);
62 |
63 | if (instance && typeof instance.onSelect === 'function') {
64 | instance.onSelect(evt);
65 | }
66 | },
67 |
68 | _onDeselect: function(evt) {
69 | var instance = slate.utils.findInstance(this.instances, 'id', evt.detail.sectionId);
70 |
71 | if (instance && typeof instance.onDeselect === 'function') {
72 | instance.onDeselect(evt);
73 | }
74 | },
75 |
76 | _onReorder: function(evt) {
77 | var instance = slate.utils.findInstance(this.instances, 'id', evt.detail.sectionId);
78 |
79 | if (instance && typeof instance.onReorder === 'function') {
80 | instance.onReorder(evt);
81 | }
82 | },
83 |
84 | _onBlockSelect: function(evt) {
85 | var instance = slate.utils.findInstance(this.instances, 'id', evt.detail.sectionId);
86 |
87 | if (instance && typeof instance.onBlockSelect === 'function') {
88 | instance.onBlockSelect(evt);
89 | }
90 | },
91 |
92 | _onBlockDeselect: function(evt) {
93 | var instance = slate.utils.findInstance(this.instances, 'id', evt.detail.sectionId);
94 |
95 | if (instance && typeof instance.onBlockDeselect === 'function') {
96 | instance.onBlockDeselect(evt);
97 | }
98 | },
99 |
100 | register: function(type, constructor) {
101 | this.constructors[type] = constructor;
102 |
103 | $('[data-section-type=' + type + ']').each(function(index, container) {
104 | this._createInstance(container, constructor);
105 | }.bind(this));
106 | }
107 | });
108 |
--------------------------------------------------------------------------------
/src/scripts/slate/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Utility helpers
3 | * -----------------------------------------------------------------------------
4 | * A collection of useful functions for dealing with arrays and objects
5 | *
6 | * @namespace utils
7 | */
8 |
9 | /* eslint-disable */
10 |
11 | slate.utils = {
12 |
13 | /**
14 | * Return an object from an array of objects that matches the provided key and value
15 | *
16 | * @param {array} array - Array of objects
17 | * @param {string} key - Key to match the value against
18 | * @param {string} value - Value to get match of
19 | */
20 | findInstance: function(array, key, value) {
21 | for (var i = 0; i < array.length; i++) {
22 | if (array[i][key] === value) {
23 | return array[i];
24 | }
25 | }
26 | },
27 |
28 | /**
29 | * Remove an object from an array of objects by matching the provided key and value
30 | *
31 | * @param {array} array - Array of objects
32 | * @param {string} key - Key to match the value against
33 | * @param {string} value - Value to get match of
34 | */
35 | removeInstance: function(array, key, value) {
36 | var i = array.length;
37 | while(i--) {
38 | if (array[i][key] === value) {
39 | array.splice(i, 1);
40 | break;
41 | }
42 | }
43 |
44 | return array;
45 | },
46 |
47 | /**
48 | * _.compact from lodash
49 | * Remove empty/false items from array
50 | * Source: https://github.com/lodash/lodash/blob/master/compact.js
51 | *
52 | * @param {array} array
53 | */
54 | compact: function(array) {
55 | var index = -1;
56 | var length = array == null ? 0 : array.length;
57 | var resIndex = 0;
58 | var result = [];
59 |
60 | while (++index < length) {
61 | var value = array[index];
62 | if (value) {
63 | result[resIndex++] = value;
64 | }
65 | }
66 | return result;
67 | },
68 |
69 | /**
70 | * _.defaultTo from lodash
71 | * Checks `value` to determine whether a default value should be returned in
72 | * its place. The `defaultValue` is returned if `value` is `NaN`, `null`,
73 | * or `undefined`.
74 | * Source: https://github.com/lodash/lodash/blob/master/defaultTo.js
75 | *
76 | * @param {*} value - Value to check
77 | * @param {*} defaultValue - Default value
78 | * @returns {*} - Returns the resolved value
79 | */
80 | defaultTo: function(value, defaultValue) {
81 | return (value == null || value !== value) ? defaultValue : value
82 | }
83 | };
84 |
--------------------------------------------------------------------------------
/src/scripts/slate/variants.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Variant Selection scripts
3 | * ------------------------------------------------------------------------------
4 | *
5 | * Handles change events from the variant inputs in any `cart/add` forms that may
6 | * exist. Also updates the master select and triggers updates when the variants
7 | * price or image changes.
8 | *
9 | * @namespace variants
10 | */
11 |
12 | /* eslint-disable */
13 |
14 | slate.Variants = (function() {
15 |
16 | /**
17 | * Variant constructor
18 | *
19 | * @param {object} options - Settings from `product.js`
20 | */
21 | function Variants(options) {
22 | this.$container = options.$container;
23 | this.product = options.product;
24 | this.singleOptionSelector = options.singleOptionSelector;
25 | this.originalSelectorId = options.originalSelectorId;
26 | this.enableHistoryState = options.enableHistoryState;
27 | this.currentVariant = this._getVariantFromOptions();
28 |
29 | $(this.singleOptionSelector, this.$container).on('change', this._onSelectChange.bind(this));
30 | }
31 |
32 | Variants.prototype = $.extend({}, Variants.prototype, {
33 |
34 | /**
35 | * Get the currently selected options from add-to-cart form. Works with all
36 | * form input elements.
37 | *
38 | * @return {array} options - Values of currently selected variants
39 | */
40 | _getCurrentOptions: function() {
41 | var currentOptions = $.map($(this.singleOptionSelector, this.$container), function(element) {
42 | var $element = $(element);
43 | var type = $element.attr('type');
44 | var currentOption = {};
45 |
46 | if (type === 'radio' || type === 'checkbox') {
47 | if ($element[0].checked) {
48 | currentOption.value = $element.val();
49 | currentOption.index = $element.data('index');
50 |
51 | return currentOption;
52 | } else {
53 | return false;
54 | }
55 | } else {
56 | currentOption.value = $element.val();
57 | currentOption.index = $element.data('index');
58 |
59 | return currentOption;
60 | }
61 | });
62 |
63 | // remove any unchecked input values if using radio buttons or checkboxes
64 | currentOptions = slate.utils.compact(currentOptions);
65 |
66 | return currentOptions;
67 | },
68 |
69 | /**
70 | * Find variant based on selected values.
71 | *
72 | * @param {array} selectedValues - Values of variant inputs
73 | * @return {object || undefined} found - Variant object from product.variants
74 | */
75 | _getVariantFromOptions: function() {
76 | var selectedValues = this._getCurrentOptions();
77 | var variants = this.product.variants;
78 | var found = false;
79 |
80 | variants.forEach(function(variant) {
81 | var satisfied = true;
82 |
83 | selectedValues.forEach(function(option) {
84 | if (satisfied) {
85 | satisfied = (option.value === variant[option.index]);
86 | }
87 | });
88 |
89 | if (satisfied) {
90 | found = variant;
91 | }
92 | });
93 |
94 | return found || null;
95 | },
96 |
97 | /**
98 | * Event handler for when a variant input changes.
99 | */
100 | _onSelectChange: function() {
101 | var variant = this._getVariantFromOptions();
102 |
103 | this.$container.trigger({
104 | type: 'variantChange',
105 | variant: variant
106 | });
107 |
108 | if (!variant) {
109 | return;
110 | }
111 |
112 | this._updateMasterSelect(variant);
113 | this._updateImages(variant);
114 | this._updatePrice(variant);
115 | this.currentVariant = variant;
116 |
117 | if (this.enableHistoryState) {
118 | this._updateHistoryState(variant);
119 | }
120 | },
121 |
122 | /**
123 | * Trigger event when variant image changes
124 | *
125 | * @param {object} variant - Currently selected variant
126 | * @return {event} variantImageChange
127 | */
128 | _updateImages: function(variant) {
129 | var variantImage = variant.featured_image || {};
130 | var currentVariantImage = this.currentVariant.featured_image || {};
131 |
132 | if (!variant.featured_image || variantImage.src === currentVariantImage.src) {
133 | return;
134 | }
135 |
136 | this.$container.trigger({
137 | type: 'variantImageChange',
138 | variant: variant
139 | });
140 | },
141 |
142 | /**
143 | * Trigger event when variant price changes.
144 | *
145 | * @param {object} variant - Currently selected variant
146 | * @return {event} variantPriceChange
147 | */
148 | _updatePrice: function(variant) {
149 | if (variant.price === this.currentVariant.price && variant.compare_at_price === this.currentVariant.compare_at_price) {
150 | return;
151 | }
152 |
153 | this.$container.trigger({
154 | type: 'variantPriceChange',
155 | variant: variant
156 | });
157 | },
158 |
159 | /**
160 | * Update history state for product deeplinking
161 | *
162 | * @param {variant} variant - Currently selected variant
163 | * @return {k} [description]
164 | */
165 | _updateHistoryState: function(variant) {
166 | if (!history.replaceState || !variant) {
167 | return;
168 | }
169 |
170 | var newurl = window.location.protocol + '//' + window.location.host + window.location.pathname + '?variant=' + variant.id;
171 | window.history.replaceState({path: newurl}, '', newurl);
172 | },
173 |
174 | /**
175 | * Update hidden master select of variant change
176 | *
177 | * @param {variant} variant - Currently selected variant
178 | */
179 | _updateMasterSelect: function(variant) {
180 | $(this.originalSelectorId, this.$container)[0].value = variant.id;
181 | }
182 | });
183 |
184 | return Variants;
185 | })();
186 |
--------------------------------------------------------------------------------
/src/scripts/slater/cart.js:
--------------------------------------------------------------------------------
1 | import fetch from 'unfetch'
2 | import mitt from 'mitt'
3 | import app from '../app.js'
4 |
5 | const ev = mitt()
6 |
7 | export const on = ev.on
8 |
9 | export function addVariant (variant, quantity) {
10 | const numAvailable = variant.inventory_policy === 'deny' && variant.inventory_management === 'shopify' ? (
11 | variant.inventory_quantity
12 | ) : null // null means they can add as many as they want
13 |
14 | return fetchCart().then(({ items }) => {
15 | const existing = items.filter(item => item.id === variant.id)[0] || {}
16 | const numRequested = (existing.quantity || 0) + quantity
17 |
18 | if (numAvailable !== null && numRequested > numAvailable) {
19 | const err = `There are only ${numAvailable} of that product available, requested ${numRequested}.`
20 | ev.emit('error', err)
21 | throw new Error(err)
22 | } else {
23 | return addItemById(variant.id, quantity)
24 | }
25 | })
26 | }
27 |
28 | export function updateAddon (id, quantity) {
29 | return fetchCart().then(({ items }) => {
30 | for (let i = 0; i < items.length; i++) {
31 | if (items[i].variant_id === parseInt(id)) {
32 | return changeAddon(i + 1, quantity) // shopify cart is a 1-based index
33 | }
34 | }
35 | })
36 | }
37 |
38 | export function removeAddon (id) {
39 | return updateAddon(id, 0)
40 | }
41 |
42 | function changeAddon (line, quantity) {
43 | ev.emit('updating')
44 |
45 | return fetch('/cart/change.js', {
46 | method: 'POST',
47 | credentials: 'include',
48 | headers: {
49 | 'Content-Type': 'application/json'
50 | },
51 | body: JSON.stringify({ line, quantity })
52 | }).then(res => res.json()).then(cart => {
53 | ev.emit('addon', { item: null, cart })
54 | app.hydrate({ cart: cart })(() => console.log('updated') )
55 | return cart
56 | })
57 | }
58 |
59 | /**
60 | * Warning: this does not check available products first
61 | */
62 | export function addItemById (id, quantity) {
63 | ev.emit('updating')
64 |
65 | console.log('yo adddy')
66 |
67 | return fetch('/cart/add.js', {
68 | method: 'POST',
69 | credentials: 'include',
70 | headers: {
71 | 'Content-Type': 'application/json'
72 | },
73 | body: JSON.stringify({ id, quantity })
74 | }).then(r => r.json()).then(item => {
75 | return fetchCart().then(cart => {
76 | app.hydrate({ cart: cart })(() => console.log('updated'))
77 | app.actions.toggleCart()
78 | // ev.emit('updated', { item, cart })
79 | return { item, cart }
80 | })
81 | })
82 | }
83 |
84 | export function fetchCart () {
85 | return fetch('/cart.js', {
86 | method: 'GET',
87 | credentials: 'include'
88 | }).then(res => res.json())
89 | }
90 |
--------------------------------------------------------------------------------
/src/scripts/slater/currency.js:
--------------------------------------------------------------------------------
1 | import { defaultTo } from './utils.js'
2 |
3 | /**
4 | * Currency Helpers
5 | * -----------------------------------------------------------------------------
6 | * A collection of useful functions that help with currency formatting
7 | *
8 | * Current contents
9 | * - formatMoney - Takes an amount in cents and returns it as a formatted dollar value.
10 | *
11 | */
12 |
13 | /**
14 | * Format money values based on your shop currency settings
15 | * @param {Number|string} cents - value in cents or dollar amount e.g. 300 cents
16 | * or 3.00 dollars
17 | * @param {String} format - shop money_format setting
18 | * @return {String} value - formatted value
19 | */
20 |
21 | /* eslint-disable */
22 |
23 | export function formatMoney (cents, format = '${{amount}}') {
24 | if (typeof cents === 'string') {
25 | cents = cents.replace('.', '')
26 | }
27 |
28 | let value = ''
29 | const placeholderRegex = /\{\{\s*(\w+)\s*\}\}/
30 |
31 | function formatWithDelimiters (number, precision, thousands, decimal) {
32 | precision = defaultTo(precision, 2)
33 | thousands = defaultTo(thousands, ',')
34 | decimal = defaultTo(decimal, '.')
35 |
36 | if (isNaN(number) || number == null) {
37 | return 0
38 | }
39 |
40 | number = (number / 100.0).toFixed(precision)
41 |
42 | const parts = number.split('.')
43 | const dollarsAmount = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + thousands)
44 | const centsAmount = parts[1] ? (decimal + parts[1]) : ''
45 |
46 | return dollarsAmount + centsAmount
47 | }
48 |
49 | switch (format.match(placeholderRegex)[1]) {
50 | case 'amount':
51 | value = formatWithDelimiters(cents, 2)
52 | break
53 | case 'amount_no_decimals':
54 | value = formatWithDelimiters(cents, 0)
55 | break
56 | case 'amount_with_space_separator':
57 | value = formatWithDelimiters(cents, 2, ' ', '.')
58 | break
59 | case 'amount_no_decimals_with_comma_separator':
60 | value = formatWithDelimiters(cents, 0, ',', '.')
61 | break
62 | case 'amount_no_decimals_with_space_separator':
63 | value = formatWithDelimiters(cents, 0, ' ')
64 | break
65 | }
66 |
67 | return format.replace(placeholderRegex, value)
68 | }
69 |
--------------------------------------------------------------------------------
/src/scripts/slater/images.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Image Helper Functions
3 | * -----------------------------------------------------------------------------
4 | * A collection of functions that help with basic image operations.
5 | *
6 | */
7 |
8 | /**
9 | * Preloads an image in memory and uses the browsers cache to store it until needed.
10 | *
11 | * @param {Array} images - A list of image urls
12 | * @param {String} size - A shopify image size attribute
13 | */
14 | export function preload (images, size) {
15 | if (typeof images === 'string') {
16 | images = [images]
17 | }
18 |
19 | for (var i = 0; i < images.length; i++) {
20 | var image = images[i]
21 | loadImage(getSizedImageUrl(image, size))
22 | }
23 | }
24 |
25 | /**
26 | * Loads and caches an image in the browsers cache.
27 | * @param {string} path - An image url
28 | */
29 | export function loadImage (path) {
30 | /* eslint-disable */
31 | new Image().src = path
32 | /* eslint-enable */
33 | }
34 |
35 | /**
36 | * Find the Shopify image attribute size
37 | *
38 | * @param {string} src
39 | * @returns {null}
40 | */
41 | export function imageSize (src) {
42 | /* eslint-disable */
43 | var match = src.match(/.+_((?:pico|icon|thumb|small|compact|medium|large|grande)|\d{1,4}x\d{0,4}|x\d{1,4})[_\.@]/)
44 | /* esling-enable */
45 |
46 | if (match) {
47 | return match[1]
48 | } else {
49 | return null
50 | }
51 | }
52 |
53 | /**
54 | * Adds a Shopify size attribute to a URL
55 | *
56 | * @param src
57 | * @param size
58 | * @returns {*}
59 | */
60 | export function getSizedImageUrl (src, size) {
61 | if (size === null) {
62 | return src
63 | }
64 |
65 | if (size === 'master') {
66 | return removeProtocol(src)
67 | }
68 |
69 | var match = src.match(/\.(jpg|jpeg|gif|png|bmp|bitmap|tiff|tif)(\?v=\d+)?$/i)
70 |
71 | if (match) {
72 | var prefix = src.split(match[0])
73 | var suffix = match[0]
74 |
75 | return removeProtocol(prefix[0] + '_' + size + suffix)
76 | } else {
77 | return null
78 | }
79 | }
80 |
81 | export function removeProtocol (path) {
82 | return path.replace(/http(s)?:/, '')
83 | }
84 |
--------------------------------------------------------------------------------
/src/scripts/slater/product-selector.js:
--------------------------------------------------------------------------------
1 | import mitt from 'mitt'
2 |
3 | export default function ProductSelector (
4 | config = {
5 | main: '[data-product-select]',
6 | options: '[data-single-option-selector]',
7 | data: '[data-product-json]'
8 | }
9 | ) {
10 | const ev = mitt()
11 |
12 | const main = document.querySelector(config.main)
13 | const options = [].slice.call(
14 | document.querySelectorAll(config.options)
15 | )
16 | const data = JSON.parse(
17 | document.querySelector(config.data).innerHTML
18 | )
19 |
20 | options.forEach(opt => opt.addEventListener('change', e => {
21 | const val = options.reduce((res, opt, i) => {
22 | res += i < options.length - 1 ? opt.value + ' / ' : opt.value
23 | return res
24 | }, '')
25 |
26 | for (let i = 0; i < main.options.length; i++) {
27 | if (main.options[i].text === val) {
28 | main.selectedIndex = i
29 | break
30 | }
31 | }
32 |
33 | ev.emit('update', data.variants.filter(v => v.title === val)[0])
34 | }))
35 |
36 | return {
37 | on: ev.on,
38 | destroy () {
39 | options.forEach(opt => {
40 | // opt.removeEventListener('change', updateSelect)
41 | })
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/scripts/slater/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Utility helpers
3 | * -----------------------------------------------------------------------------
4 | * A collection of useful functions for dealing with arrays and objects
5 | *
6 | * @namespace utils
7 | */
8 |
9 | /**
10 | * Return an object from an array of objects that matches the provided key and value
11 | *
12 | * @param {array} array - Array of objects
13 | * @param {string} key - Key to match the value against
14 | * @param {string} value - Value to get match of
15 | */
16 | export function findInstance (array, key, value) {
17 | for (let i = 0; i < array.length; i++) {
18 | if (array[i][key] === value) {
19 | return array[i]
20 | }
21 | }
22 | }
23 |
24 | /**
25 | * Remove an object from an array of objects by matching the provided key and value
26 | *
27 | * @param {array} array - Array of objects
28 | * @param {string} key - Key to match the value against
29 | * @param {string} value - Value to get match of
30 | */
31 | export function removeInstance (array, key, value) {
32 | let i = array.length
33 |
34 | while (i--) {
35 | if (array[i][key] === value) {
36 | array.splice(i, 1)
37 | break
38 | }
39 | }
40 |
41 | return array
42 | }
43 |
44 | /**
45 | * _.compact from lodash
46 | * Remove empty/false items from array
47 | * Source: https://github.com/lodash/lodash/blob/master/compact.js
48 | *
49 | * @param {array} array
50 | */
51 | export function compact (array) {
52 | let index = -1
53 | let resIndex = 0
54 | const length = array == null ? 0 : array.length
55 | const result = []
56 |
57 | while (++index < length) {
58 | const value = array[index]
59 |
60 | if (value) {
61 | result[resIndex++] = value
62 | }
63 | }
64 |
65 | return result
66 | }
67 |
68 | /**
69 | * _.defaultTo from lodash
70 | * Checks `value` to determine whether a default value should be returned in
71 | * its place. The `defaultValue` is returned if `value` is `NaN`, `null`,
72 | * or `undefined`.
73 | * Source: https://github.com/lodash/lodash/blob/master/defaultTo.js
74 | *
75 | * @param {*} value - Value to check
76 | * @param {*} defaultValue - Default value
77 | * @returns {*} - Returns the resolved value
78 | */
79 | export function defaultTo (value, defaultValue) {
80 | return (value == null) ? defaultValue : value
81 | }
82 |
--------------------------------------------------------------------------------
/src/scripts/templates/customers-addresses.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Customer Addresses Script
3 | * ------------------------------------------------------------------------------
4 | * A file that contains scripts highly couple code to the Customer Addresses
5 | * template.
6 | *
7 | * @namespace customerAddresses
8 | */
9 |
10 | /* eslint-disable */
11 |
12 | theme.customerAddresses = (function() {
13 | var $newAddressForm = $('#AddressNewForm');
14 |
15 | if (!$newAddressForm.length) {
16 | return;
17 | }
18 |
19 | // Initialize observers on address selectors, defined in shopify_common.js
20 | if (Shopify) {
21 | new Shopify.CountryProvinceSelector('AddressCountryNew', 'AddressProvinceNew', {
22 | hideElement: 'AddressProvinceContainerNew'
23 | });
24 | }
25 |
26 | // Initialize each edit form's country/province selector
27 | $('.address-country-option').each(function() {
28 | var formId = $(this).data('form-id');
29 | var countrySelector = 'AddressCountry_' + formId;
30 | var provinceSelector = 'AddressProvince_' + formId;
31 | var containerSelector = 'AddressProvinceContainer_' + formId;
32 |
33 | new Shopify.CountryProvinceSelector(countrySelector, provinceSelector, {
34 | hideElement: containerSelector
35 | });
36 | });
37 |
38 | // Toggle new/edit address forms
39 | $('.address-new-toggle').on('click', function() {
40 | $newAddressForm.toggleClass('hide');
41 | });
42 |
43 | $('.address-edit-toggle').on('click', function() {
44 | var formId = $(this).data('form-id');
45 | $('#EditAddress_' + formId).toggleClass('hide');
46 | });
47 |
48 | $('.address-delete').on('click', function() {
49 | var $el = $(this);
50 | var formId = $el.data('form-id');
51 | var confirmMessage = $el.data('confirm-message');
52 | if (confirm(confirmMessage || 'Are you sure you wish to delete this address?')) {
53 | Shopify.postLink('/account/addresses/' + formId, {parameters: {_method: 'delete'}});
54 | }
55 | });
56 | })();
57 |
--------------------------------------------------------------------------------
/src/scripts/templates/customers-login.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Password Template Script
3 | * ------------------------------------------------------------------------------
4 | * A file that contains scripts highly couple code to the Password template.
5 | *
6 | * @namespace password
7 | */
8 |
9 | /* eslint-disable */
10 |
11 | theme.customerLogin = (function() {
12 | var config = {
13 | recoverPasswordForm: '#RecoverPassword',
14 | hideRecoverPasswordLink: '#HideRecoverPasswordLink'
15 | };
16 |
17 | if (!$(config.recoverPasswordForm).length) {
18 | return;
19 | }
20 |
21 | checkUrlHash();
22 | resetPasswordSuccess();
23 |
24 | $(config.recoverPasswordForm).on('click', onShowHidePasswordForm);
25 | $(config.hideRecoverPasswordLink).on('click', onShowHidePasswordForm);
26 |
27 | function onShowHidePasswordForm(evt) {
28 | evt.preventDefault();
29 | toggleRecoverPasswordForm();
30 | }
31 |
32 | function checkUrlHash() {
33 | var hash = window.location.hash;
34 |
35 | // Allow deep linking to recover password form
36 | if (hash === '#recover') {
37 | toggleRecoverPasswordForm();
38 | }
39 | }
40 |
41 | /**
42 | * Show/Hide recover password form
43 | */
44 | function toggleRecoverPasswordForm() {
45 | $('#RecoverPasswordForm').toggleClass('hide');
46 | $('#CustomerLoginForm').toggleClass('hide');
47 | }
48 |
49 | /**
50 | * Show reset password success message
51 | */
52 | function resetPasswordSuccess() {
53 | var $formState = $('.reset-password-success');
54 |
55 | // check if reset password form was successfully submited.
56 | if (!$formState.length) {
57 | return;
58 | }
59 |
60 | // show success message
61 | $('#ResetSuccess').removeClass('hide');
62 | }
63 | })();
64 |
--------------------------------------------------------------------------------
/src/scripts/util/theme-provider.js:
--------------------------------------------------------------------------------
1 | export default div => {
2 | const theme = div.getAttribute('data-theme')
3 | const header = document.getElementById('header')
4 |
5 | if (!/light|dark/.test(theme)) {
6 | return console.warn(`Header theme must be either 'light' or 'dark', not ${theme}.`)
7 | }
8 |
9 | if (theme === 'light') {
10 | header.classList.add('header--light')
11 | } else {
12 | header.classList.remove('header--light')
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/sections/collection-list.liquid:
--------------------------------------------------------------------------------
1 | {% if section.settings.title != blank %}
2 | {{ section.settings.title | escape }}
3 | {% endif %}
4 |
5 | {% for block in section.blocks limit: section.blocks.size %}
6 |
28 | {% endfor %}
29 |
30 | {% if section.blocks.size == 0 %}
31 | {% include 'no-blocks' %}
32 | {% endif %}
33 |
34 | {% schema %}
35 | {
36 | "name": "Collection list",
37 | "max_blocks": 12,
38 | "settings": [
39 | {
40 | "type": "text",
41 | "id": "title",
42 | "label": "Heading",
43 | "default": "Collection list"
44 | }
45 | ],
46 | "blocks": [
47 | {
48 | "type": "collection",
49 | "name": "Collection",
50 | "settings": [
51 | {
52 | "label": "Collection",
53 | "id": "collection",
54 | "type": "collection"
55 | }
56 | ]
57 | }
58 | ],
59 | "presets": [
60 | {
61 | "name": "Collection list",
62 | "category": "Collection",
63 | "blocks": [
64 | {
65 | "type": "collection"
66 | },
67 | {
68 | "type": "collection"
69 | },
70 | {
71 | "type": "collection"
72 | }
73 | ]
74 | }
75 | ]
76 | }
77 | {% endschema %}
78 |
--------------------------------------------------------------------------------
/src/sections/featured-collection.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {% if section.settings.title != blank %}
6 |
{{ section.settings.title | escape }}
7 | {% endif %}
8 |
9 | {%- assign collection = collections[section.settings.collection] -%}
10 |
11 |
12 | {% for product in collection.products limit: 6 %}
13 |
35 | {% endfor %}
36 |
37 |
38 |
39 |
40 |
41 | {% schema %}
42 | {
43 | "name": "Featured collection",
44 | "settings": [
45 | {
46 | "type": "text",
47 | "id": "title",
48 | "label": "Heading",
49 | "default": "Featured collection"
50 | },
51 | {
52 | "id": "collection",
53 | "type": "collection",
54 | "label": "Collection"
55 | }
56 | ],
57 | "presets": [
58 | {
59 | "name": "Featured collection",
60 | "category": "Collection"
61 | }
62 | ]
63 | }
64 |
65 | {% endschema %}
66 |
--------------------------------------------------------------------------------
/src/sections/featured-product.liquid:
--------------------------------------------------------------------------------
1 | {%- assign product = all_products[section.settings.product] -%}
2 | {%- assign current_variant = product.selected_or_first_available_variant -%}
3 |
4 | {% if product == empty %}
5 | {%- assign section_onboarding = true -%}
6 | {%- assign onboarding_title = 'homepage.onboarding.product_title' | t -%}
7 | {% endif %}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 | {% if product.featured_image.src != blank %}
25 |
26 | {% else %}
27 | {{ 'product-1' | placeholder_svg_tag: 'placeholder-svg placeholder-svg--small' }}
28 | {% endif %}
29 |
30 | {% if product.images.size > 1 %}
31 |
32 | {% for image in product.images %}
33 |
34 |
35 |
36 |
37 |
38 | {% endfor %}
39 |
40 | {% endif %}
41 |
42 |
{{ product.title | default: onboarding_title }}
43 | {% if product.vendor != blank %}
44 |
{{ product.vendor }}
45 | {% endif %}
46 |
47 |
48 |
49 |
50 |
51 |
52 | {% include 'add-to-cart-form' %}
53 |
54 |
55 |
56 |
57 | {{ product.description }}
58 |
59 |
60 | {% if section.settings.show_share_buttons %}
61 | {% include 'social-sharing', share_title: product.title, share_permalink: product.url, share_image: product %}
62 | {% endif %}
63 |
64 | {% unless product == empty %}
65 |
68 | {% endunless %}
69 |
70 |
71 | {% schema %}
72 | {
73 | "name": "Featured product",
74 | "settings": [
75 | {
76 | "type": "product",
77 | "id": "product",
78 | "label": "Product"
79 | },
80 | {
81 | "type": "checkbox",
82 | "id": "show_share_buttons",
83 | "label": "Show social sharing buttons"
84 | }
85 | ],
86 | "presets": [
87 | {
88 | "name": "Featured product",
89 | "category": "Product"
90 | }
91 | ]
92 | }
93 | {% endschema %}
94 |
--------------------------------------------------------------------------------
/src/sections/footer.liquid:
--------------------------------------------------------------------------------
1 |
16 |
17 | {% schema %}
18 | {
19 | "name": "Footer",
20 | "settings": [
21 | {
22 | "type": "link_list",
23 | "id": "footer_menu",
24 | "label": "First menu",
25 | "default": "footer"
26 | }
27 | ]
28 | }
29 | {% endschema %}
30 |
--------------------------------------------------------------------------------
/src/sections/header.liquid:
--------------------------------------------------------------------------------
1 |
7 |
8 |
53 |
--------------------------------------------------------------------------------
/src/sections/product.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 | {%- assign current_variant = product.selected_or_first_available_variant -%}
4 | {%- assign featured_image = current_variant.featured_image | default: product.featured_image -%}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
20 |
21 |
22 | {% if product.images.size > 1 %}
23 |
24 | {% for image in product.images %}
25 |
26 |
27 |
28 |
29 |
30 | {% endfor %}
31 |
32 | {% endif %}
33 |
34 |
{{ product.title }}
35 |
{{ product.vendor }}
36 |
37 |
38 |
39 |
40 |
41 | {% include 'add-to-cart-form' %}
42 |
43 |
44 |
45 | {{ product.description }}
46 |
47 |
48 | {% comment %}
49 |
50 | Commenting out social sharing for now
51 |
52 | {% if section.settings.show_share_buttons %}
53 | {% include 'social-sharing', share_title: product.title, share_permalink: product.url, share_image: product %}
54 | {% endif %}
55 |
56 | {% endcomment %}
57 |
58 | {% unless product == empty %}
59 |
62 | {% endunless %}
63 |
64 |
65 | {% schema %}
66 | {
67 | "name": "Product pages",
68 | "settings": [
69 | {
70 | "type": "checkbox",
71 | "id": "show_share_buttons",
72 | "label": "Show social sharing buttons",
73 | "default": true
74 | }
75 | ]
76 | }
77 | {% endschema %}
78 |
--------------------------------------------------------------------------------
/src/sections/s-cart.liquid:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/src/snippets/account-address.liquid:
--------------------------------------------------------------------------------
1 |
2 | {% if default %}
3 |
Default
4 | {% endif %}
5 |
6 |
7 | {{ address.name }}
8 | {% if address.company %}{{ address.company }} {% endif %}
9 | {{ address.street }}
10 | {{ address.city | capitalize }}{% if address.province %}, {{ address.province | upcase }} {% endif %} {{ address.zip | upcase }}
11 | {{ address.country }}
12 | {% if address.phone != '' %}{{ address.phone }}{% endif %}
13 |
14 |
15 | {% if edit %}
16 |
20 | {% endif %}
21 |
22 |
--------------------------------------------------------------------------------
/src/snippets/account-titles.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
{{ title }}
7 |
8 |
9 |
10 |
11 | {% if template contains 'account' %}
12 | Account Home
13 | {% else %}
14 | Account Home
15 | {% endif %}
16 |
17 |
18 |
19 | {% if template contains 'address' %}
20 | Addresses
21 | {% else %}
22 | Addresses
23 | {% endif %}
24 |
25 |
26 |
27 | Logout
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/snippets/add-to-cart-form.liquid:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/snippets/button.liquid:
--------------------------------------------------------------------------------
1 | {% assign tag = tag | default: 'a' %}
2 |
3 | {% capture cx %}
4 | button rel sans caps track
5 | {{ className }}
6 | {% if outline == true %}button--outline{% endif %}
7 | {% if small == true %}button--small{% endif %}
8 | {% if light == true %}button--light{% endif %}
9 | {% if inherit == true %}button--inherit{% endif %}
10 | {% endcapture %}
11 |
12 | <{{ tag }}
13 | {% if href %}href='{{ href }}'{% endif %}
14 | {% if type %}type='{{ type }}'{% endif %}
15 | {% if name %}name='{{ name }}'{% endif %}
16 | class='{% include 'u-cx' with cx %}'
17 | role='button'>
18 |
19 | {{ cta }}
20 |
21 | {{ tag }}>
22 |
--------------------------------------------------------------------------------
/src/snippets/cart-drawer.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Subtotal: $-.--
26 |
27 | {%
28 | include 'button' with
29 | tag: 'a',
30 | cta: 'Checkout',
31 | href: '/checkout',
32 | className: 'fill-y no-ajax'
33 | %}
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/snippets/form-input-select.liquid:
--------------------------------------------------------------------------------
1 | {% if required == blank %}
2 | {% assign required = 'false' %}
3 | {% endif %}
4 |
5 | {%- capture id -%}
6 | {{type}}{{name}}{{ className | replace: ' ', '' }}
7 | {%- endcapture -%}
8 |
9 | {%- capture cx -%}
10 | select-wrapper x rel
11 | {{ className }}
12 | {% if boxed %}is-boxed{% endif %}
13 | {%- endcapture -%}
14 |
15 |
16 | {{ label }}
17 |
23 | {% if placeholder %}{{ placeholder }} {% endif %}
24 | {{ options }}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/snippets/form-input-text.liquid:
--------------------------------------------------------------------------------
1 | {% if type == blank %}
2 | {% assign type = 'text' %}
3 | {% endif %}
4 | {% if autocomplete == blank %}
5 | {% assign autocomplete = 'off' %}
6 | {% endif %}
7 | {% if autocapitalize == blank %}
8 | {% assign autocapitalize = 'off' %}
9 | {% endif %}
10 | {% if spellcheck == blank %}
11 | {% assign spellcheck = 'false' %}
12 | {% endif %}
13 | {% if required == blank %}
14 | {% assign required = 'false' %}
15 | {% endif %}
16 |
17 | {%- capture id -%}
18 | {{type}}{{name}}{{ className | replace: ' ', '' }}
19 | {%- endcapture -%}
20 |
21 | {%- capture cx -%}
22 | input-wrapper x
23 | {{ className }}
24 | {%- endcapture -%}
25 |
26 |
27 |
39 | {{ label }}
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/snippets/form-input-textarea.liquid:
--------------------------------------------------------------------------------
1 | {% if type == blank %}
2 | {% assign type = 'text' %}
3 | {% endif %}
4 | {% if autocomplete == blank %}
5 | {% assign autocomplete = 'off' %}
6 | {% endif %}
7 | {% if autocapitalize == blank %}
8 | {% assign autocapitalize = 'off' %}
9 | {% endif %}
10 | {% if spellcheck == blank %}
11 | {% assign spellcheck = 'false' %}
12 | {% endif %}
13 | {% if rows == blank %}
14 | {% assign rows = '3' %}
15 | {% endif %}
16 | {% if required == blank %}
17 | {% assign required = 'false' %}
18 | {% endif %}
19 |
20 | {%- capture cx -%}
21 | input-wrapper x
22 | {{ className }}
23 | {%- endcapture -%}
24 |
25 |
26 |
37 | {{ label }}
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/snippets/head-meta.liquid:
--------------------------------------------------------------------------------
1 | {% assign social_type = 'website' %}
2 | {% assign social_title = page_title | strip_html | escape %}
3 | {% assign social_description = page_description | strip_html | escape %}
4 | {% assign social_image = 'social-image.jpg' | file_url %}
5 |
6 | {% if template contains 'product' %}
7 | {% assign social_type = 'product' %}
8 | {% assign social_title = product.title | strip_html | escape %}
9 | {% assign social_price = product.price | money_without_currency | strip_html | escape %}
10 | {% assign social_currency = shop.currency %}
11 | {% if product.featured_image %}
12 | {% assign social_product_img = current_variant.featured_image | default: product.featured_image %}
13 | {% assign social_image = social_product_img | img_url: '1024x' %}
14 | {% endif %}
15 | {% elsif template contains 'collection' %}
16 | {% if collection.description != blank %}
17 | {% assign social_description = collection.description | strip_html | escape %}
18 | {% endif %}
19 | {% if collection.image %}
20 | {% assign social_image = collection.image | img_url: 'master' %}
21 | {% endif %}
22 | {% elsif template contains 'blog' %}
23 | {% for article in blog.articles %}
24 | {% if article.image %}
25 | {% assign social_image = article.image | img_url: 'master' %}
26 | {% break %}
27 | {% endif %}
28 | {% endfor %}
29 | {% elsif template contains 'article' %}
30 | {% assign social_type = 'article' %}
31 | {% assign img_tag = '<' | append: 'img' %}
32 | {% if article.image %}
33 | {% assign social_image = article.image | img_url: 'master' %}
34 | {% elsif article.content contains img_tag %}
35 | {% assign src = article.content | split: 'src="' %}
36 | {% assign src = src[1] | split: '"' | first | remove: 'https:' | remove: 'http:' %}
37 | {% assign social_image = src %}
38 | {% endif %}
39 | {% endif %}
40 |
41 | {% comment %}
42 | Page-level Overrides
43 |
44 | Override variable assignments below this
45 | using `template contains` and similar operators.
46 | {% endcomment %}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | {% if social_price %} {% endif %}
57 | {% if social_currency %} {% endif %}
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/snippets/icon-chevron.liquid:
--------------------------------------------------------------------------------
1 | {% capture cx %}
2 | chevron
3 | {% if left == true %}is-left{% endif %}
4 | {% if down == true %}is-down{% endif %}
5 | {% endcapture %}
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/snippets/navigation.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% for link in linklists.main-menu.links %}
4 | {% assign child_list_handle = link.title | handleize %}
5 |
6 | {% comment %}For sub-menus{% endcomment %}
7 | {% if menus[child_list_handle].links != blank %}
8 |
20 | {% else %}
21 |
22 | {{ link.title }}
23 |
24 | {% endif %}
25 | {% endfor %}
26 |
27 |
--------------------------------------------------------------------------------
/src/snippets/no-blocks.liquid:
--------------------------------------------------------------------------------
1 |
2 | {{ 'homepage.onboarding.no_content' | t }}
3 |
4 |
--------------------------------------------------------------------------------
/src/snippets/pagination.liquid:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/snippets/search.liquid:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/snippets/social-sharing.liquid:
--------------------------------------------------------------------------------
1 |
2 |
29 |
--------------------------------------------------------------------------------
/src/snippets/theme-provider.liquid:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/styles/main.css:
--------------------------------------------------------------------------------
1 | @import 'svbstrate/src/lib/reset.css';
2 | @import 'svbstrate/src/lib/display.css';
3 | @import 'svbstrate/src/lib/positioning.css';
4 | @import 'svbstrate/src/lib/flexbox.css';
5 | @import 'svbstrate/src/lib/align.css';
6 | @import 'svbstrate/src/lib/spacing.css';
7 | @import 'svbstrate/src/lib/buttons.css';
8 | @import 'svbstrate/src/lib/forms.css';
9 | @import 'svbstrate/src/lib/z-index.css';
10 |
11 | /* Default Slater Theme */
12 | @import 'slater/lib/var';
13 | @import 'slater/lib/colors';
14 | @import 'slater/lib/breakpoint';
15 | @import 'slater/lib/spacing';
16 | @import 'slater/lib/containers';
17 | @import 'slater/lib/forms';
18 | @import 'slater/lib/lists';
19 | @import 'slater/lib/visibilty';
20 |
21 | @import 'slater/components/cart-drawer';
22 | @import 'slater/components/page-transition';
23 | @import 'slater/components/accounts';
24 | @import 'slater/components/buttons';
25 |
26 | body {
27 | background-color: pink;
28 | color: red;
29 | }
--------------------------------------------------------------------------------
/src/styles/slater/components/buttons.css:
--------------------------------------------------------------------------------
1 | button {
2 | border: 0;
3 | padding: 1.1rem 1.3rem;
4 | }
--------------------------------------------------------------------------------
/src/styles/slater/components/cart-drawer.css:
--------------------------------------------------------------------------------
1 | .cart-quantity {
2 | cursor: pointer;
3 | }
4 |
5 | .cart-drawer-outer {
6 | display: none;
7 |
8 | &.is-active {
9 | display: block;
10 | }
11 | &.is-visible {
12 | .cart-drawer {
13 | transform: translateX(0);
14 | }
15 | .cart-drawer-overlay {
16 | opacity: 0.8;
17 | }
18 | }
19 | }
20 | .cart-drawer-overlay {
21 | opacity: 0;
22 | transition: all var(--slow) var(--ease);
23 | }
24 | .cart-drawer {
25 | max-width: 500px;
26 | transition: all var(--slow) var(--ease);
27 | transform: translateX(100%);
28 | flex-direction: column;
29 | padding-bottom: 160px; /* make room for footer */
30 |
31 | header {
32 | border-bottom: 1px solid var(--gray);
33 | height: 75px;
34 |
35 | @media (--m) {
36 | height: 100px;
37 | }
38 |
39 | button {
40 | height: 30px;
41 | width: 30px;
42 |
43 | &:focus {
44 | outline: 1px dashed var(--gray);
45 | }
46 |
47 | svg {
48 | height: 15px;
49 | width: 15px;
50 | }
51 | }
52 | }
53 | }
54 | .cart-drawer__items {
55 | overflow: auto;
56 | padding-top: 75px;
57 |
58 | @media (--m) {
59 | padding-top: 100px;
60 | }
61 |
62 | svg.loader {
63 | width: 100px;
64 | height: 100px;
65 | animation-duration: 1.5s;
66 | animation-name: pulse;
67 | animation-iteration-count: infinite;
68 | }
69 | }
70 |
71 | .cart-drawer__item {
72 | padding: 2em 0 1.5em;
73 | border-top: 1px solid var(--gray);
74 |
75 | &:first-child {
76 | border-top: 0;
77 | }
78 |
79 | img {
80 | width: 90px;
81 | }
82 |
83 | button {
84 | top: 1.5em;
85 |
86 | svg {
87 | width: 10px;
88 | height: 10px;
89 | }
90 | }
91 | }
92 | .cart-drawer__item__remove {
93 | top: 1.5em;
94 |
95 | svg {
96 | width: 10px;
97 | height: 10px;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/styles/slater/components/page-transition.css:
--------------------------------------------------------------------------------
1 | .page-transition {
2 | transition: all var(--slow) var(--ease);
3 | height: 100vh;
4 | width: 100vw;
5 | position: fixed;
6 | top: 0;
7 | left: 0;
8 | transform: translateY(100%);
9 | &.cover {
10 | transform: translateY(0);
11 | }
12 | }
--------------------------------------------------------------------------------
/src/styles/slater/lib/breakpoint.css:
--------------------------------------------------------------------------------
1 | @custom-media --s (min-width: 37.5em); /* 600 */
2 | @custom-media --ms (max-width: 37.5em); /* 600 */
3 | @custom-media --m (min-width: 56.26em); /* 900 */
4 | @custom-media --mm (max-width: 56.26em); /* 900 */
5 | @custom-media --l (min-width: 75em); /* 1200 */
6 | @custom-media --xl (min-width: 81.25em); /* 1300 */
7 |
--------------------------------------------------------------------------------
/src/styles/slater/lib/colors.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --white: #F4F4F4;
3 | --gray-medium: #EAEAEA;
4 | --gray: #EAEAEA;
5 | --ruby: #BB3136;
6 | }
7 |
8 | .cw {
9 | color: var(--white);
10 | }
11 |
12 | .cgm {
13 | color: var(--white);
14 | }
15 |
16 | .bcw {
17 | background-color: var(--white);
18 | }
19 |
20 | .bcgm {
21 | background-color: var(--gray-medium);
22 | }
23 |
24 | .bcr {
25 | background-color: var(--ruby);
26 | }
--------------------------------------------------------------------------------
/src/styles/slater/lib/containers.css:
--------------------------------------------------------------------------------
1 | .container--c {
2 | margin-right: auto;
3 | margin-left: auto;
4 | }
5 |
6 | .container--l {
7 | max-width: 1280px;
8 | }
9 |
10 | .container--xl {
11 | max-width: 1480px;
12 | }
13 |
14 | .outer {
15 | width: 100%;
16 | padding-left: 1.5em;
17 | padding-right: 1.5em;
18 | @media (--m) {
19 | padding-left: 3em;
20 | padding-right: 3em;
21 | }
22 | }
23 | .outer--v {
24 | padding-top: 1.5em;
25 | padding-bottom: 1.5em;
26 | }
27 | .container {
28 | margin-left: auto;
29 | margin-right: auto;
30 | }
31 |
32 | .container--s {
33 | max-width: 480px;
34 | width: 100%;
35 | }
36 |
37 | .container--a {
38 | max-width: 630px;
39 | }
40 |
41 | .container--m {
42 | max-width: 700px;
43 | }
44 |
45 | .container--mm {
46 | max-width: 890px;
47 | }
--------------------------------------------------------------------------------
/src/styles/slater/lib/forms.css:
--------------------------------------------------------------------------------
1 | form {
2 | margin: 0;
3 | }
4 | input,
5 | textarea,
6 | select {
7 | display: block;
8 | outline: 0;
9 | border-radius: 0;
10 | border: 0;
11 | position: relative;
12 | font-size: inherit;
13 | background-color: transparent;
14 | padding: 0;
15 | color: var(--ruby);
16 | border-bottom: 1px solid var(--ruby);
17 |
18 | &:focus {
19 | /* border-color: black; */
20 | }
21 | &::-webkit-input-placeholder {
22 | color: var(--ruby);
23 | }
24 | /* Change the white to any color ;) */
25 | &:-webkit-autofill {
26 | -webkit-box-shadow: 0 0 0 30px white inset;
27 | }
28 | }
29 | input,
30 | select {
31 | height: 55px;
32 | }
33 | input[type='checkbox'] {
34 | height: auto;
35 | }
36 | textarea {
37 | max-width: 100%;
38 | overflow: auto;
39 | resize: vertical;
40 | min-height: 55px;
41 | padding-top: 0.5em;
42 | }
43 | .input--reset {
44 | height: auto;
45 | padding: 0;
46 | }
47 | .select-wrapper {
48 | display: block;
49 | position: relative;
50 |
51 | svg {
52 | }
53 |
54 | select {
55 | appearance: none;
56 | padding-right: 50px;
57 | width: 100%;
58 | }
59 |
60 | &.is-boxed {
61 | svg {
62 | right: 2em;
63 | }
64 | select {
65 | height: 75px;
66 | padding-left: 2em;
67 | padding-right: 2em;
68 |
69 | &:focus {
70 | border-color: black;
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/styles/slater/lib/lists.css:
--------------------------------------------------------------------------------
1 | ol, ul {
2 | list-style: none;
3 | padding: 0;
4 | margin: 0;
5 | }
6 | ul.list,
7 | ol.list {
8 | padding-left: 2em;
9 | }
10 | ol.list {
11 | list-style: decimal;
12 | }
13 | ul.list {
14 | list-style: disc;
15 | }
16 |
--------------------------------------------------------------------------------
/src/styles/slater/lib/spacing.css:
--------------------------------------------------------------------------------
1 | .mt15, .my15, .m15 { margin-top: 1.5em }
2 | .mb15, .my15, .m15 { margin-bottom: 1.5em }
3 | .ml15, .mx15, .m15 { margin-left: 1.5em }
4 | .mr15, .mx15, .m15 { margin-right: 1.5em }
5 | .pt15, .pv15, .p15 { padding-top: 1.5em }
6 | .pb15, .pv15, .p15 { padding-bottom: 1.5em }
7 | .pl15, .py15, .p15 { padding-left: 1.5em }
8 | .pr15, .py15, .p15 { padding-right: 1.5em }
9 |
10 | .mt2, .my2, .m2 { margin-top: 2em }
11 | .mb2, .my2, .m2 { margin-bottom: 2em }
12 | .ml2, .mx2, .m2 { margin-left: 2em }
13 | .mr2, .mx2, .m2 { margin-right: 2em }
14 | .pt2, .pv2, .p2 { padding-top: 2em }
15 | .pb2, .pv2, .p2 { padding-bottom: 2em }
16 | .pl2, .py2, .p2 { padding-left: 2em }
17 | .pr2, .py2, .p2 { padding-right: 2em }
18 |
--------------------------------------------------------------------------------
/src/styles/slater/lib/typography.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | color: var(--black);
4 | font-family: -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
5 | font-size: 100%;
6 | line-height: 1.7;
7 | font-weight: 500;
8 | -webkit-font-smoothing: antialiased;
9 | }
10 |
11 | /**
12 | * @font-face definitions in /assets/fonts.css
13 | * and linked in of theme.liquid
14 | */
15 | body, .text {
16 | font-family: 'sofia-pro';
17 | }
18 | .serif {
19 | font-family: 'freight-display-pro', serif;
20 | }
21 | .sans {
22 | font-family: 'sofia-pro';
23 | letter-spacing: 0.1em;
24 | }
25 | .light {
26 | font-weight: 400;
27 | }
28 | .medium {
29 | font-weight: 500;
30 | }
31 | h1, h2, h3, h4, h5, h6 {
32 | font-weight: 400;
33 | }
34 | .h0 {
35 | font-size: calc((54 /16) * 1rem);
36 | @media (--m) {
37 | font-size: calc((96 /16) * 1rem);
38 | }
39 | }
40 | .s1,
41 | h1, .h1 {
42 | font-size: calc((34 /16) * 1rem);
43 | @media (--s) {
44 | font-size: calc((54 /16) * 1rem);
45 | }
46 | @media (--m) {
47 | font-size: calc((110 /16) * 1rem);
48 | }
49 | }
50 |
51 | h1.hero-h1 {
52 | @media (--s) {
53 | font-size: calc((70/16) * 1rem);
54 | }
55 | @media (--m) {
56 | font-size: calc((90/16) * 1rem);
57 | }
58 | @media (--l) {
59 | font-size: calc((110/16) * 1rem);
60 | }
61 | }
62 |
63 | h1, .h1 {
64 | line-height: 1.1;
65 | }
66 | .s2,
67 | h2, .h2 {
68 | font-size: calc((32 /16) * 1rem);
69 | @media (--m) {
70 | font-size: calc((54 /16) * 1rem);
71 | }
72 | @media (--l) {
73 | font-size: calc((72 /16) * 1rem);
74 | }
75 | }
76 | h2, .h2 {
77 | line-height: 1.3;
78 | }
79 | .s3,
80 | h3, .h3 {
81 | font-size: calc((28 /16) * 1rem);
82 | @media (--m) {
83 | font-size: calc((32 /16) * 1rem);
84 | }
85 | @media (--l) {
86 | font-size: calc((60 /16) * 1rem);
87 | }
88 | }
89 | h3, .h3 {
90 | line-height: 1.3;
91 | @media (--l) {
92 | line-height: 1.4;
93 | }
94 | }
95 | .s4,
96 | h4, .h4 {
97 | font-size: calc((26 /16) * 1rem);
98 | @media (--m) {
99 | font-size: calc((35 /16) * 1rem);
100 | }
101 | @media (--l) {
102 | font-size: calc((48 /16) * 1rem);
103 | }
104 | }
105 | h4, .h4 {
106 | line-height: 1.5;
107 | @media (--l) {
108 | line-height: 1.1;
109 | }
110 | }
111 | .s5,
112 | h5, .h5 {
113 | font-size: calc((15 /16) * 1rem);
114 | letter-spacing: 0.05rem;
115 | }
116 | h5, .h5 {
117 | line-height: 1.5;
118 | }
119 | .s6,
120 | h6, .h6 {
121 | font-size: calc((26 /16) * 1rem);
122 | }
123 | h6, .h6 {
124 | line-height: 1.2;
125 | }
126 | .s0,
127 | p, .p {
128 | font-size: calc((16/16) * 1rem);
129 | @media (--m) {
130 | font-size: calc((18/16) * 1rem);
131 | }
132 | }
133 | .s1,
134 | p .p1 {
135 | font-size: calc((14/16) * 1rem);
136 | letter-spacing: 0.05rem;
137 | }
138 | .s5,
139 | p .p5 {
140 | font-size: calc((12/16) * 1rem);
141 | letter-spacing: 0.1rem;
142 | }
143 | .s5 {
144 | line-height: 1.5;
145 | }
146 | .s16,
147 | .s16 p {
148 | font-size: calc((16/16) * 1rem);
149 | }
150 | p, .p {
151 | line-height: 1.7;
152 | }
153 | p {
154 | margin: 1em 0;
155 | }
156 |
157 | blockquote {
158 | text-align: center;
159 | color: var(--blue);
160 | padding: 10px;
161 | margin: 0;
162 | p {
163 | font-family: 'freight-display-pro', serif;
164 | font-size: 26px;
165 | line-height: 1.4;
166 | @media (--m) {
167 | font-size: 36px;
168 | line-height: 1.3;
169 | }
170 | }
171 | }
172 |
173 | .p2, .p2 p {
174 | font-size: calc((18 /16) * 1rem);
175 | @media (--m) {
176 | font-size: calc((22 /16) * 1rem);
177 | }
178 | }
179 |
180 | .p4 {
181 | font-size: calc((28 /16) * 1rem);
182 | @media (--m) {
183 | font-size: calc((35 /16) * 1rem);
184 | line-height: 1.3;
185 | }
186 | }
187 | small, .small {
188 | font-size: calc((12 /16) * 1rem);
189 | }
190 | .xsmall {
191 | font-size: calc((10 /16) * 1rem);
192 | }
193 | a {
194 | color: inherit;
195 | text-decoration: none;
196 | outline: none;
197 | &:hover {
198 | outline: none;
199 | }
200 | }
201 | hr {
202 | display: block;
203 | border: 0;
204 | margin: 0;
205 | height: 1px;
206 | width: 100%;
207 | background-color: currentColor;
208 | color: inherit;
209 | }
210 | strong, .b {
211 | font-weight: bold;
212 | }
213 | em, .i {
214 | font-style: italic;
215 | }
216 | .caps {
217 | text-transform: uppercase;
218 | }
219 | .normal {
220 | text-transform: none;
221 | }
222 | .nw {
223 | font-weight: normal;
224 | }
225 | .no-under {
226 | text-decoration: none;
227 | }
228 | .light {
229 | font-weight: 300;
230 | }
231 | .book,
232 | .medium {
233 | font-weight: 400;
234 | }
235 | .demi {
236 | font-weight: 500;
237 | }
238 | .bold {
239 | font-weight: 700;
240 | }
241 | .track {
242 | letter-spacing: 0.2em;
243 | }
244 | .track--l {
245 | letter-spacing: 0.3em;
246 | }
247 | .underline {
248 | position: relative;
249 | overflow: hidden;
250 | &:after {
251 | content: '';
252 | transition: all .25s;
253 | position: absolute;
254 | bottom: 0;
255 | left: 0;
256 | width: 100%;
257 | height: 1px;
258 | border-bottom: 1px solid var(--orange);
259 | }
260 | &--black {
261 | &:after {
262 | bottom: 4px;
263 | border-bottom-color: var(--black);
264 | }
265 | }
266 | &--white {
267 | &:after {
268 | bottom: -2px;
269 | border-bottom-color: white;
270 | border-bottom-width: 2px;
271 | }
272 | &:hover {
273 | &:after {
274 | transform: translateY(4px);
275 | }
276 | }
277 | }
278 | &:hover {
279 | &:after {
280 | transform: translateY(4px);
281 | }
282 | }
283 | }
284 | .link-hover {
285 | transition: all var(--fast) var(--ease);
286 | &:hover {
287 | color: var(--yellow);
288 | }
289 | }
290 |
--------------------------------------------------------------------------------
/src/styles/slater/lib/var.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --fast: 200ms;
3 | --slow: 400ms;
4 | --ease: ease-in-out;
5 | --cubic: cubic-bezier(.01,.57,.13,1);
6 | --shadow: 0 15px 35px -10px rgba(0,0,0,0.15);
7 | --video-shadow: 0 25px 50px -20px rgba(0,0,0,0.15);
8 | }
9 |
--------------------------------------------------------------------------------
/src/styles/slater/lib/visibilty.css:
--------------------------------------------------------------------------------
1 | .hide {
2 | display: none;
3 | }
4 | .hide--s {
5 | @media (--s) {
6 | display: none;
7 | }
8 | }
9 | .hide--m {
10 | @media (--m) {
11 | display: none;
12 | }
13 | }
14 | .hide--l {
15 | @media (--l) {
16 | display: none;
17 | }
18 | }
19 | .show--s {
20 | display: none;
21 | @media (--s) {
22 | display: block;
23 | }
24 | }
25 | .show--m {
26 | display: none;
27 | @media (--m) {
28 | display: block;
29 | }
30 | }
31 | .show--l {
32 | display: none;
33 | @media (--l) {
34 | display: block;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/styles/slater/theme.css:
--------------------------------------------------------------------------------
1 | /* Default Slater Theme */
2 | @import 'lib/reset';
3 | @import 'lib/var';
4 | @import 'lib/colors';
5 | @import 'lib/breakpoint';
6 | @import 'lib/align';
7 | @import 'lib/display';
8 | @import 'lib/containers';
9 | @import 'lib/flexbox';
10 | @import 'lib/lists';
11 | @import 'lib/visibilty';
12 | @import 'lib/spacing';
13 | @import 'lib/z-index';
14 | @import 'lib/positioning';
15 |
16 | @import 'components/cart-drawer';
17 | @import 'components/page-transition';
18 | @import 'components/buttons';
19 |
20 | body {
21 | background-color: pink;
22 | color: red;
23 | }
--------------------------------------------------------------------------------
/src/templates/404.liquid:
--------------------------------------------------------------------------------
1 | {{ 'general.404.title' | t }}
2 | {{ 'general.404.subtext_html' | t }}
3 |
--------------------------------------------------------------------------------
/src/templates/article.liquid:
--------------------------------------------------------------------------------
1 | {% comment %}
2 | When a comment is submitted, the browser is redirected to a page that includes
3 | the new comment id in its URL.
4 | #comments is a required ID and is used as an anchor link by Shopify.
5 | {% endcomment %}
6 |
7 | {%- assign number_of_comments = article.comments_count -%}
8 |
9 | {% comment %}
10 | If a comment was just submitted but requires moderation, we have an extra comment to count.
11 | {% endcomment %}
12 | {% if comment and comment.status != 'published' %}
13 | {% assign number_of_comments = article.comments_count | plus: 1 %}
14 | {% endif %}
15 |
16 |
17 |
18 | {% if article.image %}
19 | {{ article | img_url: '1024x1024' | img_tag: article.title }}
20 | {% endif %}
21 |
22 |
27 |
28 |
29 | {{ article.content }}
30 |
31 |
32 | {% if article.tags.size > 0 %}
33 |
34 | {% for tag in article.tags %}
35 |
36 | {{ tag }} {% unless forloop.last %}, {% endunless %}
37 |
38 | {% endfor %}
39 |
40 | {% endif %}
41 |
42 | {% if settings.social_sharing_blog %}
43 | {% include 'social-sharing', share_title: article.title, share_permalink: article.url, share_image: article.image %}
44 | {% endif %}
45 |
46 | {% if blog.comments_enabled? %}
47 | {{ 'blogs.comments.with_count' | t: count: number_of_comments }}
48 |
49 | {% paginate article.comments by 5 %}
50 |
51 |
94 |
95 | {% endpaginate %}
96 |
97 | {% form 'new_comment', article %}
98 | {{ 'blogs.comments.title' | t }}
99 |
100 | {{ form.errors | default_errors }}
101 |
102 |
103 | {{ 'blogs.comments.name' | t }}
104 |
105 |
112 |
113 |
114 | {{ 'blogs.comments.email' | t }}
115 |
116 |
125 |
126 |
127 | {{ 'blogs.comments.message' | t }}
128 |
129 |
136 |
137 | {% if blog.moderated? %}
138 | {{ 'blogs.comments.moderated' | t }}
139 | {% endif %}
140 |
141 |
142 | {% endform %}
143 |
144 | {% endif %}
145 |
146 |
147 |
--------------------------------------------------------------------------------
/src/templates/blog.liquid:
--------------------------------------------------------------------------------
1 | {% paginate blog.articles by 5 %}
2 |
3 | {%- assign blog_title = blog.title -%}
4 |
5 | {% if current_tags %}
6 | {% capture blog_title %}{{ blog.title | link_to: blog.url }} — {{ current_tags.first }}{% endcapture %}
7 | {% endif %}
8 |
9 | {{ blog_title }}
10 |
11 | {% if blog.all_tags.size > 0 %}
12 | {{ 'blogs.general.categories' | t }}
13 |
14 |
15 | {% for tag in blog.all_tags %}
16 |
17 | {% if current_tags contains tag %}
18 | {{ tag }}
19 | {% else %}
20 | {{ tag | link_to_tag: tag }}
21 | {% endif %}
22 |
23 | {% endfor %}
24 |
25 | {% endif %}
26 |
27 |
28 | {% comment %}
29 |
30 | Article Previews
31 | ====================
32 | {% endcomment %}
33 |
34 | {% for article in blog.articles %}
35 |
38 |
39 | {% capture date %}{{ article.published_at | time_tag: format: 'month_day_year' }}{% endcapture %}
40 |
41 |
42 | {{ 'blogs.article.author_on_date_html' | t: author: article.author, date: date }}
43 |
44 |
45 | {% if article.image %}
46 |
47 | {{ article | img_url: '1024x1024' | img_tag: article.title }}
48 |
49 | {% endif %}
50 |
51 |
52 | {% if article.excerpt.size > 0 %}
53 | {{ article.excerpt }}
54 | {% else %}
55 |
56 | {{ article.content | strip_html | truncatewords: 100 }}
57 |
58 | {% endif %}
59 |
60 |
61 | {% if blog.comments_enabled? or article.tags.size > 0 %}
62 |
80 | {% endif %}
81 |
82 |
83 | {{ 'blogs.article.read_more' | t }} →
84 |
85 |
86 | {% endfor %}
87 |
88 | {% if paginate.pages > 1 %}
89 | {% include 'pagination' %}
90 | {% endif %}
91 |
92 | {% endpaginate %}
93 |
--------------------------------------------------------------------------------
/src/templates/cart.liquid:
--------------------------------------------------------------------------------
1 |
2 | {% if cart.item_count > 0 %}
3 |
{{ 'cart.general.title' | t }}
4 |
5 |
104 | {% else %}
105 |
{{ 'cart.general.title' | t }}
106 |
107 | {% comment %}
108 | Cart empty state
109 | {% endcomment %}
110 |
111 |
{{ 'cart.general.empty' | t }}
112 |
{{ 'cart.general.continue_browsing_html' | t }}
113 |
114 |
115 | {% comment %}
116 | Cart no cookies state
117 | ---------------------
118 | Browser cookies are required to use the cart. If cookies aren't enabled in the
119 | browser a message is displayed prompting the user to enable them.
120 | {% endcomment %}
121 |
122 |
{{ 'cart.general.cookies_required' | t }}
123 |
124 | {% endif %}
125 |
126 |
--------------------------------------------------------------------------------
/src/templates/collection.liquid:
--------------------------------------------------------------------------------
1 | {% paginate collection.products by 12 %}
2 |
26 |
27 |
28 | {% for product in collection.products %}
29 |
30 |
31 |
32 |
33 |
34 |
35 | {{ product.title }}
36 |
37 |
38 | {% if product.compare_at_price > product.price %}
39 |
40 | {% if product.price_varies %}
41 | {%- assign sale_price = product.price | money -%}
42 | {{ 'products.product.on_sale_from_html' | t: price: sale_price }}
43 | {% else %}
44 | {{ 'products.product.on_sale' | t }}
45 | {{ product.price | money }}
46 | {% endif %}
47 |
48 | {% else %}
49 |
50 | {% if product.price_varies %}
51 | {%- assign price = product.price | money -%}
52 | {{ 'products.product.from_text_html' | t: price: price }}
53 | {% else %}
54 | {{ product.price | money }}
55 | {% endif %}
56 |
57 | {% endif %}
58 |
59 | {% if product.compare_at_price > product.price %}
60 | {{ 'products.product.regular_price' | t }}
61 | {{ product.compare_at_price | money }}
62 | {% endif %}
63 |
64 | {% unless product.available %}
65 | {{ 'products.product.sold_out' | t }}
66 | {% endunless %}
67 |
68 |
69 |
70 | {% else %}
71 | {% if collection.handle == 'all' and collection.all_vendors.size == 0 and collection.all_types.size == 0 %}
72 | {% for i in (1..8) %}
73 |
74 | {% capture current %}{% cycle 1, 2, 3, 4, 5, 6 %}{% endcapture %}
75 | {{ 'product-' | append: current | placeholder_svg_tag: 'placeholder-svg placeholder-svg--small' }}
76 |
77 |
78 |
79 | {{ 'homepage.onboarding.product_title' | t }}
80 |
81 |
82 |
83 | {{ 1999 | money }}
84 |
85 | {% endfor %}
86 | {% else %}
87 |
{{ 'collections.general.no_matches' | t }}
88 | {% endif %}
89 |
90 | {% endfor %}
91 |
92 |
93 | {% if paginate.pages > 1 %}
94 | {% include 'pagination' %}
95 | {% endif %}
96 | {% endpaginate %}
97 |
--------------------------------------------------------------------------------
/src/templates/customers/account.liquid:
--------------------------------------------------------------------------------
1 | {% include 'theme-provider' with theme: 'dark' %}
2 |
3 |
4 |
5 | {%
6 | include 'account-titles' with
7 | title: 'My Account'
8 | %}
9 |
10 |
11 |
12 |
Order History
13 |
14 |
15 |
16 | {% paginate customer.orders by 99 %}
17 |
18 | {% if customer.orders.size != 0 %}
19 |
20 |
46 |
47 | {% else %}
48 |
You don't have any orders yet!
49 | {% endif %}
50 |
51 | {% include 'pagination' %}
52 | {% endpaginate %}
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/templates/customers/activate_account.liquid:
--------------------------------------------------------------------------------
1 | {{ 'customer.activate_account.title' | t }}
2 | {{ 'customer.activate_account.subtext' | t }}
3 |
4 | {% form 'activate_customer_password' %}
5 | {{ form.errors | default_errors }}
6 |
7 |
8 |
9 | {{ 'customer.activate_account.password' | t }}
10 |
11 |
15 |
16 |
17 |
18 |
19 | {{ 'customer.activate_account.password_confirm' | t }}
20 |
21 |
25 |
26 |
27 |
28 |
29 | {% endform %}
30 |
--------------------------------------------------------------------------------
/src/templates/customers/addresses.liquid:
--------------------------------------------------------------------------------
1 | {% include 'theme-provider' with theme: 'dark' %}
2 |
3 | {% include 'account-titles' with title: 'Addresses' %}
4 |
5 | {% paginate customer.addresses by 100 %}
6 |
7 |
8 |
9 |
10 |
Create new address
11 |
12 | {%
13 | include 'account-address-form' with
14 | id: 'new',
15 | action: customer.new_address,
16 | title: 'Add New Address',
17 | cta: 'Add Address',
18 | formClassName: 'mb1 pb1'
19 | %}
20 |
21 | {% comment %}
22 | List all customer addresses with a unique edit form.
23 | Also add pagination in case they have a large number of addresses
24 | {% endcomment %}
25 | {% for address in customer.addresses %}
26 | {% if customer.default_address == address %}
27 | {% assign default_addy = true %}
28 | {% else %}
29 | {% assign default_addy = false %}
30 | {% endif %}
31 | {%
32 | include 'account-address' with
33 | address: address,
34 | default: default_addy,
35 | edit: true,
36 | className: 'mb1'
37 | %}
38 |
39 | {%
40 | include 'account-address-form' with
41 | id: address.id,
42 | action: address,
43 | title: 'Edit Address',
44 | cta: 'Update Address',
45 | formClassName: 'is-edit-form mb1'
46 | %}
47 | {% endfor %}
48 |
49 | {% if customer.addresses == empty %}
50 |
You have no saved address.
51 | {% endif %}
52 |
53 | {% include 'pagination' %}
54 |
55 |
56 |
57 |
58 |
89 |
90 | {% endpaginate %}
91 |
92 |
--------------------------------------------------------------------------------
/src/templates/customers/login.liquid:
--------------------------------------------------------------------------------
1 | {% include 'theme-provider' with theme: 'dark' %}
2 |
3 |
4 |
5 |
6 |
Forgot your password?
7 |
Enter your email and we will send you a password reset link.
8 |
9 | {% form 'recover_customer_password', class: 'mha' %}
10 |
11 | {% if form.posted_successfully? %}
12 |
Success! Check your email for a link to reset your password.
13 | {% else %}
14 | {%
15 | include 'form-input-text' with
16 | label: 'Email',
17 | placeholder: 'Email',
18 | name: 'email',
19 | type: 'email'
20 | %}
21 |
22 | {% if form.errors %}
23 | {% for field in form.errors %}
24 | {% if field == 'form' %}
25 |
{{ form.errors.messages[field] }}
26 | {% else %}
27 |
{{ form.errors.translated_fields[field] | capitalize }} {{ form.errors.messages[field] }}.
28 | {% endif %}
29 | {% endfor %}
30 | {% endif %}
31 |
32 |
33 | {%
34 | include 'button' with
35 | tag: 'button',
36 | type: 'submit',
37 | cta: 'Reset password',
38 | className: 'btn btn__blue'
39 | %}
40 |
41 | {% endif %}
42 |
43 | {% endform %}
44 |
45 |
Cancel
46 |
47 |
48 |
49 |
Sign In
50 |
51 | {% form 'customer_login', class: 'ma' %}
52 |
53 | {%
54 | include 'form-input-text' with
55 | label: 'Email',
56 | placeholder: 'Email',
57 | name: 'customer[email]',
58 | className: 'mb1',
59 | type: 'email'
60 | %}
61 |
62 | {%
63 | include 'form-input-text' with
64 | label: 'Password',
65 | placeholder: 'Password',
66 | name: 'customer[password]',
67 | type: 'password',
68 | className: 'mb1'
69 | %}
70 |
71 |
72 | {% if form.errors %}
73 | {% for field in form.errors %}
74 | {% if field == 'form' %}
75 |
{{ form.errors.messages[field] }}
76 | {% else %}
77 |
{{ form.errors.translated_fields[field] | capitalize }} {{ form.errors.messages[field] }}
78 | {% endif %}
79 | {% endfor %}
80 | {% endif %}
81 |
82 |
83 | {%
84 | include 'button' with
85 | tag: 'button',
86 | type: 'submit',
87 | cta: 'Sign in',
88 | className: 'btn btn__blue caps bold'
89 | %}
90 |
91 | {% endform %}
92 |
93 |
99 |
100 | {% if shop.checkout.guest_login %}
101 |
102 |
103 |
104 |
105 | {% form 'guest_login' %}
106 | {%
107 | include 'button' with
108 | tag: 'button',
109 | type: 'submit',
110 | cta: 'Continue as a guest',
111 | className: 'btn btn__blue',
112 | outline: true
113 | %}
114 | {% endform %}
115 | {% endif %}
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/templates/customers/order.liquid:
--------------------------------------------------------------------------------
1 | {% comment %}
2 | The data-label attributes on elements are mobile-friendly
3 | helpers used for responsive-table labels
4 | {% endcomment %}
5 |
6 | {{ 'customer.account.title' | t }}
7 |
8 | {{ 'customer.account.return' | t }}
9 |
10 | {{ 'customer.order.title' | t: name: order.name }}
11 |
12 | {{ 'customer.order.date' | t: date: order.created_at | date: "%B %d, %Y %I:%M%p" }}
13 |
14 | {% if order.cancelled %}
15 | {%- assign cancelled_at = order.cancelled_at | date: "%B %d, %Y %I:%M%p" -%}
16 | {{ 'customer.order.cancelled' | t: date: cancelled_at }}
17 | {{ 'customer.order.cancelled_reason' | t: reason: order.cancel_reason }}
18 | {% endif %}
19 |
20 |
21 |
22 |
23 | {{ 'customer.order.product' | t }}
24 | {{ 'customer.order.sku' | t }}
25 | {{ 'customer.order.price' | t }}
26 | {{ 'customer.order.quantity' | t }}
27 | {{ 'customer.order.total' | t }}
28 |
29 |
30 |
31 | {% for line_item in order.line_items %}
32 |
33 |
34 | {{ line_item.title | link_to: line_item.product.url }}
35 | {% if line_item.fulfillment %}
36 |
43 | {% endif %}
44 |
45 | {{ line_item.sku }}
46 | {{ line_item.price | money }}
47 | {{ line_item.quantity }}
48 | {{ line_item.quantity | times: line_item.price | money }}
49 |
50 | {% endfor %}
51 |
52 |
53 |
54 | {{ 'customer.order.subtotal' | t }}
55 | {{ order.subtotal_price | money }}
56 |
57 |
58 | {% for discount in order.discounts %}
59 |
60 | {{ discount.code }} {{ 'customer.order.discount' | t }}
61 | {{ discount.savings | money }}
62 |
63 | {% endfor %}
64 |
65 | {% for shipping_method in order.shipping_methods %}
66 |
67 | {{ 'customer.order.shipping' | t }} ({{ shipping_method.title }})
68 | {{ shipping_method.price | money }}
69 |
70 | {% endfor %}
71 |
72 | {% for tax_line in order.tax_lines %}
73 |
74 | {{ 'customer.order.tax' | t }} ({{ tax_line.title }} {{ tax_line.rate | times: 100 }}%)
75 | {{ tax_line.price | money }}
76 |
77 | {% endfor %}
78 |
79 |
80 | {{ 'customer.order.total' | t }}
81 | {{ order.total_price | money }} {{ order.currency }}
82 |
83 |
84 |
85 |
86 | {{ 'customer.order.billing_address' | t }}
87 |
88 | {{ 'customer.order.payment_status' | t }}: {{ order.financial_status_label }}
89 |
90 | {{ order.billing_address | format_address }}
91 |
92 | {{ 'customer.order.shipping_address' | t }}
93 |
94 | {{ 'customer.order.fulfillment_status' | t }}: {{ order.fulfillment_status_label }}
95 |
96 | {{ order.shipping_address | format_address }}
97 |
--------------------------------------------------------------------------------
/src/templates/customers/register.liquid:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ 'customer.register.title' | t }}
5 |
6 | {% form 'create_customer' %}
7 | {{ form.errors | default_errors }}
8 |
9 | {%
10 | include 'form-input-text' with
11 | placeholder: 'First Name',
12 | name: 'customer[first_name]',
13 | value: form.first_name,
14 | type: 'text',
15 | className: 'mb1'
16 | %}
17 | {%
18 | include 'form-input-text' with
19 | placeholder: 'Last Name',
20 | name: 'customer[last_name]',
21 | type: 'text',
22 | className: 'mb1'
23 | %}
24 | {%
25 | include 'form-input-text' with
26 | placeholder: 'Email',
27 | name: 'customer[email]',
28 | type: 'email',
29 | value: form.email,
30 | spellcheck: "off",
31 | autocapitalize: "off",
32 | className: 'mb1'
33 | %}
34 |
35 | {%
36 | include 'form-input-text' with
37 | placeholder: 'Password',
38 | name: 'customer[password]',
39 | type: 'password',
40 | className: 'mb1'
41 | %}
42 |
43 |
44 | Sign Up
45 |
46 |
52 | {% endform %}
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/templates/customers/reset_password.liquid:
--------------------------------------------------------------------------------
1 |
31 |
--------------------------------------------------------------------------------
/src/templates/gift_card.liquid:
--------------------------------------------------------------------------------
1 | {% comment %}
2 | QR code is rendered in `#QrCode`
3 |
4 | `gift_card.pass_url` is true if apple wallet is enabled for the shop
5 | {% endcomment %}
6 |
7 | {% layout 'gift_card' %}
8 |
9 |
10 |
15 | {{ shop.url | escape }}
16 |
17 |
18 |
19 |
20 | {{ 'gift_cards.issued.subtext' | t }}
21 | {% unless gift_card.enabled %}
22 | {{ 'gift_cards.issued.disabled' | t }}
23 | {% endunless %}
24 |
25 | {%- assign gift_card_expiry_date = gift_card.expires_on | date: "%d/%m/%y" -%}
26 |
27 | {% if gift_card.expired and gift_card.enabled %}
28 | {{ 'gift_cards.issued.expired' | t: expiry: gift_card_expiry_date }}
29 | {% endif %}
30 |
31 | {% if gift_card.expired != true and gift_card.expires_on and gift_card.enabled %}
32 | {{ 'gift_cards.issued.active' | t: expiry: gift_card_expiry_date }}
33 | {% endif %}
34 |
35 |
36 |
37 | {%- assign initial_value_size = formatted_initial_value | size -%}
38 | {{ formatted_initial_value }}
39 |
40 | {% if gift_card.balance != gift_card.initial_value %}
41 | {{ 'gift_cards.issued.remaining_html' | t: balance: gift_card.balance | money }}
42 | {% endif %}
43 |
44 | {%- assign code_size = gift_card.code | format_code | size -%}
45 | {{ gift_card.code | format_code }}
46 |
47 | {{ 'gift_cards.issued.redeem' | t }}
48 |
49 | {{ 'gift_cards.issued.shop_link' | t }}
50 |
51 |
52 |
53 | {% if gift_card.pass_url %}
54 |
55 |
56 |
57 | {% endif %}
58 |
59 |
60 | {{ 'gift_cards.issued.print' | t }}
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/templates/index.liquid:
--------------------------------------------------------------------------------
1 |
2 | {{ content_for_index }}
3 |
--------------------------------------------------------------------------------
/src/templates/list-collections.liquid:
--------------------------------------------------------------------------------
1 | {% comment %}
2 | This page represents the /collections and /products pages.
3 | {% endcomment %}
4 |
5 | {{ page_title }}
6 |
7 | {% for collection in collections %}
8 | {% unless collection.handle == 'frontpage' %}
9 |
10 | {% if collection.image != blank %}
11 | {{ collection | img_url: '480x480' | img_tag: collection.title }}
12 | {% elsif collection.products.first != blank %}
13 | {{ collection.products.first | img_url: '480x480' | img_tag: collection.title }}
14 | {% else %}
15 | {% capture current %}{% cycle 1, 2, 3, 4, 5, 6 %}{% endcapture %}
16 | {{ 'collection-' | append: current | placeholder_svg_tag: 'placeholder-svg placeholder-svg--small' }}
17 | {% endif %}
18 |
19 |
20 |
21 | {{ collection.title }}
22 |
23 | {% endunless %}
24 | {% endfor %}
25 |
--------------------------------------------------------------------------------
/src/templates/page.contact.liquid:
--------------------------------------------------------------------------------
1 | {{ page.title }}
2 |
3 |
4 | {{ page.content }}
5 |
6 |
7 | {% form 'contact' %}
8 |
9 | {% if form.posted_successfully? %}
10 |
11 | {{ 'contact.form.post_success' | t }}
12 |
13 | {% endif %}
14 |
15 | {{ form.errors | default_errors }}
16 |
17 | {{ 'contact.form.name' | t }}
18 |
23 |
24 | {{ 'contact.form.email' | t }}
25 |
33 |
34 | {{ 'contact.form.phone' | t }}
35 |
41 |
42 | {{ 'contact.form.message' | t }}
43 |
47 | {% if form.body %}
48 | {{ form.body }}
49 | {% endif %}
50 |
51 |
52 |
53 |
54 | {% endform %}
55 |
--------------------------------------------------------------------------------
/src/templates/page.liquid:
--------------------------------------------------------------------------------
1 | {{ page.title }}
2 |
3 |
4 | {{ page.content }}
5 |
6 |
--------------------------------------------------------------------------------
/src/templates/password.liquid:
--------------------------------------------------------------------------------
1 | {% comment %}
2 | The share buttons share the home page URL. The share text is grabbed from the store
3 | meta description.
4 | {% endcomment %}
5 |
6 | {% layout 'password' %}
7 |
8 | {{ 'general.password_page.opening_soon' | t }}
9 |
10 | {% unless shop.password_message == blank %}
11 |
12 | {{ shop.password_message }}
13 |
14 | {% endunless %}
15 |
16 | {% form 'customer' %}
17 |
18 | {{ form.errors | default_errors }}
19 |
20 | {% if form.posted_successfully? %}
21 |
24 | {% else %}
25 | {{ 'general.password_page.signup_form_heading' | t }}
26 |
27 |
28 | {{ 'general.password_page.signup_form_email_label' | t }}
29 |
30 |
37 |
38 | asdfdfdf
39 | {{ 'general.password_page.signup_form_submit' | t }}
40 |
41 | {% endif %}
42 | {% endform %}
43 |
44 | {% if settings.share_facebook or settings.share_twitter %}
45 | {{ 'general.password_page.spread_the_word' | t }}
46 | {% include 'social-sharing' %}
47 | {% endif %}
48 |
49 |
50 | {% include 'icon-lock' %}
51 | {{ 'general.password_page.password_link' | t }} →
52 |
53 |
--------------------------------------------------------------------------------
/src/templates/product.liquid:
--------------------------------------------------------------------------------
1 | {% section 'product' %}
2 |
--------------------------------------------------------------------------------
/src/templates/search.liquid:
--------------------------------------------------------------------------------
1 | {% paginate search.results by 10 %}
2 |
3 |
4 | {% if search.performed %}
5 | {% if search.results_count == 0 %}
6 | {{ 'general.search.no_results_html' | t: terms: search.terms }}
7 | {% else %}
8 | {{ 'general.search.results_for_html' | t: terms: search.terms }}
9 | {% endif %}
10 | {% else %}
11 | {{ 'general.search.title' | t }}
12 | {% endif %}
13 |
14 |
15 |
16 |
17 | {{ 'general.search.placeholder' | t }}
18 |
19 |
24 |
25 | {% include 'icon-search' %}
26 | {{ 'general.search.submit' | t }}
27 |
28 |
29 |
30 |
31 | {% if search.performed %}
32 |
33 | {% for item in search.results %}
34 |
35 | {% if item.featured_image %}
36 |
37 | {{ item.featured_image.src | img_url: '240x240' | img_tag: item.featured_image.alt }}
38 |
39 | {% endif %}
40 |
41 | {{ item.title | link_to: item.url }}
42 |
43 | {% if item.object_type == 'product' %}
44 |
45 | {% if item.compare_at_price > item.price %}
46 | {% if item.price_varies %}
47 | {% assign sale_price = item.price | money %}
48 | {{ 'products.product.on_sale_from_html' | t: price: sale_price }}
49 | {% else %}
50 | {{ 'products.product.on_sale' | t }}
51 | {{ item.price | money }}
52 | {% endif %}
53 | {{ 'products.product.regular_price' | t }}
54 | {{ item.compare_at_price | money }}
55 | {% else %}
56 | {% if item.price_varies %}
57 | {% assign price = item.price | money %}
58 | {{ 'products.product.from_text_html' | t: price: price }}
59 | {% else %}
60 | {{ item.price | money }}
61 | {% endif %}
62 | {% endif %}
63 | {% unless item.available %}
64 | {{ 'products.product.sold_out' | t }}
65 | {% endunless %}
66 |
67 | {% else %}
68 | {{ item.content | strip_html | truncatewords: 50 }}
69 | {% endif %}
70 |
71 |
72 | {% endfor %}
73 |
74 |
75 | {% if paginate.pages > 1 %}
76 | {% include 'pagination' %}
77 | {% endif %}
78 | {% endif %}
79 |
80 | {% endpaginate %}
81 |
--------------------------------------------------------------------------------
/theme.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | {}
3 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const path = require('path')
3 | const LodashModuleReplacementPlugin = require('lodash-webpack-plugin')
4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
5 | const p = process.env.NODE_ENV === 'production'
6 |
7 | module.exports = {
8 | target: 'web',
9 | devtool: 'source-map',
10 | entry: path.join(__dirname, 'src/scripts/index.js'),
11 | output: {
12 | path: path.join(__dirname, 'src/assets'),
13 | filename: 'index.js'
14 | },
15 | module: {
16 | rules: [
17 | {
18 | enforce: 'pre',
19 | test: /\.js?$/,
20 | loader: 'standard-loader',
21 | exclude: /node_modules/,
22 | options: {
23 | parser: 'babel-eslint'
24 | }
25 | },
26 | {
27 | test: /\.js$/,
28 | exclude: /node_modules/,
29 | include: path.join(__dirname, 'src/scripts'),
30 | loaders: ['babel-loader']
31 | }
32 | ]
33 | },
34 | resolve: {
35 | alias: {
36 | slater: path.join(__dirname, 'src/scripts/', 'slater'),
37 | micromanagerRoot: path.join(__dirname, 'src/scripts'),
38 | components: path.join(__dirname, 'src/scripts/', 'components'),
39 | pages: path.join(__dirname, 'src/scripts/', 'pages'),
40 | templates: path.join(__dirname, 'src/scripts/', 'templates'),
41 | lib: path.join(__dirname, 'src/scripts/', 'lib')
42 | }
43 | },
44 | plugins: p ? [
45 | new webpack.NoEmitOnErrorsPlugin(),
46 | new LodashModuleReplacementPlugin(),
47 | new UglifyJsPlugin(),
48 | new webpack.optimize.OccurrenceOrderPlugin()
49 | ] : []
50 | };
51 |
--------------------------------------------------------------------------------
54 | {% if blog.moderated? and comment.status != 'published' %} 55 | {{ 'blogs.comments.success_moderated' | t }} 56 | {% else %} 57 | {{ 'blogs.comments.success' | t }} 58 | {% endif %} 59 |
60 | {% endif %} 61 | 62 | {% if number_of_comments > 0 %} 63 |64 | {% comment %} 65 | Display comment from URL parameters if it is waiting moderation 66 | {% endcomment %} 67 | {% if comment and comment.status != 'published' %} 68 |-
69 |
70 | {{ comment.content }}
71 |
72 | {% capture date %}{{ comment.created_at | time_tag: format: 'month_day_year' }}{% endcapture %}
73 |
75 | {% endif %}
76 |
77 | {% for comment in article.comments %}
78 | -
79 |
80 | {{ comment.content }}
81 |
82 | {% capture date %}{{ comment.created_at | time_tag: format: 'month_day_year' }}{% endcapture %}
83 |
85 |
86 | {% endfor %}
87 |
88 | 89 | {% if paginate.pages > 1 %} 90 | {% include 'pagination' %} 91 | {% endif %} 92 | {% endif %} 93 |{{ 'blogs.article.comment_meta_html' | t: author: comment.author, date: date }}
74 |{{ 'blogs.article.comment_meta_html' | t: author: comment.author, date: date }}
84 |