Custom Product Color Variations in Shopify

Note: Shopify now supports multiple "traits" per product, so this technique is no longer required. Thanks for visiting though! You may want to check out my Shopify Consulting site if you are interested in customizing Shopify themes.

Poetry in Flowers This page explains how I customized my Shopify site - Poetry in Flowers - by adding a color attribute to products, allowing shoppers to select their preferred color.

Before we begin

  • Poetry in Flowers is based on the Vogue theme.
  • This solution uses mootools - a very neat lightweight javascript library that Vogue, and other themes, use. One drawback of moo though, is that it doesn't play nice with prototype or scriptaculous. If you use a different theme, make sure those libraries are not being used.
  • Although I've shown how to display a list of colors, it could be any attribute, or combination of attributes, you care to associate with a product - e.g. material choice or shirt size for example.

Step 1 - Set up your product catalog as normal.

I set up my product catalog using variants for the different sizes (and prices) of flowers being offered.

Step 2 - Mootools

This solution uses mootools. Vogue uses this by default, but you should double check that it is referenced in the header section of theme.liquid.

{{ 'mootools.js' | global_asset_url | script_tag }}

Step 3 - Product.liquid

Open up product.liquid. We will be enhancing it so it will display available colors, and save the user's selection to a cookie when they click "purchase".

At the top of the file, add the following:

<script type="text/javascript">
  var colors = [];
  var variants = [];
</script>

Search for where it says "{% for variant in product.variants %}" and add the following lines inside the for loop:

<script type="text/javascript">
  variants.push(Json.evaluate('{"id":"{{ variant.id }}", "desc":"{{ variant.title }}"}'));
</script>

We will now add some code inside the form that will display the available colors as radio buttons:

<script type="text/javascript">
  if (colors.length > 0) {
    document.write("<strong><em>Available Colours</em></strong><div id=\"product-variants\"><ul>");
    for (i=0; i < colors.length; i++) {
      document.write("<li class='" + (i % 2 == 0 ? "odd" : "even") + "'>");
      document.write("<input type=\"radio\" name=\"color\" value=\"" + colors[i] + "\" id=\"radio_color_" + i + "\"");
      if (i == 0) {
        document.write(" checked=\"checked\" ");
      }
      document.write("\"/>");
      document.write(" <label for=\"radio_color_" + i + "\" class=\"radio\"><strong>" + colors[i] + "</strong></label>");
      document.write("</li>");
    }
    document.write("</ul></div>");
  }
</script>

Still in the form, find the purchase image element and add an id="purchase" attribute to it.

At the bottom of product.liquid, add the following javascript:

<script type="text/javascript">
function findVariantDesc(id) {
  for (var i = 0; i < variants.length; i++) {
    if (variants[i].id == id) {
      return variants[i].desc;
    }
  }
}

function getSelectedColor() {
    for (var i = 0; i < colors.length; i++) { // find selected color
      var chk = $('radio_color_' + i);
      if (chk && chk.checked) {
        return chk.value;
      }
    }
}

function getSelectedVariant() {
    for (var i = 0; i < variants.length; i++) { // find selected variant
      var chk = $('radio_' + variants[i].id);
      if (chk && chk.checked) {
        return chk.value;
      }
    }
}

$('purchase').addEvent('click', function(evt) {
  // invoked when "purchase" is clicked
  if (colors.length > 0) { // any custom color options?
    var variant = getSelectedVariant();
    var color = getSelectedColor();

    // get the product colors cookie (the cookie is session bound)
    var cookie = new Hash.Cookie('product-colors', {path: '/'});

    // colors are stored as an array of json tuples {product.id, product.variant, desc, color}
    var ac = $defined(cookie.get('{{ product.id }}')) ? Json.evaluate(cookie.get('{{ product.id }}')) : [];
    ac.push(Json.evaluate('{"id":"{{ product.id }}", "variant":"' + variant + '", "desc":"' + findVariantDesc(variant) + ' {{ product.title }}", "color":"' + color +'"}'));

    cookie.set('{{ product.id }}', Json.toString(ac));
  }
});
</script>

Step 4 - Cart.liquid

We will now enhance cart.liquid so that it displays the color selected by the user. I've chosen to display the colors in a dropdown so the user still has a chance to change colors if they wish.

Insert the following code just above the <form> tag:

<script type="text/javascript">
var colors = [];
var cookie = new Hash.Cookie('product-colors', {path: '/'});

function getProductColor(product, variant) {
  var ac = $defined(cookie.get(product)) ? Json.evaluate(cookie.get(product)) : [];
  for (i = 0; i < ac.length; i++) {
    if (ac[i].variant == variant) {
      return ac[i].color;
    }
  }
}

function remove_item(product, variant) {
  var ac = $defined(cookie.get(product)) ? Json.evaluate(cookie.get(product)) : [];
  var nac = [];
  for (i = 0; i < ac.length; i++) {
    if (ac[i].variant != variant) {
      nac.push(ac[i]);
    }
  }
  cookie.set(product, Json.toString(nac));

  $('updates_'+variant).value = 0;
  $('cartform').submit();
}
</script>

Add an additional table header (<th>) and column (<td>). Inside the <td> tag, add the following:

<script type="text/javascript">
  var color = getProductColor({{ item.product.id }}, {{ item.variant.id }});
  if ($defined(color)) {
    document.write('<select name="select_{{item.product.id}}_{{item.variant.id}}" id="select_{{item.product.id}}_{{item.variant.id}}">');
    for (i = 0; i < colors.length; i++) {
      document.write('<option ');
      if (colors[i] == color) {
        document.write('selected ');
      }
      document.write('value="' + colors[i] + '">');
      document.write(colors[i]);
      document.write('</option>');
    }
    document.write('</select>');
  }
</script>

While you are here, change everywhere it says "{{ item.product.description | strip_html | truncate: 50 }}" to "{{ item.product.description }}".

We need to make a couple of changes to the form elements. First, find the checkout image element and add an id="checkout" attribute to it. Second, add a hidden textarea. This will be used to store the selected colors (as an 'attributes' item), and can be used in your email notifications when an order arrives (described in Step 7):

<textarea name="attributes[color_choices]" id="color_choices" style="display:none;"></textarea>

Finally, add the code that will save the selected colors just above the last {%endif%}:

<script type="text/javascript">
$('checkout').addEvent('click', function(evt) {
  // invoked when checkout is clicked
  $('color_choices').value = "";
  cookie.each(function(value, key){
    var ac = Json.evaluate(value);
    for (i = 0; i < ac.length; i++) {
      var select = $('select_' + ac[i].id + '_' + ac[i].variant);
      $('color_choices').value += ac[i].desc + ' = ' + select.value + '\n';
    }
  });
});
</script>

Step 5 - Specifying those colors

Ok, you've done the hard work. Hopefully you are still with me as we are almost done. We now need to add the available color selections to each product page. For each product that offers color choices, edit its description and add the following:

<notextile>
<script type="text/javascript">
   colors = ['Pastels', 'Pinks', 'Whites', 'Yellows & Oranges'];
</script>
</notextile>

Step 6 - Clearing the cookie

We need to put in a sanity check to make sure when the cart is empty (e.g. after a checkout) the colors cookie is also empty. In theme.liquid, add the following:

{% if cart.item_count == 0 %}
<script type="text/javascript">
var cookie = new Hash.Cookie('product-colors', {path: '/'});
cookie.empty();
</script>
{% endif %}

Step 7 - Notifications

Now open up the New Order Notification email template and add a reference to the colors:

{{ attributes.color_choices }}

That's it - you are done!

Limitations

There are some things I'd like to improve on...

  • This approach only allows users to select the colors for a product + variant combo. This means you can't simultaneously select a Large White Rose Box and a Large Pink Rose Box, for example.
  • Every time the product description is used javascript is executed. In this case it is benign, but you may want to consider storing colors in a blog article or javascript file asset instead.
  • It would be nice to be able to put the selected colors in the notification emails along with the line items they belong to. I think I have a way to do this -I  just need a rainy day to play around a bit.

Wrapping Up

Shopify Shopify is a great solution for small to midsize business that need to get an ecommerce site up and running quickly. There is an awesome ecosystem of designers working with the platform, creating some truly remarkable sites. Hopefully you have found this tutorial useful, and given you some ideas on how to make your shop a success. Please let me know if you find any problems, or have any other suggestions, so that I can incorporate your feedback and improve this page.

Update: I am now offering Shopify consulting help for back-end Shopify development. Web Designers and Shopify Store Owners, if you are looking for someone to help with liquid, javascript, webhooks or any other of the technical aspects behind a Shopify site I'd love to help - give me a shout.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d8341c9edc53ef00e5521d301f8833

Listed below are links to weblogs that reference Custom Product Color Variations in Shopify:

Comments

Feed You can follow this conversation by subscribing to the comment feed for this post.

Gavin: great write up. I've been digging into Shopify customization and this is a superb example.
Your code is exactly what Im looking for... I am going to mod it a little so that it pulls the options from the products tags instead of hard coding them... If you want the modified code let me know. Phil
@Phil - that sounds like a good approach. I am using the tags for navigation, but I'm sure other folks would like to know how you go using tags. Send me the code I'll update the page.
I don't know how to get code to display in this comment box correctly, please bear with me.

Does this still work? Maybe I am missing something but I can't get colors displayed by editing the product description, adding,

notextile
script type="text/javascript"
colors = ['Navy', 'Black', 'White'];
/script
/notextile

I edited the code on product.liquid so it says,

if (colors.length > 0) {...} else { document.write("no colors available"); }

and it says that no colors are available.
Then I defined colors in the script at the top of product.liquid so it says,

script type="text/javascript"
var colors = ["Red", "Orange", "Blue"];
var variants = [];
/script

And then Red, Orange and Blue displayed correctly. This leads me to think it is a problem with the code in the product description.
Did I forget something?
Thanks.
Brad - in the product description, are you using tags properly? (maybe you left them out because the comments won't allow it). Anyways, what is happening is {{ product.description }} is actually loading the javascript that specifies the colors, so make sure it is above the "if colors.length" statement. You should be able to "view source" and see the javascript statements from the product description if it working properly.
Thank you, it works perfectly now. The problem was that {{ product.description }} was loading after I was using the colors, so nothing was available yet. A little rearranging and colors appeared. Thank you for the help.
Do you have the original themes/txt that i can copy and paste - mine are not working properly over on - http://www.purplepigz.co.uk Some bits are ok but the checkout page is messed up.. colours and rows and i get undefined in the email notification.
@Phil - Sorry, I don't have anything you can download. Re your checkout page, it looks like you are missing the javascript tags around the javascript code.

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been saved. Comments are moderated and will not appear until approved by the author. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment

Comments are moderated, and will not appear until the author has approved them.