I'm going to teach you how to be a Shopify Ninja


You need these things before we start

Vision

Vision is a tool that lets you build Shopify Themes in a localhost environment (like your own little server).

Partner Account

Partner accounts are filled with awesomeness and you can create crippled Shopify stores sans credit card.

Pants

I know a lot of you web design types think it's funny when you design without them, but seriously guys.


Let's get Vision up and running

  • What's vision again? Vision lets you create Shopify themes offline and when your finished you just export your theme and upload it to your store. You don't need to install anything other than Vision on your computer.
  • Do I have to use it? No, but its the quickest way to create Shopify themes
  • What's the worst thing about it? Sometimes Vision doesn't support newer Shopify features. There is an occasional bug or two in there as well.

Setting up Vision

This is a video walking you though downloading and installing Vision.


What you need to remember from the video

  • To go to the vision dashboard go to 127.0.0.1/dashboard/ on windows or localhost:3232/dashboard/ on the mac
  • Assets - put your themes css/javascript/images in this folder
  • Layout - contains the master theme.liquid template
  • Templates - where all your templates are located
  • In vision you don't have access to the Shopify Admin and you can't edit the default store products*
    *Actually you can, but it involves some tricky database editing

5 things you need to know about Shopify

  • Liquid

    We use Liquid to tell Shopify what to do.
  • Templates

    Control the look and feel of your shop.
  • Objects

    Are pieces of data we can use.
  • Filters

    Manipulate the outputs of objects.
  • Logic

    Advanced Liquid statements

Liquid

Liquid is a simple programming language. We use Liquid to tell Shopify what to do.
There are 2 types of Liquid tags

  1. {% %} this is a logic tag, nothing will visually appear on the screen
  2. {{ }} this is an output tag, something will visually appear on the screen

If we want to display our shop's name, this is the Liquid we use to do it:

<h1>{{ shop.name }}<h1>

Notice how we use {{ }} brackets, if we used {% %} brackets nothing would've appeared on the screen

Bg Liquid
Bg Templates

Templates

Templates control the look and feel of you shop's content. Let's say a customer views one of your shop's products, Shopify will use the product.liquid template to show render the product page. If a customer views your shop's blog, Shopify will use blog.liquid to render the blog page.

Templates always end in the .liquid extension

In Vision templates are found in the Templates folder and on your online shop they are found under Assets>Theme Editor

Objects

Objects are the pieces of data we can use in your shop. If we want to get the name of a product we use the product.title object. If we want to get the name of your store we use the shop.title object.

One object you'll use a lot is the handle object. Think of a handle as a name or id of something in your Shopify store. For example if I have a product named "Red Toothbrush" then the handle will be "red-toothbrush".

The Shopify Cheat Sheet has a list of all (if not most) Shopify objects

Bg Objects
Bg Filter

Filters

Filters manipulate the output of objects.

For example if you want to show a customer a price of a $99.00 product and you write {{ product.price }}, Shopify will display the price as 9900. I have to use a filter to manipulate the output of the product.price object.

So if I use the money_with_currency filter on the product.price object it will now display as $99.00 USD

Filters look like this {{ objectname | filtername }}. So in the example above I use {{ product.price | money_with_currency }} on the template product.liquid $99.00 USD will appear.

Logic

Logic are conditional Liquid statements

Lets say we are working on product.liquid template and want to display the message "Free shipping", but only on products whose price is greater than $100. Since the product will either have a price greater than $100 or less than $100, the if logic statement is the most appropriate statement to use.

{% if product.price > 100 %}
Free Shipping
{% else %}
No free shipping
{% end if %}

There are other logic statements like {% for %} and {% unless %}

Bg Logic

Let's Create a Theme From Scratch

Download this blank slate theme, unzip it and put it into your Vision › Themes folder. It's a blank slate theme that we will be using. Open it up in your fave text editor.



Theme.liquid

What is theme.liquid? Think of theme.liquid as the master template. If you have any HTML element that needs to be on every single page of your website, put it in theme.liquid.

There are 2 things that every theme.liquid file needs. The first thing is {{ content_for_header }} and that belongs in your <head></head>. The second is {{ content_for_layout }} and that belongs in <body></body>

<!DOCTYPE html>
<html>
<head>
{{ content_for_header }}
<title>Blank Slate</title>
</head>

