Awesome Open Source
Awesome Open Source

Shopify Theme Lab

Shopify Theme Lab is a customizable modular development environment for blazing-fast Shopify theme creation. By default, it's bundled with Vue.js and Tailwind CSS, but you can swap them for pretty much anything. Build a custom Shopify theme from scratch with a modern stack!

Disclaimer: This project is not affiliated with Shopify Inc., Tailwind Labs Inc. or vuejs

TL;DR Go to Installing, then go to Getting started. Now you're ready to start πŸ”₯

Table of contents

Ecosystem

Project Status Description
Shopify Theme Lab Modular development environment for blazing-fast Shopify theming
Shopify Theme Lab CLI Command Line Interface for Shopify Theme Lab
Shopify Theme Lab Plugins Official Shopify Theme Lab plugins
Shopify Settings Control Automatic Git version control for Shopify settings_data.json
Shopify Foundation Theme A modern Shopify starter theme built with Vue and Tailwind CSS

Features

System requirements

  • Node.js >= 14.0.0
  • npm or yarn

Installing

  1. Clone this repo or simply run the following command in your terminal:
$ npx themelab create <directory-name>
  1. Run the following command(s) with your preferred package manager:

npm

$ npm install

yarn

$ yarn import # migrate package-lock.json to yarn.lock
$ rm package-lock.json # or delete manually
$ yarn install --force

If you migrated to yarn, you can replace npm run with yarn when executing upcoming commands.

Getting started

  1. Get Shopify API access: Instructions at Theme Kit Docs

  2. Initialize theme on Shopify store with credentials from the first step. Either for dev or live environment:

npm requires the extra -- before any arguments! When using yarn you can omit them.

$ npm run shopify:init -- --password [your-api-password] --store [your-store.myshopify.com] --env [dev or live] --name [theme-name]
  1. Publish the new theme through the Shopify panel: your-store.myshopify.com/admin/themes

  2. Start developing:

$ npm run start
$ npm run open:dev # open/preview theme in default browser

Deploying

first, make sure the configuration for the live environment is initialized.

$ npm run build # bundle css and js
$ npm run deploy:live # deploy shopify/ directory

There is a safety mechanism in place, which won't allow you to deploy to an already published theme on the live store. If you want to deploy regardless use the --allow-live flag.

$ npm run deploy:live -- --allow-live

By default, the deploy task overrides all files on the remote store, if any changes were made through the Shopify theme editor you might want to download the settings_data.json file before deploying:

$ npm run settings:live

Teams

The shopify:init task always creates a new theme with a unique ID for the provided store. Sometimes it can be useful to connect to an existing initialized theme (e.g. when multiple people deploy to the same live environment).

  1. Run the following command to list all themes from the provided store and write down the ID for the theme in question:
$ npm run shopify:themes -- --password [your-api-password] --store [your-store.myshopify.com]
  1. Copy and rename the Shopify sample config file:
$ cp .config/shopify/shopify.sample.yml .config/shopify/shopify.live.yml # or copy and rename manually
  1. Adjust the contents of the newly created shopify.live.yml file.

CI/CD

GitHub actions

  1. Add the following four secrets to your Shopify Theme Lab repo in settings β†’ secrets:
SHOPIFY_API_PASSWORD # your-api-password
SHOPIFY_STORE_URL # your-store.myshopify.com
SHOPIFY_ENV # dev or live
SHOPIFY_THEME_ID # theme-id (without quotation marks) - find the id either in shopify.[env].config.yml or with shopify:themes task
  1. Copy and paste into a GitHub action (adjust contents if necessary):
# Shopify Theme Lab CI/CD integration for GitHub actions
name: Shopify Theme Lab CI/CD

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
  workflow_dispatch: # allows to manually run from GitHub actions panel

