[TECHNICAL] Theme architecture and best practices

This technical documentation only applies to our theme Impact and Prestige v7 (and higher). While we plan to further extend our other themes to share the same technical foundation, if you are using a different theme, please refer to the file "custom.js" inside the Assets folder to access the technical documentation for your theme.

Editing the Liquid code or any files in the assets folder of your theme will remove you from the automatic upgrade path.

Our themes use modern JavaScript and CSS code to have the best performance possible. The theme shares a common Shopify theme structure:

CSS

Technical considerations

The theme ships a single theme.css file. We have decided not to split our CSS into multiple files for several reasons:

  • Our CSS is optimized out of the box, so the performance gain of splitting is minimal but makes maintenance and code changes much harder.
  • There is no concept of "CSS modules" (as of JavaScript module) which means that by splitting the CSS into small files if a section includes the same file several times, the CSS is parsed as many times as it is included, which can cause poorer performance.
  • Splitting files does not allow making efficient use of GZip compression.

CSS variables

Our theme exposes a lot of CSS variables (such as color and spacing...) that merchants in the theme editor can configure. All those CSS variables can be found in the snippets/css-variables.liquid file.

Across the sections you will also find a common pattern across the sections where CSS variables are defined at the top. This allows us to limit the amount of CSS code in the main file by setting the dynamic variables (that depend on the section's settings) in the section itself.

Framework

We do not use any CSS framework. All our code is handcrafted to meet the need of a specific theme. Modern browsers come with native support for grid, flexbox... and we encourage you to use those new technologies for the best performance.

We frequently get asked why we are not using Tailwind (a highly popular utility framework). We do not use such frameworks mainly for support: introducing an external building tool makes it harder for our support team to locate issues quickly. However, attentive readers may see that we use some utility classes inside our main CSS. Feel free to use them, or even add Tailwind on top of the theme if you would like to use such libraries.

JavaScript

Our themes use web components across the board. Web components allow to easily re-use behavior and features without needing a complex setup or initialization code.

In our themes, the JavaScript is split across different files.

On Impact:

  • assets/vendor.min.js : this file contains all our third-party dependencies. You can learn more about the dependencies we ship and how to use them by clicking here.
  • assets/theme.js.liquid : this file contains the main web components used in the theme that are potentially included in more than one page/section. Those components are fundamental building blocks that will be available in all our themes in the future.
  • assets/sections.js.liquid : some sections contains their own JavaScript. Those are shipped inside the "sections.js. liquid" file. They are dissociated from the "theme.js.liquid" to allow developers to more easily extend/alter the behavior of a specific section, without having to go through a very long JS file.

On Prestige v7 and higher:

  • assets/vendor.min.js : this file contains all our third-party dependencies. You can learn more about the dependencies we ship and how to use them by clicking here.
  • assets/theme.js : this file contains the generic web component as well as section JS. This is the approach we are going to use now for new themes. Compared to Impact, we have removed the Liquid extension from the JS, and we have merged all the theme-specific JS code into a single file. This allows to slightly improve cache compared to Impact aproach.

Some people may ask why we are not splitting more our JS code. Effectively, all our JavaScript is shipped on every pages, independently of which sections are used or not. There are several reasons to that:

  • During our tests, we have found that many, small JS files may lead to bigger file size (due to the fact that GZip compression is less effective).
  • Our JS file is already pretty small, so the performance benefit of splitting is not obvious. JS performance is also not only due to file size, but also (and mainly) due to what the JavaScript code is doing. 100 lines JS-file that trigger a lot of layout thrash and recalculate will have a significantly bigger impact on performance than a 2000 lines optimized JS. During all our development process, we are benchmarking each of our component individually to make sure they are as performant as possible.
  • Due to the lack of sub-folders support in the "Assets" part of a theme, splitting the code too much lead to harder code changes.

For all those reasons, we decided to keep a standard bundling approach, with the extra usage of ES modules, allowing a way easier extension.

Best practices to extend the theme

Often, developers and agencies may wish to extend the theme. We highly discourage you from editing the vendor.min. js , theme.js or theme.css files directly.

Indeed, this will make upgrades much harder, and for those files being compiled, may not be convenient to edit directly.

Instead, we export all the theme components as modules, which allows you to easily extend them to create your behavior. For instance, let's say you would like to re-use the theme drawer infrastructure but add behavior. You can do that easily by importing the default drawer and add your behavior:

<script type="module">
  import {Drawer} from "{{ 'theme.js' | asset_url }}"; // Import the theme web component

  // Extend it
  class CustomDrawer extends Drawer {

  }

  // Create the custom elements
  window.customElements.define('custom-drawer', CustomDrawer);
</script>

This pattern will allow you not only to not touch the original code and make upgrades easier but also to create custom components based on theme infrastructure and re-use them across your projects (even if they are using different themes from ours).