Shopify color swatches on product cards and filters

Complete Guide to Color Swatches on PDP, PLP and Filters in Shopify

By Nathaly Rodriguez
ShopifyColor SwatchesProduct DisplayTheme DevelopmentUX

Color swatches are an essential visual element that significantly enhances the Shopify shopping experience by allowing customers to quickly see available color options without clicking through multiple product pages. This comprehensive guide will walk you through implementing color swatches across three key areas: Product Detail Pages (PDP), Product Listing Pages (PLP), and collection filters.

Whether you’re a Shopify theme developer or store owner looking to improve user experience and conversion rates, this step-by-step tutorial will help you add professional color swatches that match your brand’s aesthetic while maintaining fast loading times and mobile responsiveness.

Benefits of Color Swatches

  • Enhanced User Experience: Visual color selection reduces clicks and improves conversion rates
  • Professional Appearance: Creates a polished, modern look that matches customer expectations
  • Better Product Discovery: Helps customers quickly identify preferred color options
  • Reduced Bounce Rate: Visual engagement keeps users browsing longer
  • Mobile-Friendly: Touch-friendly color selection on all devices

Implementation Overview

This comprehensive guide covers three main implementation areas for Shopify color swatches:

  1. Product Cards (PLP) - Display color swatches on collection pages, featured collections, and related product sections
  2. Collection Filters - Implement color-based filtering with visual swatches for better user navigation
  3. Product Detail Pages (PDP) - Add color swatch variant selectors for intuitive product customization

Prerequisites for Implementation

Before diving into the implementation, ensure you have:

  • Access to your Shopify theme’s Liquid files
  • Color swatch images prepared as PNG files (detailed requirements below)
  • Basic understanding of Shopify theme structure and Liquid templating
  • Theme customization permissions in your Shopify admin

File Structure Overview

You’ll be working with these key files:

  • card_product.liquid - Product card template for collection pages
  • facets.liquid - Collection filtering system
  • product-variant-options.liquid - Product page variant selector
  • settings_schema.json - Theme configuration settings
  • en.default.schema.json - Translation strings and labels

Step 1: Add Color Swatches to Product Cards (PLP)

For color swatches on product cards that will be rendered on collection pages, as well as other sections like featured collections and suggested collections, go to

card_product.liquid or whatever the file name of the product cards of the theme, look for the class “card__content” and paste this snippet of code below substituting all the content inside.

<div class="card__information">
  <div class="card-title-and-color-swatch-container {%- if settings.color_swatches == true -%}twcss-grid twcss-grid-rows-2 {%- endif -%} twcss-w-full">
    {%- if settings.color_swatches == true -%}
      {% assign keys = "Color,color,Colour,colour" | split: ',' %} {% comment %} This is to get the color in the name of the variable {% endcomment %}    
      {%- for key in keys -%}
        {%- if card_product.options contains key -%}
        <div class="card-color-swatches">
          {%- for option in card_product.options_by_name[key].values -%}
            {%- assign color_swatch = option | escape | downcase | replace: ' ', '-' | replace: '/', '-' -%}
            {%- assign background = color_swatch | append: '.png' | file_url -%}
            {%- if background -%}
              <span class="circle-color-swatch twcss-inline-block twcss-border twcss-border-gray-300 twcss-h-6 twcss-w-6 twcss-rounded-full twcss-bg-cover twcss-bg-center" style="background: url({{ background }});"></span>
              <span class="visually-hidden" aria-hidden="true">{{ color_swatch }}</span>
            {%- endif -%}
          {%- endfor -%}
        </div>
        {%- endif -%}
      {%- endfor -%}
    {%- endif -%}
    <h3
      class="card__heading"
      {% if card_product.featured_media == null and settings.card_style == 'standard' %}
        id="title-{{ section_id }}-{{ card_product.id }}"
      {% endif %}
    >
      <a
        href="{{ card_product.url }}"
        id="StandardCardNoMediaLink-{{ section_id }}-{{ card_product.id }}"
        class="full-unstyled-link"
        aria-labelledby="StandardCardNoMediaLink-{{ section_id }}-{{ card_product.id }} NoMediaStandardBadge-{{ section_id }}-{{ card_product.id }}"
      >
        {{ card_product.title | escape }}
      </a>
    </h3>
    </div>
  </div>