jobs:
  build-and-deploy:
    name: Build and Deploy
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 14.x ]

    steps:
      - name: Checkout master branch
        uses: actions/[email protected]

      - name: Use Node.js
        uses: actions/[email protected]
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install dependencies and build dist files
        run: |
          npm install
          npm run build

        # first, make sure the theme is already initialized on the Shopify store in question
        #
        # Below "run" commands explained:
        # 1. creates a Shopify credential config
        # 2. downloads settings_data.json from the remote store
        # 3. deploys the shopify/ directory to the remote store
      - name: Deploy to Shopify store
        run: |
          npx themelab shopify:init -p ${{ secrets.SHOPIFY_API_PASSWORD }} -s ${{ secrets.SHOPIFY_STORE_URL }} -e ${{ secrets.SHOPIFY_ENV }} -i ${{ secrets.SHOPIFY_THEME_ID }}
          npm run settings:${{ secrets.SHOPIFY_ENV }}
          npm run deploy:${{ secrets.SHOPIFY_ENV }} -- --allow-live

CSS preprocessors

For the most cohesive development experience, it's recommended that you use PostCSS exclusively when working with Tailwind CSS.

By default, only PostCSS with postcss-preset-env is installed. postcss-preset-env lets you use tomorrow’s CSS today. If you want to use a preprocessor it's recommended to use SASS/SCSS since it's the most compatible with a variety of CSS frameworks.

SASS/SCSS

  1. Run the following command:

npm

$ npm install sass sass-loader --save-dev

yarn

$ yarn add sass sass-loader --dev
  1. Rename src/css/main.css to src/css/main.scss

  2. Change import './css/main.css' to import './css/main.scss' in src/main.js

LESS

  1. Run the following command:

npm

$ npm install less less-loader --save-dev

yarn

$ yarn add less less-loader --dev
  1. Rename src/css/main.css to src/css/main.less

  2. Change import './css/main.css' to import './css/main.less' in src/main.js

Stylus

  1. Run the following command:

npm

$ npm install stylus stylus-loader --save-dev

yarn

$ yarn add stylus stylus-loader --dev
  1. Rename src/css/main.css to src/css/main.styl

  2. Change import './css/main.css' to import './css/main.styl' in src/main.js

Swapping CSS framework

Removing Tailwind CSS

  1. Remove package:

npm

$ npm uninstall tailwindcss

yarn

$ yarn remove tailwindcss
  1. Remove tailwind config:
$ rm src/tailwind.config.js # or delete manually
  1. Inside postcss.config.js remove require('tailwindcss')(path.resolve(__dirname, '../src/tailwind.config.js')).

  2. Remove all @import "tailwindcss/.."; imports from main.css

Bulma

  1. Install SASS/SCSS and update files accordingly

  2. Install package:

npm

$ npm install bulma

yarn

$ yarn add bulma
  1. import bulma in main.scss with @import "~bulma/bulma";

Swapping JavaScript framework

Removing Vue

  1. Remove packages:

npm

$ npm uninstall vue vuex vue-loader @vue/compiler-sfc

yarn

$ yarn remove vue vuex vue-loader @vue/compiler-sfc
  1. Remove vue directory:
$ rm -r src/vue # or delete manually
  1. Remove everything from main.js except import './css/main.css'

  2. Inside .eslintrc.js:

{
  ...
  extends: [
    ...
    'plugin:vue/vue3-recommended' // remove 'plugin:vue/vue3-recommended'
    ...
  ],
  plugins: [
    'vue' // remove 'vue'
  ]
  ...
}
  1. Inside webpack.common.js:
...
const webpack = require('webpack') // remove explicit webpack require
const { VueLoaderPlugin } = require('vue-loader') // remove VueLoaderPlugin require
...
module: {
  rules: [
    ...
    // remove vue-loader
    {
      test: /\.vue$/,
      loader: 'vue-loader'
    }
    ...
  ]
}
{
  plugins: [
    ...
    // remove VueLoaderPlugin and webpack.DefinePlugin
    new VueLoaderPlugin(),
    new webpack.DefinePlugin({
      __VUE_OPTIONS_API__: 'true',
      __VUE_PROD_DEVTOOLS__: 'false'
    })
    ...
  ]
}