<body>
<h1>This is a blank theme</h1>
{{ content_for_layout }}
</body>
</html>
Theme Ss1

Your first line of Liquid

Lets output the name of our shop using the "shop" object with the "name" property. Put them both together and you get "shop.name"

<h1>{{ shop.name }}</h1>

Now lets add a title tag. page_title is a weird object. We won't cover those in this tutorial.

<title>{{ shop.name }} - {{ page_title }}</title>
Theme Ss2

Add a navigation

Shopify's navigations are called "Linklists". They are template objects just like "shop" and have properties like linklist.title, linklist.links, or linklists.handle. Vision comes with a pre-made linklist with the handle main-menu (remember handle's are like names or IDs).

So for every link in main-menu we want to generate a link. A loop logic statement would be the best logic statement to use here since we are cycling through every link in the link list main-menu.

This is what a regular Liquid loop statement looks like

{% for something in something %}
output this
{% endfor %}

And this is what our Liquid navigation loop will look like

  <ul class="navigation">
{% for link in linklists.main-menu.links %}
<li><a href="{{ link.url }}">{{ link.title }}</a></li>
{% endfor %}
</ul>

In English this reads as For every link in the link list "main-menu" generate an <li> and inside of that li generate the link.title and link it up to the link.url

Theme Ss3

Recap of my theme.liquid

<!DOCTYPE html>
<html>
<head>
{{ content_for_header }}
<title>{{ shop.name }} - {{ page_title }}</title>
</head>

<body>
<h1>{{ shop.name }}</h1>
<ul class="navigation">
{% for link in linklists.main-menu.links %}
<li><a href="{{ link.url }}">{{ link.title }}</a></li>
{% endfor %}
</ul>
{{ content_for_layout }}
</body>
</html>

Page.liquid

Page.liquid template generates all of your page content.

  <h1>{{ page.title }}</h1>
{{ page.content }}

page.content will be where all the content from the page will be generated. The page.title object returns the title of the page.

Some other page objects you can use

  • page.handle returns page handle
  • page.url returns page url
  • page.author returns author
Theme Ss4

Recap of my theme.liquid

<h1>{{ page.title }}</h1>
{{ page.content }}

Collection.liquid

Collections.liquid is used for displaying collections of your products. In most cases collections will be something like "Shoes" or "Shirts". In our blank slate theme we can see an example of the collection template in action by clicking on "Catalog" in our navigation

We are going to use the tablerow logic statement to generate our collection. In it's basic form a tablerow logic statement looks like this

<table>
{% tablerow something in something %}
do this
{% endtablerow %}
</table>

Now let's take the above code and specify what data we want the tablerow to use. In this case we are going to specify all products (collection.products). If we wanted to specify a specific collection we could use collection.snowboard or whatever the handle name might be.

<table>
{% tablerow product in collection.products %}
do this
{% endtablerow %}
</table>

Now let's throw in the rest of the Liquid so we can show the user the images and prices associated with this collection

<table>
{% tablerow product in collection.products %}

<div class="image">
<a href="{{ product.url | within: collection }}" title="{{ product.title }}">
<img src="{{ product.images.first | product_img_url: 'small' }}" alt="{{ product.title }}" />
</a>
</div>

<div class="description">
<a href= "{{ product.url | within: collection }}">{{ product.title }}</a><br />
<small>{{ product.price }}</small>
</div>


{% endtablerow %}
</table>
Theme Ss5

There are some issues with the above code. The first one is our table doesn't have any columns. To fix that we use an attribute called "cols". An attribute is something that modifies the output of a logic statement (in this case our logic statement is a tablerow). This is how we add an attribute:

{% tablerow product in collection.products cols:3 %} 
Theme Ss6

Another problem is our price for the products is messed up. That's because we need to use a filter to modify the output of the object. In other words we need to apply the "money" filter to the "product.price" object.
Vision outputs a space between the currency symbol and the price number, this is a bug and does not occur when you export your theme online.

<small>{{ product.price | money_without_currency }}</small>

Let's do some more filters...

  <div class="image">
<a href="{{ product.url | within: collection }}" title="{{ product.title }}">
<img src="{{ product.images.first | product_img_url: 'small' }}" alt="{{ product.title }}" />
</a>
</div>

<div class="description">
<a href= "{{ product.url | within: collection }}">{{ product.title | truncate: 30 }}</a><br />
<small>{{ product.price | money }}</small>
</div>