Step 2: Add Color Swatches to Collection Filters

Next, navigate to facets.liquid or the file where the filters are stored. Inside the <ul> tag where each filter item name is rendered, paste the following code to add color swatches to your filter options:

{%- for value in filter.values -%}
  {%- liquid
    if settings.color_swatches == true 
      assign color_swatch = value.value | escape | downcase | replace: ' ', '-' | replace: '/', '-'
      assign background = color_swatch | append: '.png' | file_url
    endif
    assign filter_name = filter.label | escape
  -%}
  <li class="list-menu__item facets__item{% if forloop.index > 10 and filter_type == 'vertical' %} show-more-item hidden{% endif %}">
    <label
      for="Filter-{{ filter.param_name | escape }}-{{ forloop.index }}"
      class="facet-checkbox{% if value.count == 0 and value.active == false %} facet-checkbox--disabled{% endif %}"
    >
      <input
        type="checkbox"
        name="{{ value.param_name }}"
        value="{{ value.value }}"
        id="Filter-{{ filter.param_name | escape }}-{{ forloop.index }}"
        class="twcss-peer"
        {% if value.active %}
          checked
        {% endif %}
        {% if value.count == 0 and value.active == false %}
          disabled
        {% endif %}
      >

      <svg
        width="1.6rem"
        height="1.6rem"
        viewBox="0 0 16 16"
        aria-hidden="true"
        focusable="false"
        {%- if settings.color_swatches == true -%}
          style="background: url({{ background }});"
          {%- if filter_name == 'Color' or filter_name == 'color' or filter_name == 'Colour' or filter_name == 'colour' -%}
            class="twcss-rounded-full twcss-w-8 twcss-h-8 twcss-bg-cover twcss-bg-center"
          {% else %}
          class="twcss-w-7 twcss-h-7 twcss-bg-cover twcss-bg-center"
          {%- endif -%}
        {%- endif -%}
      >
      {% if filter_name == 'Color' or filter_name == 'color' or filter_name == 'Colour' or filter_name == 'colour' %}
        <circle cx="8" cy="8" r="8" stroke-width=".35" fill="none" stroke-width="1" stroke="currentColor"/>
      {% else %}
        <rect width="16" height="16" stroke="currentColor" fill="none" stroke-width="1"></rect>
      {% endif %}
      </svg>

      <svg
        aria-hidden="true"
        class="icon icon-checkmark"
        {% if filter_name == 'Color' or filter_name == 'color' or filter_name == 'Colour' or filter_name == 'colour' %}
          width="1.4rem"
          height="1.2rem"
        {% else %}
          width="1.1rem"
          height="0.7rem"
        {% endif %}
        viewBox="0 0 11 7"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      >
        <path d="M1.5 3.5L2.83333 4.75L4.16667 6L9.5 1"
          stroke="currentColor"
          stroke-width="1.75"
          stroke-linecap="round"
          stroke-linejoin="round" />
      </svg>

      <span aria-hidden="true"  class="peer-checked:twcss-font-bold">{{ value.label | escape }} ({{ value.count }})</span>
      <span class="visually-hidden">
        {{- value.label | escape }} (
        {%- if value.count == 1 -%}
          {{- 'products.facets.product_count_simple.one' | t: count: value.count -}}
        {%- else -%}
          {{- 'products.facets.product_count_simple.other' | t: count: value.count -}}
        {%- endif -%}
        )</span
      >
    </label>
  </li>
{%- endfor -%}

Step 3: Add Color Swatches to Product Detail Pages (PDP)

For the product detail page implementation, navigate to product-variant-options.liquid and add the following code:

<style>
  .product-form__input input[type=radio]:checked+label.color-swatch{
    box-shadow: 0 0 0 2px #000;
    transition: box-shadow .2s ease;
  }
  .product-form__input input[type=radio]+label.color-swatch {
    border: 2px solid gray;
    padding: 0;
  }
  .product-form__input input[type=radio]+label.color-swatch::before{
    content:"";
    border: 3px solid #fff;
    border-radius:100%;
    z-index:1;
    position: absolute;
    top:-1px;
    left:-1px;
    right:-1px;
    bottom:-1px;
  }