Alpine.js

If you want to use something lighter than Vue, a good alternative is Alpine.js.

  1. Install package:

npm

$ npm install alpinejs

yarn

$ yarn add alpinejs
  1. Import alpinejs in main.js:
import 'alpinejs'

Project structure

shopify-theme-lab/             πŸ“ root of your Shopify Theme Lab project
β”œβ”€β”€ .config/                   πŸ“ development environment files and configs
β”‚   β”œβ”€β”€ shopify/               πŸ“ Shopify credential-configs
β”‚   β”‚   β”œβ”€β”€ .shopifyignore     πŸ“„ files and directories that won't be uploaded to Shopify
β”‚   β”‚   └── ...
β”‚   β”œβ”€β”€ webpack/               πŸ“ webpack configs
β”‚   β”‚   β”œβ”€β”€ webpack.common.js  πŸ“„ webpack shared config used in development and production
β”‚   β”‚   β”œβ”€β”€ webpack.dev.js     πŸ“„ webpack development config
β”‚   β”‚   └── webpack.prod.js    πŸ“„ webpack production config
β”‚   β”œβ”€β”€ .browserslistrc        πŸ“„ Browserslist config
β”‚   β”œβ”€β”€ .eslintrc.js           πŸ“„ ESLint config
β”‚   β”œβ”€β”€ .stylelintrc.js        πŸ“„ stylelint config
β”‚   └── postcss.config.js      πŸ“„ PostCSS config
β”œβ”€β”€ .github/                   πŸ“ files related to GitHub and images for READMEs
β”œβ”€β”€ shopify/                   πŸ“ default Shopify theme structure
β”‚   β”œβ”€β”€ assets/                πŸ“ files outputted by webpack will be placed here
β”‚   └── ...
β”œβ”€β”€ src/                       πŸ“ source files processed by webpack
β”‚   β”œβ”€β”€ css/                   πŸ“ css directory
β”‚   β”‚   └── main.css           πŸ“„ main stylesheet
β”‚   β”œβ”€β”€ vue/                   πŸ“ Vue, Vuex files and directories
β”‚   β”‚   └── ...
β”‚   β”œβ”€β”€ main.js                πŸ“„ webpack's main entry point
β”‚   └── tailwind.config.js     πŸ“„ Tailwind CSS config
β”œβ”€β”€ .gitignore                 πŸ“„ files and directories ignored by git
β”œβ”€β”€ package.json               πŸ“„ dependencies and tasks
└── ...

Tasks

Task Description
start run dev, reloader and shopify:watch tasks simultaneously in parallel
dev bundle and watch for changes in src/ files with webpack
build create minified production files for Shopify in shopify/assets/ directory
reloader run an HTTP server and WebSocket server for remote auto-reloading
lint run lint:js and lint:css tasks in sequence
lint:js lint .js and .vue files inside the src/ directory
lint:css lint the <style></style> section of .vue files, .css, .sass and .scss files inside the src/ directory
shopify:watch watch for changes in the shopify/ directory and upload to the dev store
shopify:init initialize a theme on remote Shopify store and create a Shopify config file for the specified environment (Run in the root directory of your project)
shopify:themes list all themes with IDs from the provided store. Takes two arguments --password and --store
deploy:dev upload the shopify/ directory to the dev store
deploy:live upload the shopify/ directory to the live store
settings:dev download settings_data.json from the dev store
settings:live download settings_data.json from the live store
open:dev open/preview theme on the dev store
open:live open/preview theme on the live store

Development environment concepts

CLI

Under the hood Shopify Theme Lab uses the Shopify Theme Lab CLI for some tasks. You can also use the CLI independantly from included tasks.

Configs