Here is a quick description on what they do

  • within:collection - this doesn't work in Vision. If the product is in a custom collection it will put the collection name in the url instead of the regular /products url. Don't worry if you don't understand.
  • product_img_url: 'small' - we are specifying the image size from the 9 Liquid gives us.
  • truncate: 30 - truncate the number of characters by 30
  • money - makes prices look nice
Theme Ss7

Sometimes our collections will have too many products in them and we need to use multiple pages. To create multiple pages we use pagination
Pagination doesn't work in Vision, wait until you upload your theme online to tweak pagination

{% paginate collection.products by 12 %}

<table class = "products">
... your table code ....
</table>

{% if paginate.pages > 1 %}
<div id="paginate">
{{ paginate | default_pagination }}
</div>
{% endif %}
{% endpaginate %}

Theme Ss8

Don't forget to prepare for nothing. If a collection is empty we want to make sure the user doesn't just see a blank page, so we add a simple "if" logic statement

{% paginate collection.products by 12 %}

{% if collection.products.size == 0 %}
No products found in this collection.
{% else %}


...table stuff...

....pagination stuff...

{% endif %}

{% endpaginate %}

Let's add the title of the collection and description of the collection to our template

<h2>{{ collection.title }}</h2>
{{ collection.description }}
Theme Ss9

Recap of collection.liquid

{% paginate collection.products by 12 %}

<h2>{{ collection.title }}</h2>

{{ collection.description }}

{% if collection.products.size == 0 %}
<strong>No products found in this collection.</strong>
{% else %}

<table class = "products">
{% tablerow product in collection.products cols: 3 %}

<div class="image">
<a href="{{ product.url | within: collection }}"
title="{{ product.title }}">
<img src="{{ product.images.first | product_img_url: 'small' }}"
alt="{{ product.title }}" /></a>
</div>

<div class="description">
<a href= "{{ product.url | within: collection }}">
{{ product.title | truncate: 30 }}</a><br />
<small>{{ product.price | money }}</small>
</div>

{% endtablerow %}
</table>


{% if paginate.pages > 1 %}
<div id="paginate">
{{ paginate | default_pagination }}
</div>
{% endif %}

{% endif %}

{% endpaginate %}

Product.liquid

This template will display the individual products to the customer. For the sake of space this tutorial will not cover products with multiple options (products with more than one attribute, like a product with size and color)

Let's generate our product title and product description and test to see if the product is available

<h2>{{ product.title }}</h2>
{{ product.description }}

{% if product.available %}
This product is available!
{% else %}
Sorry, the product is not available
{% endif %}

Theme Ss10

Add to cart

Use a <form> with the action="/cart/add". You need to loop through any variants a product my have. Variants are just variations of an individual product like size or color

...{% if product.available %}

<form action="/cart/add" method="post">
<select id="product-select" name='id'>

{% for variant in product.variants %}
<option value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
{% endfor %}

</select>
<input type="submit" value="Add to cart" id="addtocart" />
</form>

{% else %}....
Theme Ss11

Adding images

First we need to loop through all the images in product.images and if the image is the first image we want to make that a big image

{% for image in product.images %}

{% if forloop.first %}

<div id="first-image">
<a href="{{ image | product_img_url: 'large' }}" title="{{ product.title }}">
<img src="{{ image | product_img_url: 'medium' }}" alt="{{ product.title | escape }}" /></a>
</div>

{% else %}

<div class="product-images">
<a href="{{ image | product_img_url: 'large' }}" title="{{ product.title }}">
<img src="{{ image | product_img_url: 'small' }}" alt="{{ product.title | escape }}" /></a>
</div>

{% endif %}

{% endfor %}
Theme Ss12

Product.liquid recap

 <h2>{{ product.title }}</h2>
{{ product.description }}

{% if product.available %}

<form action="/cart/add" method="post">
<select id="product-select" name='id'>
{% for variant in product.variants %}
<option value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
{% endfor %}
</select>
<input type="submit" value="Add to cart" id="addtocart" />
</form>


{% for image in product.images %}

{% if forloop.first %}

<div id="first-image">
<a href="{{ image | product_img_url: 'large' }}" title="{{ product.title }}"><img src="{{ image | product_img_url: 'medium' }}" alt="{{ product.title | escape }}" /></a>
</div>

{% else %}

<div class="product-images">
<a href="{{ image | product_img_url: 'large' }}" title="{{ product.title }}"><img src="{{ image | product_img_url: 'small' }}" alt="{{ product.title | escape }}" /></a>
</div>

{% endif %}

{% endfor %}

{% else %}
Sorry, the product is not available
{% endif %}

Cart.liquid

This is the shopping cart template. VISION DOES NOT SUPPORT CART FUNCTIONALITY - so don't worry if your cart doesn't update or you can't delete products

What if the cart's empty?

Definitely room for you to creative here, but let's keep it short and sweet

<h2>Shopping Cart</h2>
{% if cart.item_count == 0 %}
<p>Your shopping basket is empty.</p>
{% else %}
do stuff
{% endif %}
Theme Ss13

The fat ass Cart table

This is going to look really complicated, but it's really nothing you can't handle. It has a bit of inline javascript to get the "update" and "remove" buttons working.

The only 3 important things to pay attention to is the <form> uses the action /cart, we loop through each of the items in the user's cart by using {% for item in cart.items %} and the 2 submit buttons at the bottom to update and checkout.

<script type="text/javascript">
function remove_item(id) {
document.getElementById('updates_'+id).value = 0;
document.getElementById('cartform').submit();
}
</script>

<form action="/cart" method="post" id="cartform">
<table>
<tr>
<th>Item Description</th>
<th>Price</th>
<th>Qty</th>
<th>Delete</th>
<th>Total</th>
</tr>

{% for item in cart.items %}
<tr>
<td class="images-and-description">
<a href="{{ item.product.url }}"><img src="{{ item.product.images.first | product_img_url: 'thumb' }}" /></a>
<p><a href="{{ item.product.url }}">{{ item.title }}</a></p>
{{ item.product.description | strip_html | truncate: 120 }}
</td>

<td class="price">{{ item.price | money }}
</td>

<td class="quantity"><input type="text" size="4" name="updates[{{item.variant.id}}]" id="updates_{{ item.variant.id }}" value="{{ item.quantity }}" onfocus="this.select();"/>
</td>

<td class="delete"><a href="#" onclick="remove_item({{ item.variant.id }}); return false;">Remove</a>
</td>

<td class="total">{{ item.line_price | money }}</td>
</tr>
{% endfor %}

</table>

<h3>Subtotal {{ cart.total_price | money }}</h3>
<input type="submit" id="update-cart" name="update" value="Update" />
<input type="submit" name="checkout" value="Checkout" />
</form>

{% endif %}
Theme Ss14

Cart.liquid Recap

<h2>Shopping Cart</h2>
{% if cart.item_count == 0 %}
<p>Your shopping basket is empty.</p>
{% else %}

<script type="text/javascript">
function remove_item(id) {
document.getElementById('updates_'+id).value = 0;
document.getElementById('cartform').submit();
}
</script>

<form action="/cart" method="post" id="cartform">
<table>
<tr>
<th>Item Description</th>
<th>Price</th>
<th>Qty</th>
<th>Delete</th>
<th>Total</th>
</tr>

{% for item in cart.items %}
<tr>
<td class="images-and-description">
<a href="{{ item.product.url }}"><img src="{{ item.product.images.first | product_img_url: 'thumb' }}" /></a>
<p><a href="{{ item.product.url }}">{{ item.title }}</a></p>
{{ item.product.description | strip_html | truncate: 120 }}
</td>
<td class="price">{{ item.price | money }}</td>
<td class="quantity"><input type="text" size="4" name="updates[{{item.variant.id}}]" id="updates_{{ item.variant.id }}" value="{{ item.quantity }}" onfocus="this.select();"/></td>
<td class="delete"><a href="#" onclick="remove_item({{ item.variant.id }}); return false;">Remove</a></td>
<td class="total">{{ item.line_price | money }}</td>
</tr>
{% endfor %}

</table>
<h3>Subtotal {{ cart.total_price | money }}</h3>
<input type="submit" id="update-cart" name="update" value="Update" />
<input type="submit" name="checkout" value="Checkout" />
</form>

{% endif %}

This is the end of the tutorial for now. I missed out a few Shopify templates like search and blog - I'll write about those eventually, but I'm sure you can figure them out by yourself. Cheers for reading and give me some feedback.