As some of you may know from my previous articles Shopify is a theme-based hosted commerce platform which enables you to build online stores. It’s been steadily growing over the last few years and today is the platform behind 400,000+ businesses in approximately 175 countries.
The Shopify platform is constantly evolving and over the last few months, a number of key features have been launched, specifically aimed at theme and app developers. In this article, we’ll be looking at a selection of these new features and toolsets including:
- Updates to theme image management
- Sections
- Blocks
- ThemeKit and Slate
- The Polaris design system
Many of the following examples will discuss Liquid. Liquid is the template language enabling theme developers to use placeholders and logic constructs in their templates that will be replaced with live store data when a page is requested and rendered. It uses a simple syntax which allows output (e.g. a product title) and logic (e.g. a for
loop). I won’t go into too many details here but you can watch my 25 minute screencast that goes into a lot more detail on how Liquid works.
And whilst you’re here, you may also be interested in the following inspiration post, for themes available on Themeforest:
-
20 Best Shopify Themes With Beautiful eCommerce Designs
1. New Theme Image Management
Let’s begin by looking at one of the major theme related changes that happened recently: the img_url
Liquid filter. Until recently images were rendered based on a set of predefined named sizes. For example:
{{ product.featured_image | img_url: 'grande' }}
Here we are requesting the featured image for the product in size grande
. This would, assuming the original image had one side equal or greater than 600px, render the image with the longer of the two axes being 600px.
These named sizes are now deprecated and whilst they will still work they have been replaced by a new set of parameters including size
, crop
, scale
, and format
. This now makes it possible to do a whole host of image related manipulations previously unavailable to Shopify theme developers.
I’ll be using the img_url
Liquid filter in these examples but the techniques will also work with the following other image related objects:
- product_img_url
- collection_img_url
- article_img_url
- asset_img_url
Let’s begin by looking at how we can resize an image. In order to do this, we replace the now deprecated image name
with a specific size in pixels. Here’s an example:
{{ product.featured_image | img_url: '450x450' }}
Using the above syntax now puts the control of the image dimensions in your hands: here we have specified both the width and height (in that order).
You can also specify only a width:
{{ product.featured_image | img_url: '450x' }}
or only a height:
{{ product.featured_image | img_url: 'x450'}}
When specifying a single value, Shopify will calculate the remaining dimension based on the original image size, keeping the original image’s aspect ratio intact.
Going back to our original example, you might think that it would result in a 450×450 version of your image being rendered. This, however, isn’t always the case.
This request would result in a perfect square, only if both of the following conditions are met:
- The original image was 450px or greater on both axes
- Both sides are of the same length
If both conditions are true then a 450×450 square image would be rendered. If not, Shopify will resize it using the same logic as if you’ve specified only height or width. The longest side wins out in this situation and is scaled accordingly.
In order to create square images, you can make use of the crop parameter to ensure that the resulting image’s dimensions match the requested dimensions. If the entire image won’t fit in your requested dimensions, the crop parameter specifies which part of the image to show. There are three valid options:
- top
- center
- bottom
- left
- right
This functionality has been available since late 2016 but in early 2017 an update was released that added even more flexibility. Themes available in the Shopify Theme Store, including the premium Empire theme pictured below, all make use of these techniques. Installing a free theme is a great way to learn more about how to implement these ideas.
{% for product in collection.products %} {% assign image = product.featured_image %} {% endfor %}
In this example, we are using the assign
Liquid function to create a variable called image
that is equal to the currently viewed products featured image (which is set in the Shopify admin). We are then able to use Liquid logic to create our srcset
declarations using, in this case, the width
property:
{% if image.width > 640 %}{{ image.src | img_url: '640x' }} 640w{% endif %}
The good news is that theme developers don’t need to worry about re-uploading all of their images as Shopify has indexed every image on the platform.
Two other parameters worth discussing are scale
and format
. The scale parameter enables you to specify the pixel density of the image. You can scale up either 2x or 3x depending on your needs:
{{ product.image | img_url: '400x400', scale: 2 }}
The format
parameter lets you specify what file format to use for the image. Currently, you can specify either jpg
or pjpg
(progressive JPEG):
{{ product.image | img_url: '400x400', format: 'pjpg' }}
You can also take advantage of this technique for images residing in your theme’s assets folder. In order to do this, you use the asset_img_url
filter. Here’s an example that also makes use of the img_tag
filter which will result in a fully formed img
element being rendered in the template:
{{ logo.jpg' | asset_img_url: '300x' | img_tag }}
These new image manipulation filters and image properties finally make it possible to be as flexible as you need to be when it comes to dealing with images, art direction, and being friendly to the end users data plans.
2. Sections
In late 2016 Shopify introduced “Sections”. This new feature allows theme developers to create a custom admin interface which allows store owners to easily add, reorder, and remove content sections such as products, slideshows, videos, or product collections. These are common use cases but you can literally use this functionality to allow store owners to add and edit any type of content. All changes in the admin can be viewed in real-time and once saved will be live in the store.
Sections can be included statically in a theme’s templates (like the header and footer), or they can be dynamically added and removed, via the admin interface, on a theme’s homepage. In the above example, which we’ll go into shortly, you can see how we are able to edit a static section which will appear in the footer of the store. You’ll also notice the “Add Section” button which enables us to add dynamic sections, more on those later, to the homepage.
Section templates reside in the new sections folder and can be referenced in a similar way to snippets. If our file was located at sections/promotion.liquid we would reference it as follows:
{% section “promotion” %}
Note: you don’t need the .liquid
extension as is common with snippets in Shopify themes. Let’s have a look at an example to help clarify the power of sections. The following is the contents of sections/footer.liquid
:
{{ section.settings.title }}
{{ section.settings.description }}
{% schema %} { "name": "Footer Promotion", "settings": [ { "id": "title", "type": "text", "label": "Promotion Title", "default": "Title" }, { "id": "description", "type": "richtext", "label": "Add your description below", "default": "Add your description here
" } ] } {% endschema %} {% stylesheet %} {% endstylesheet %} {% javascript %} {% endjavascript %}
If you are familiar with Shopify theme settings some of this may look quite familiar. It comprises a mixture of HTML, Liquid placeholders and JSON similar to that found in settings_schema.json
. Incidentally, the functionality of settings_schema.json
still remains: sections just add an extra layer of functionality.
At the top of the template is the HTML output that I would like to be generated when the template encounters the section at render time. Inside each of the h1
and p
elements are Liquid placeholders using the new {{ section.settings.[x] }}
output syntax. In our examples, our section template is going to look for data corresponding to {{ section.settings.title }}
and {{ section.settings.description }}
.
So far nice and easy but how does Shopify know what to populate these placeholders with? This all comes down to the JSON I mentioned earlier nestled between the opening and closing {% schema %}
tags.
{ "name": "Footer Promotion", "settings": [ { "id": "title", "type": "text", "label": "Promotion Title", "default": "Title" }, { "id": "description", "type": "richtext", "label": "Add your description below", "default": "Add your description here
" } ] }
In order for our section to appear in the “Customise Theme” area of the store’s admin we need to give it an identifier: we do this by defining the “name” value at the top level of the JSON.
Next, we have the settings node which has, in this example, two sub-nodes. Both contain properties of id
, type
, label
, and default
. Each of these, depending on their value, will govern how the admin interface is rendered. Let’s have a look at each in turn:
id
A text string which will be used internally. It’s worth noting that whilst the IDs must be unique across a section file they do not have to be unique across all section files. As such it’s perfectly OK to have an id
of title
in multiple section files. Section settings also won’t conflict with settings in settings_schema.json
.
type
This represents the control that will be rendered within the admin. The most commonly used options are as follows:
-
text
: Single-line text fields -
textarea
: Multi-line text areas -
richtext
: A rich text editor -
image_picker
: Image uploads -
radio
: Radio buttons -
select
: Selection drop-downs -
checkbox
: Checkboxes
Some of these require additional JSON to work. For example, the select control requires options to populate it. More information on how these work, as well as other controls that you might want to consider, are available in the Shopify docs.
label
This represents the HTML label that will be generated in the admin above your control.
default
This setting allows you to add in placeholder values to the control. It’s worth noting that these are the values that will be used until the section has been updated by the store owner.
My example is pretty simple and will create two controls. The first is a single line text field that will be rendered in the h1
element in the template, the second is a rich text box which gives the options of bold, italic, and URL.
There are many other options that you can use to pimp out your store’s admin including adding bespoke controls for URLs, collection and product listings, as well as custom HTML. We won’t look at all of them in this article but I encourage you to delve into the possibilities.
You’ll also notice that you can add custom CSS and JavaScript to section files using the following Liquid tags:
{% stylesheet %} {% endstylesheet %}
{% javascript %} {% endjavascript %}
You might be thinking that this could add a lot of potentially bloated inline CSS and JavaScript to your theme. The good news is that Shopify concatenates all CSS and JS into a single file which is injected via the Liquid content_for_header
placeholder. The platform ensures that only a single instance is ever included, even if that section is used multiple times on a page. More information about how scripts are executed is available in the Shopify docs.
Once you have set up your controls and names in the JSON file you are able to include the section in any relevant template. Sections can be added to a layout file (the outer skin of a page) or an individual template file. The Shopify admin will display the controls contextually: i.e. only when viewing the relevant template in the “customize theme” editor. Changing values will result in a real-time update in the admin: this is a great way to see how amends will affect the layout before updating and pushing live.
If you have a look at the rendered HTML you’ll notice that sections are wrapped in a div
element: