Theme from scratch

From Shopify Wiki

Jump to: navigation, search

In this tutorial you will learn how to create a Shopify theme from scratch.

Contents

What we need to get started

Partner Accounts allows you to create a free Shopify store you can test on after you sign up. Login into your account and click on the "Referral" tab and click "Create a new affiliate store".

If you are on a Mac, check out the Desktop Theme Editor so you can edit live Shopify stores from a local environment.

5 things you need to know about Shopify

There are 5 fundamental concepts you need to understand before you make a theme

Liquid

Liquid is Shopify's super simple programming language. There are 2 types of Liquid tags:

  • {% %} this is a logic tag, nothing will visually appear on the screen
  • {{ }} 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>
Your shop name

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

Templates

Templates control the CSS and HTML of you shop's content. Every part of your Shopify website is rendered by a different template. For example, when customer views one of your shop's products, Shopify will use the product.liquid template to render the product page. If they view the blog Shopify will use blog.liquid to render the page.

Template Variables

Template variables 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 variable. If we want to get the name of your store we use the shop.title variable.

Filters

Filters manipulate the output of template variables. If you want to show a customer a price of a $99.00 product and you use the template variable {{ product.price }}, Shopify will display the price as 9900 (in cents). So we need to use a filter to manipulate the output of this template variable.

The syntax for a filter looks like this: {{ variablename | filtername }}. Out of all the filters I can choose from, the money_with_currency filter makes the most sense for the example problem. Your code should look like this:

{{ product.price | money_with_currency }}
$99.00 USD

Logic

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

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

Summary

  • Liquid - We use Liquid to tell Shopify what to do.
  • Templates - Control the look and feel of your shop.
  • Template variables - Are pieces of data we can use.
  • Filters - Manipulate the outputs of template variables.
  • Logic - Advanced Liquid statements

Let's Create a theme from scratch

Theme.liquid

What is Theme.liquid? Think of theme.liquid as the master template - all other templates are rendered inside of theme.liquid. If you have any HTML elements that are repeated in your theme (ex: site navigation), 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>

scratch

Your first line of Liquid

Lets output the name of our shop using the "shop" template variable with the "name" attribute. Put them both together and you get shop.name:

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

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

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

scratch

Add a navigation

Your shop's navigations are created in your Shopify store's admin and are called "Linklists". Linklists are made up of individual links. Linklists have template variables just like "shop" and have attributes like linklist.title, linklist.links, or linklist.handle. Shopify stores comes with 2 pre-made linklists: main-menu and footer.

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 in the linklist 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>

scratch

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

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 variable returns the title of the page. Try out some more page template variables.

scratch

Collection.liquid

Collections.liquid is used for displaying collections of your products (collections can also be thought of "product categories"). In most cases collections will be something like "Shoes" or "Shirts". To access an example of a collection click on "catalog" in our main 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 collection's handle name might be.

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

Let's add product images and product 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>

scratch

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 %} 

scratch

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 template variable. In other words we need to apply the "money" filter to the "product.price" variable.

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

Let's add 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 - If the product is in a custom collection it will put the collection name in the url instead of the regular /products url.
  • product_img_url: 'small' - we are specifying the image size from the nine sizes we can use.
  • truncate: 30 - truncate the number of characters by 30
  • money - makes prices look nice

scratch

Sometimes our collections will have too many products in them and we need to use multiple pages. To create multiple pages we use 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 %}

scratch

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 %}

scratch

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

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

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 %}

scratch

Add to cart

Use a <form> with the action="/cart/add". You need to loop through any variants a product may 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 %}....

scratch

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 %}


scratch

Cart.liquid

This is the shopping cart template.

What if the cart's empty?

Definitely room for you to be 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 %}

scratch

The fat ass Cart table

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 %}

scratch

The End

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. I recommend visiting the Shopify theme store and downloading a free theme like New Standard so you can see other designer's Shopify code.

So what's next?

If you have questions on liquid or having trouble making your code work then head over to the designer forums.