I love digging into Shopify themes to see what solutions and optimisations I can find under the hood. The new Express theme by Shopify is moving things in a new direction due to the “one-page” approach it takes, so I knew there’d be some gems I could find.
I’ve not yet installed it on a store and taken a look at the code, but after clicking around a little, I opened up the inspector to see if I could find anything interesting.
The first thing I spotted was that one of the stylesheets comes with
onload=“this.media=‘all’;this.onload=null;” attributes. Scott Jehl of the Filament Group posted a write up on this technique here. I’d not come across it before today, but it seems like a simple solution to stop stylesheets blocking rendering.
I’m too lazy to beautify it and see exactly what is included, but within the page source is a small amount of “critical” CSS. By adding this critical CSS to the initial HTML download rather than putting it in the main stylesheet, the user can immediately see a page with some styles applied. The perfect scenario for this is where a user receives only the styles that affect elements immediately visible on the page when they enter. The idea being that the remaining styles can download in the background before the user begins scrolling. It doesn’t seem a perfect science as you won’t know in advance exactly which elements their screen fits, or how quickly they’re going to try to scroll, but they say first impressions count, and when it comes to improving perceived page load speeds, perhaps even a slightly inaccurate attempt is better than none?
Another thing I’ve noticed is that they’re making effective use of the new Sections API, which allows us to request markup for individual sections, to allow customers to browse products, add them to cart and manage their cart without having to leave the page. I’ve not been particularly imaginative about how we could take advantage of this API, having only one or two ideas myself; but they’ve taken some relatively simple use cases here that work really well.
There are 3 main areas in which I’ve noticed they’re using the Sections API:
- Product modals
- Collection pagination
- Cart drawer
When you click on a product from the homepage, it requests the “product” section which is also used to build product pages, and displays that markup inside the modal instead.
The Streamline Theme by Archetype Themes does something similar here. When you click on a product card, it also opens a modal for the product, that uses the same template as the product page. It also requests the
product-recommendations section, which is rendered at the bottom of their product modal. The rest of their product modal is presumably populated using a template and JSON data that’s fetched by their collection scripts. Streamlines implementation also utilises the Browser History API which allows the products to be shared via URL or found again at a later date via the browser history. Streamline also makes the modal stretch to the bounds of the window, so with the exception of a couple of close buttons, missing header and footer, the user still feels like they’ve navigated to a completely different page.
Collection pagination requests the
collection-template section. This time passing a
page parameter to render the next page of products. It’s so simple and practical that I’m not sure there’s anything else to say about it.
Each time you update the cart, Express requests markup for two sections:
cart-discounts. In the demo store I’m viewing
cart-discounts is empty, so I can’t really say much about that, but requesting
cart-items and then redrawing the whole cart when it arrives seems a really simple way of handling cart updates.
What else could we do?
I’d already been toying with a couple of different ideas related to these features for a few weeks now and I’ll probably be looking at them closer soon. The first of which is for using Webpack to add critical compiled CSS into a theme during the build step. One of the reasons I’ve stayed away from the idea of critical CSS since learning about it is the thought of being unable to use Scss. My idea for this is that you’d simply use a snippet, let’s call it
critical-css.liquid for now, as an entry point in Webpack and by simply writing Scss, PostCSS, Less or whatever you want to use you could create a small bundle just for the critical CSS. When I’d initially thought of this I expected I’d need to write some kind of custom loader for Webpack, but as I write this I realise it probably doesn’t need one at all.
The next step up for that feature I’d imagine would be to have a build step that launches the site using something like puppeteer, takes a snapshot of the visible portion of the page, then parses the DOM for those elements to determine which CSS is critical, extracts it from the main build and adds it to the critical CSS snippet. This would likely add a lot of complexity so I doubt I’ll get around to investigating this soon, but that might be the ultimate goal for such a feature.
The big idea I’ve had regarding the Sections API has been inspired by modern frameworks such as Next.js, GatsbyJS and Nuxt.js.
Using a JS Router, we can allow customers to traverse a whole site without having to actually load a new page. The gist of this idea is that if a customer were to go to
collections/shirts, for example, we could download the component(s) used to create the product page and the product data separately. By keeping the components detached from the data, when a customer visits another product page, say
purple-shirt, they don’t have to download the component again, only the product data. I began to work on this idea ~3 weeks ago. The homepage is the main challenge here. In order to download data for homepage sections separate to the markup, you need to create an
index.json.liquid template, then add a condition to print the setting for each section as JSON if the rendered template is the
json variant and HTML if not, so that customers can land on a server rendered page before the JS takes over.
To make things easier for myself I settled on using Vue components and Vue Router. Turns out Vue Router is already capable of creating separate bundles for dynamically imported components (and lazy-loads them too, which is a free optimisation out of the box 👌) . The only reason I never finished the experiment is because I got bored of converting Debut theme components into Vue components. I’ve got a fresh load of inspiration to see this through now though so that a speed comparison can be done between stores.
I’m sure there’s plenty more for me to discover, and I will no doubt be adding the Express theme to a development store soon so I can dig deeper into the code, but this has certainly left me thinking about which other directions we can push theme features in.