Native color swatches in Dawn theme's product cards

Topic summary

A developer is customizing the Dawn theme to display native color swatches on product cards in collection pages without using plugins. The implementation relies on variant metafields (variant.metafields.custom.color) to retrieve hex color codes.

Current Issue:

  • Color swatches only render correctly on the first product card
  • Subsequent cards show color names as text but not the actual color swatches
  • The problem appears related to variable scoping within the product loop

Solutions Attempted:

  • Multiple users provided revised Liquid code that dynamically detects the ‘Color’ option index using product.options_with_values
  • Suggestions included renaming the unique_colors variable to seen_colors to avoid scope conflicts
  • One contributor recommended moving the code to a separate snippet and calling it within the collection loop

Status:
The issue remains unresolved. The proposed code variations have not fixed the rendering problem beyond the first card. One expert suggested this requires advanced customization, potentially involving shop-level metafields or metaobjects to manage color values consistently across all products.

Summarized with AI on October 27. AI used: claude-sonnet-4-5-20250929.

I’m customizing Dawn and I would like product cards to show color swatches without using a plugin. So far I made it work in a collection page but it only works for the first card, this is the code I’m using:

{% assign color_option_index = nil %}

{%- assign first_variant = product.variants.first -%}

{%- if product.options.size > 0 -%}
  {%- if product.options[0] == 'Color' -%}
    {%- assign color_option_index = 0 -%}
  {%- elsif product.options[1] == 'Color' -%}
    {%- assign color_option_index = 1 -%}
  {%- elsif product.options[2] == 'Color' -%}
    {%- assign color_option_index = 2 -%}
  {%- endif -%}
{%- endif -%}

{% if color_option_index != nil %}
{% assign unique_colors = '' %}
<div class="color-swatches">
  {% for variant in product.variants %}
    {% assign color_name = variant.options[color_option_index] %}
    {% assign color_hex = variant.metafields.custom.color | metafield_text %}

    {% unless unique_colors contains color_name %}
      {% assign unique_colors = unique_colors | append: color_name | append: ',' %}
      
      {% if color_hex %}
        <span class="swatch" style="background-color: {{ color_hex }};" title="{{ color_name }}"></span>
      {% endif %}
    {% endunless %}
  {% endfor %}
</div>
{% endif %}

Do you know how can I make it work without hardcoding the color codes?

Hello. Tamela here. To understand your question correctly, do you mean color swatches as in the drop down selection? Normally that is already there automatically. Can you provide a screenshot?

Below is a revised version that detects the ‘Color’ index dynamically per product and ensures color swatches render independently for each product:

{%- assign color_option_index = nil -%}

{%- for option in product.options_with_values -%}
{%- if option.name == ‘Color’ -%}
{%- assign color_option_index = forloop.index0 -%}
{%- endif -%}
{%- endfor -%}

{% if color_option_index != nil %}
{% assign unique_colors = ‘’ %}

{% for variant in product.variants %} {% assign color_name = variant.options[color_option_index] %} {% assign color_hex = variant.metafields.custom.color | metafield_text %}

{% unless unique_colors contains color_name %}
{% assign unique_colors = unique_colors | append: color_name | append: ‘,’ %}

{% if color_hex %}

{% endif %}
{% endunless %}
{% endfor %}

{% endif %}

Hey! @naio ,

Yes! You’re on the right track with your code. The main issue you’re facing is likely due to how the unique_colors string is being reused or scoped incorrectly in a loop across multiple product cards—this happens in collection pages where multiple product loops occur. Here’s a fixed and improved version that avoids hardcoding color codes and ensures swatches show correctly for each product, not just the first:

{%- assign color_option_index = nil -%}

{%- if product.options.size > 0 -%}
  {%- for option in product.options_with_values -%}
    {%- if option.name == 'Color' -%}
      {%- assign color_option_index = forloop.index0 -%}
    {%- endif -%}
  {%- endfor -%}
{%- endif -%}

{%- if color_option_index != nil -%}
  {%- assign seen_colors = '' -%}
  
    {%- for variant in product.variants -%}
      {%- assign color_name = variant.options[color_option_index] -%}
      {%- assign color_hex = variant.metafields.custom.color | metafield_text -%}

      {%- unless seen_colors contains color_name -%}
        {%- assign seen_colors = seen_colors | append: color_name | append: ',' -%}
        {%- if color_hex != blank -%}
          
        {%- else -%}
          {{ color_name }}
        {%- endif -%}
      {%- endunless -%}
    {%- endfor -%}
  

{%- endif -%}

Hi @naio :waving_hand: this is an advanced customization.

You’d only hard code values if your trying to control the sort order because products have variants in an inconsistent order , or have duplication for some reason messing things up in your product architecture making everything more complicated so taking work to fix.

To get around that you need to put some list somewhere of ALL the color values, like a shop level metafield or metaobject, and loop over that to find a current match; and or then also loop over variants depending on setup.

As for only working on one product-card move the code to a snippet and call the snippet inside the collections product loop.

And or check for HTML issues cause by your changes to code, etc.

If you need this customization actually working then contact me for services.
Contact info in forum signature below :down_arrow: :down_arrow: :down_arrow:.
ALWAYS please provide context, examples: store url, theme name, post url(s) , or any further detail in ALL correspondence.

Hi @CodingFifty

The code you posted works like mine except that after the first product card the color names are shown but the colors are not. I’m looking for a different solution, thanks anyway.

Hi @Daniel481

The code you posted works exactly like mine colors are shown only on the first card, not the rest.

Thank you anyway,

The code