</style>
{%- liquid
  assign variants_available_arr = product.variants | map: 'available'
  assign variants_option1_arr = product.variants | map: 'option1'
  assign variants_option2_arr = product.variants | map: 'option2'
  assign variants_option3_arr = product.variants | map: 'option3'

  assign product_form_id = 'product-form-' | append: section.id
-%}

{%- for value in option.values -%}
  {%- liquid
    assign option_disabled = true

    for option1_name in variants_option1_arr
      case option.position
        when 1
          if variants_option1_arr[forloop.index0] == value and variants_available_arr[forloop.index0]
            assign option_disabled = false
          endif
        when 2
          if option1_name == product.selected_or_first_available_variant.option1 and variants_option2_arr[forloop.index0] == value and variants_available_arr[forloop.index0]
            assign option_disabled = false
          endif
        when 3
          if option1_name == product.selected_or_first_available_variant.option1 and variants_option2_arr[forloop.index0] == product.selected_or_first_available_variant.option2 and variants_option3_arr[forloop.index0] == value and variants_available_arr[forloop.index0]
            assign option_disabled = false
          endif
      endcase
      assign name_of_color = value | escape | downcase | replace: ' ', '-' | replace: '/', '-'
    endfor
  -%}

  {%- if block.settings.picker_type == 'button' -%}
    <input
      type="radio"
      id="{{ section.id }}-{{ option.position }}-{{ forloop.index0 }}"
      name="{{ option.name }}"
      value="{{ value | escape }}"
      form="{{ product_form_id }}"
      {% if option.selected_value == value %}
        checked
      {% endif %}
      {% if option_disabled %}
        class="disabled"
      {% endif %}
    >
    {% if option.name == 'Color' %}
    <label class="color-swatch twcss-h-16 twcss-w-16 twcss-bg-no-repeat twcss-bg-center twcss-bg-cover twcss-rounded-full" style="background-image:url({{ name_of_color | append: '.png' | file_url }})" for="{{ section.id }}-{{ option.position }}-{{ forloop.index0 }}">
      <span class="visually-hidden" aria-hidden="true">{{ name_of_color }}</span>  
    </label>
  {% else %}
    <label for="{{ section.id }}-{{ option.position }}-{{ forloop.index0 }}">
      {{ value }}
    </label>
  {% endif %}
  {%- elsif block.settings.picker_type == 'dropdown' -%}
    <option
      value="{{ value | escape }}"
      {% if option.selected_value == value %}
        selected="selected"
      {% endif %}
    >
      {% if option_disabled -%}
        {{- 'products.product.value_unavailable' | t: option_value: value -}}
      {%- else -%}
        {{- value -}}
      {%- endif %}
    </option>
  {%- endif -%}
{%- endfor -%}

Step 4: Add Theme Settings

To allow merchants to enable/disable color swatches, go to settings_schema.json and add the following setting:

{
    "name": "t:settings_schema.color_swatches.name",
    "settings": [
      {
        "type": "checkbox",
        "id": "color_swatches",
        "label": "t:settings_schema.color_swatches.settings.color_swatches.label",
        "default": true
      }
    ]
  },

Step 5: Add Translation Strings

Finally, add the translation definitions to en.default.schema.json:

"color_swatches": {
  "name": "Color swatches",
  "settings": {
    "color_swatches": {
      "label": "Enable color swatches"
    }
  }
},

Additional instructions

All the color swatches are rendered via assets files on the general settings of the store

The file MUST be .png. Also, it must has the followind directions on the name, so it can be assigned properly:

  • All letters must be downcase.
  • No spaces or special characters allowed. In this case, special characters like ”/” should be replaced by ”-“. Same for white spaces.
  • Some examples of proper name of files are:
    • noir.png ➝ for Noir color.
    • off-white.png ➝ for Off White color.
    • black-white.png ➝ for Black/White color.
    • white---light-blue.png ➝ for White / Light Blue color. In this case, there are more ”-” because there are blank spaces between the ”/“
    • bg-not-found.svg ➝ This is a fallback background only applied for now on the PDP in case the color swatch background is not founded. I added this only on that page becasuse I wasn’t sure if that’s the best solution for missing assets.

Also, I created a setting for enable/disable color swatches on the theme in case the client don’t want to add it.