Inside .configs/ are multiple pre-configured config files. You should be able to work from start to finish, without ever going into this directory. But if you feel the need to adjust some configs to your liking, go for it!

Shopify & environment initialization

By running shopify:init and entering credentials, the task initializes a new theme from shopify/ directory to the provided Shopify store. It also saves a configuration file for the specified environment inside .config/shopify/ directory. This file will be ignored by git and shouldn't be tracked for security reasons. All tasks regarding Shopify will use the credentials from the saved configuration file.

Shopify + webpack

  • All webpack configs are inside .config/webpack/ directory
  • main.js is webpack's main entry point
  • All Vue related files are auto-loaded by webpack with require.context - Vue components, Vuex modules, as well as mixins and directives with global in their filename. Everything is defined in src/main.js
  • Vue components can be either used as regular single-file-components or as renderless components without <template></template> tags (You can use Liquid templating while hooking in Vue functionality).
  • The webpack bundle and all other assets are outputted to shopify/assets/ directory. This directory is cleaned on every build.

Shopify remote auto-reloading

While npm run start task is running: The shopify/ directory is being watched for changes and all changed files are uploaded to the Shopify remote server. After the upload is finished, a request is sent to a localhost:port address (specified in package.json) and the shopify-theme-lab-reloader package reloads all connected Shopify store sites. Open the web console to check if a site is connected.

Static files

Place your static files inside the shopify/assets/ directory and add the static keyword to their filename e.g. myfile.static.png. All files inside this directory are ignored by git except for files with static in their filename. Since this directory is also shared with files generated by webpack, it's cleaned on every consecutive build except for static files.

Local fonts

  1. add your fonts to the shopify/assets/ directory with the static keyword e.g.:
shopify-theme-lab/
β”œβ”€β”€ shopify/
β”‚   β”œβ”€β”€ assets/
β”‚   β”‚   β”œβ”€β”€ open-sans-regular.static.woff
β”‚   β”‚   └── open-sans-regular.static.woff2
β”‚   └── ...
└── ...
  1. create a Shopify snippet shopify/snippets/fonts.liquid with the following content:
{% style %}
  @font-face {
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 400;
    src: url('{{ 'open-sans-regular.static.woff2' | asset_url }}') format('woff2'),
         url('{{ 'open-sans-regular.static.woff' | asset_url }}') format('woff');
  }
{% endstyle %}
  1. render the fonts snippet below bundle.css inside shopify/layout/theme.liquid:
...
{{ 'bundle.css' | asset_url | stylesheet_tag }}
{% render 'fonts' %}
...
  1. Create a css file and import it into src/css/main.css with the following content:
body {
  font-family: 'Open Sans', sans-serif;
}

Common pitfalls

  • <style></style> and <script></script> will be removed on mount inside Vue components (basically everything inside <div id="app">...</div>), use <component is="style"><componet> and <component is="script"></componet> instead
  • If you want to pass an entire Shopify Drop (Object) as a prop, you have to first convert the Drop to json and replace all double quotes with single quotes: <component :shopify-data="{{ product | json | replace: '"', "'" }}"></component>. Not all Drops can be converted to json, if you get an {"error":"json not allowed for this object"} it's a Shopify limitation and you have to pass the values in question individually

Limitations

  • When the development task is running, the browser console throws a bundle.css missing error
  • Already running Shopify tasks only upload files which are changed, a simple re-save of a file, without editing it, won't upload the file to the remote store
  • Vue components inside .liquid files can only be used in a non-self-closing <kebab-case></kebap-case> manner

Contributing

Everyone is welcome to make Shopify theme development better! Please read the Contributing guide before creating issues or submitting pull requests.


Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
javascript (70,368)Β 
vue (4,286)Β 
webpack (1,157)Β 
scss (718)Β 
tailwindcss (231)Β 
postcss (171)Β 
tailwind (89)Β 
liquid (64)Β 
shopify (59)Β