Hiding swatch colour for products having only one color

Hello Friends,

I would like to display swatch colors only if the product has more than one colour. I have added a block to my product collection page and chose swatches for that block. I can see color swatches for all products irrespective if they have one or more colors.

I tried editing the part of swatch.liquid file where it renders the colors as follows:

{% liquid

assign swatch_value = null

assign extra_classes = ‘’

if settings.show_variant_image and variant_image

assign swatch_image_width = settings.variant_swatch_width | times: 2

assign swatch_image_url = variant_image | image_url: width: swatch_image_width

assign swatch_value = ‘url(’ | append: swatch_image_url | append: ‘)’

assign extra_classes = extra_classes | append: ’ swatch–variant-image’

elsif swatch.image

assign swatch_image_url = swatch.image | image_url: width: 80

assign swatch_value = ‘url(’ | append: swatch_image_url | append: ‘)’

elsif swatch.color

assign swatch_value = ‘rgb(’ | append: swatch.color.rgb | append: ‘)’

endif

if i add:

elsif swatch.color.swatch_value>1

assign swatch_value = ‘rgb(’ | append: swatch.color.rgb | append: ‘)’

this does not work.

Any help with the above?. I am using the horizon theme

Thanks & Regards,

Girish

Just looking at the swatch.color.swatch_value>1, I’d guess that it’s a string, not a number. And not the number of color options which is what you’re looking for?

You’re probably in the wrong file entirely. Swatch.liquid is for the individual swatches. I’d trace where it’s coming from. The product card has the swatches block. That file is called swatches.liquid in the Blocks folder. It’s a block that renders variant-swatches snippet, which renders swatch snippet. So I think your answer would be in variant-swatches snippet. And it already assigns the swatch count, and has a condition for the number.

So I maybe all you need to do is change that to say

{% if swatch_count <= 1 %}
  {% continue %}
{% endif %}

or you could add a color-specific code below it like this

{% if swatch_count == 0 %}
  {% continue %}
{% endif %}

{% if swatch_count == 1 and product_option.name == 'Color' %}
  {% continue %}
{% endif %}

Hi Maximus3,

Yes i figured this out. already did the change and it works fine on desktop mode. Any suggestion on how to configure same on Mobile ?

Your feedback would be much appreciated.

Regards,

Geronim0

I’m not sure why it would be different on mobile.

Technically, if you want to avoid editing theme code (I would!), you can use “Custom CSS” to do this:

.variant-option--swatches:not(:has(label:nth-of-type(2))) {
  display:none;
}

The rule basically says – if variant option wrapper does not have second swatch (meaning – has only one) – hide it.

This code works for all screen sizes.

I am also not so sure it’s a good idea to not output those swatches HTML at all – this may prevent theme Javascript code from working properly.
Have not tested this, but be warned.

Before / After:

Also may want to hide only swatch, but keep the label
.variant-option--swatches:not(:has(label:nth-of-type(2))) label {
  display:none;
}

Hi there,

@Maximus3 is right. I checked the actual file from a Horizon theme and confirmed it: the loop that skips a single-color option is the exact same code path for phones and desktops.

If you are still seeing differennce in mobile and desktop version, reasons:

  1. You previewed the change on the desktop but never saved it.
    Now your mobile devices cannot see the change.

  2. It might be cached in you phone. Try opening it in the incognito/private mode in the browser.

  3. You might have more than one draft/duplicate theme. And you edited one and previewed another. Make sure that in the phone and mobile, (preview url is the same.

Since others have confirmed Horizon uses the same Liquid code path for desktop and mobile, I’d compare the rendered HTML before making any more code changes.

A quick way to do that is to open your browser’s device toolbar, inspect the product card, and check whether the swatch wrapper is still being rendered on the mobile layout. If the HTML is the same but the UI behaves differently, it’s probably CSS or JavaScript. If the HTML differs, then something else is affecting how the card is being rendered.

I’d also verify the basics first:

  • Make sure you’re previewing the same saved theme on both desktop and mobile.

  • Try an incognito/private window on your phone to rule out browser cache.

  • Compare the rendered HTML between desktop and mobile before changing the theme again.

That should help narrow down whether you’re dealing with a rendering issue or simply a preview/cache problem.

Hi Ajaycodewiz,

My apologize. I was checking on the shopify app. When i used a browser on mobile, i could see the exact changes appearing on the desktop mode. All good.

I just want to add a conditional formatting that will hide the text ‘Available Colours’ for those products having only one colour. At the moment, the text is appearing on all products.

Any idea how to go about this?

Thanks & Regards,

Girish

Hi cuongnm_trooix

My apologize. I was checking on the shopify app. When i used a browser on mobile, i could see the exact changes appearing on the desktop mode. All good.

I just want to add a conditional formatting that will hide the text ‘Available Colours’ for those products having only one colour. At the moment, the text is appearing on all products.

Any idea how to go about this?

Thanks & Regards,

Girish

Use Custom CSS (no code file edits):
Online Store > Themes > Customize > Theme settings > Custom CSS:

.variant-option--swatches:not(:has(label:nth-of-type(2))) legend {
  display: none;
}

It Hides the label only when there’s exactly one swatch. Products with 2+ colors keep the label as normal.

Or,
open snippets/variant-main-picker.liquid, find the <legend> , and change it to:

<legend{% if product_option.values.size == 1 %} class="visually-hidden"{% endif %}>
  {{ product_option.name | escape -}}

Note: to remove the search color, use the code given in other solutions

Regards,
Ajay from Pinflow

Hi Ajay,

I tried the CSS edit but it does not work. I manually added a text above the swatches for info. Is this the issue here ?

Please advise.

Thanks & Regards,

Geronim0

Hi @Geronim0

The swatch.liquid file only renders each individual swatch. It doesn’t know how many color variants the product has, so checking something like swatch.color.swatch_value > 1 won’t work because swatch_value is a color value, not a count.

Instead, you need to check the number of values for the Color option before rendering the swatches.

For example, in the code where the swatches are rendered, you can add a condition similar to this:

{% assign color_option = product.options_with_values | where: 'name', 'Color' | first %}

{% if color_option and color_option.values.size > 1 %}
  {# Render swatches here #}
{% endif %}

Or, if you’re looping through product options:

{% if option.name == 'Color' and option.values.size > 1 %}
  {# Render color swatches #}
{% endif %}

Since you’re using the Horizon theme, the exact file that renders the product card swatches may not be swatch.liquid. It could be in a snippet such as card-product.liquid, product-card.liquid, or another snippet that calls swatch.liquid.

If you’re not sure where to add the condition, let me know which version of the Horizon theme you’re using, and I can point you to the exact file and location.

Best regards,
Devcoder :laptop:

The :has() approach only works if the label and the swatches have a parent/child relationship that CSS can actually evaluate. Since “Available Colours” is manually added, it sounds like it’s outside that structure, so the selector never matches.

I’d move that text into the same Liquid condition that’s deciding whether to render the swatches. For example, if you’re already checking something like swatch_count > 1 (or the Color option has more than one value), wrap both the heading and the swatch markup in the same {% if %} block instead of trying to hide the heading afterward with CSS.

That keeps the markup cleaner and avoids relying on browser support or DOM structure. If you’re not sure where the heading was added, post that snippet and we can point to the exact place to wrap it.

Hey @Geronim0 ,

I don’t think swatch.color.swatch_value represents the number of available colors. It’s the value used to render the swatch (for example, an RGB value or image), so comparing it with > 1 won’t work.

You’ll likely need to check how many values the product’s Color option has before rendering the swatches, rather than checking the swatch itself.

Just to clarify are you modifying the swatches on the product cards in the collection grid or the variant picker on the product page? In the Horizon theme the rendering logic may be in different files depending on where the swatches are displayed, and that will help narrow down the correct solution.

Thank You !

Hello Steve,

Apologies for the late response. I added a block on the product’s collection page and chose swatches. I have managed to fix the issue by editing the swatches.liquid file and changing the offset to >1. Now swatches are only displayed for products having more than one colour.

However, while adding the swatches only the colors appeared at the bottom of the product. I was expecting a label name color. I had to input this manually and this is available on all products. I would like it not to display at all for products having a single colour.

How can i achieve this?

{%- liquid

assign block_settings = block.settings

for product_option in closest.product.options_with_values

assign swatch_count = product_option.values | map: ‘swatch’ | compact | size

if swatch_count > 1

assign product_has_swatches = true

endif

endfor

-%}

Glad to hear you managed to hide the swatches for single color products.

For the “Color” label, you’ll need to apply the same condition that’s controlling the swatches. At the moment, it sounds like the label is being rendered independently, so even when the swatches are hidden, the heading is still displayed.

I’d look for where the label (for example, {{ product_option.name }} or a hardcoded “Color” text) is rendered and wrap it in the same condition:

{% if swatch_count > 1 %}

<!-- Render the Color label -->
<!-- Render the Color label -->

{% endif %}

If the label is rendered in a different snippet or section, you’ll need to apply the same logic there as well.

If you’re not sure where it’s coming from, feel free to share that part of your swatches.liquid file (or the relevant snippet), and I’d be happy to help point you to the exact line that needs updating.

Thank you!

Hi Steve,

When i select the text and click on edit code it brings me to text.liquid. Is this what you are referring to ?.

{% render ‘text’, block: block %}

{% schema %}

{

“name”: “t:names.text”,

“tag”: null,

“settings”: [

{

“type”: “richtext”,

“id”: “text”,

“label”: “t:settings.text”

},

{

“type”: “header”,

“content”: “t:content.layout”

},

{

“type”: “select”,

“id”: “width”,

“label”: “t:settings.width”,

“options”: [

    {

“value”: “fit-content”,

“label”: “t:options.fit”

    },

    {

“value”: “100%”,

“label”: “t:options.fill”

    }

  \],

“default”: “fit-content”

},

{

“type”: “select”,

“id”: “max_width”,

“label”: “t:settings.max_width”,

“options”: [

    {

“value”: “narrow”,

“label”: “t:options.narrow”

    },

    {

“value”: “normal”,

“label”: “t:options.normal”

    },

    {

“value”: “none”,

“label”: “t:options.none”

    }

  \],

“default”: “normal”

},

{

“type”: “text_alignment”,

“id”: “alignment”,

“label”: “t:settings.alignment”,

“default”: “left”,

“visible_if”: “{{ block.settings.width == ‘100%’ }}”

},

{

“type”: “header”,

“content”: “t:content.typography”

},

{

“type”: “select”,

“id”: “type_preset”,

“label”: “t:settings.preset”,

“options”: [

    {

“value”: “rte”,

“label”: “t:options.default”

    },

    {

“value”: “paragraph”,

“label”: “t:options.paragraph”

    },

    {

“value”: “h1”,

“label”: “t:options.h1”

    },

    {

“value”: “h2”,

“label”: “t:options.h2”

    },

    {

“value”: “h3”,

“label”: “t:options.h3”

    },

    {

“value”: “h4”,

“label”: “t:options.h4”

    },

    {

“value”: “h5”,

“label”: “t:options.h5”

    },

    {

“value”: “h6”,

“label”: “t:options.h6”

    },

    {

“value”: “custom”,

“label”: “t:options.custom”

    }

  \],

“default”: “rte”,

“info”: “t:info.edit_presets_in_theme_settings”

},

{

“type”: “select”,

“id”: “font”,

“label”: “t:settings.font”,

“options”: [

    {

“value”: “var(–font-body–family)”,

“label”: “t:options.body”

    },

    {

“value”: “var(–font-subheading–family)”,

“label”: “t:options.subheading”

    },

    {

“value”: “var(–font-heading–family)”,

“label”: “t:options.heading”

    },

    {

“value”: “var(–font-accent–family)”,

“label”: “t:options.accent”

    }

  \],

“default”: “var(–font-body–family)”,

“visible_if”: “{{ block.settings.type_preset == ‘custom’ }}”

},

{

“type”: “select”,

“id”: “font_size”,

“label”: “t:settings.size”,

“options”: [

    {

“value”: “”,

“label”: “t:options.default”

    },

    {

“value”: “0.625rem”,

“label”: “10px”

    },

    {

“value”: “0.75rem”,

“label”: “12px”

    },

    {

“value”: “0.875rem”,

“label”: “14px”

    },

    {

“value”: “1rem”,

“label”: “16px”

    },

    {

“value”: “1.125rem”,

“label”: “18px”

    },

    {

“value”: “1.25rem”,

“label”: “20px”

    },

    {

“value”: “1.5rem”,

“label”: “24px”

    },

    {

“value”: “2rem”,

“label”: “32px”

    },

    {

“value”: “2.5rem”,

“label”: “40px”

    },

    {

“value”: “3rem”,

“label”: “48px”

    },

    {

“value”: “3.5rem”,

“label”: “56px”

    },

    {

“value”: “4.5rem”,

“label”: “72px”

    },

    {

“value”: “5.5rem”,

“label”: “88px”

    },

    {

“value”: “7.5rem”,

“label”: “120px”

    },

    {

“value”: “9.5rem”,

“label”: “152px”

    },

    {

“value”: “11.5rem”,

“label”: “184px”

    }

  \],

“default”: “1rem”,

“visible_if”: “{{ block.settings.type_preset == ‘custom’ }}”

},

{

“type”: “select”,

“id”: “line_height”,

“label”: “t:settings.line_height”,

“options”: [

    {

“value”: “tight”,

“label”: “t:options.tight”

    },

    {

“value”: “normal”,

“label”: “t:options.normal”

    },

    {

“value”: “loose”,

“label”: “t:options.loose”

    }

  \],

“default”: “normal”,

“visible_if”: “{{ block.settings.type_preset == ‘custom’ }}”

},

{

“type”: “select”,

“id”: “letter_spacing”,

“label”: “t:settings.letter_spacing”,

“options”: [

    {

“value”: “tight”,

“label”: “t:options.tight”

    },

    {

“value”: “normal”,

“label”: “t:options.normal”

    },

    {

“value”: “loose”,

“label”: “t:options.loose”

    }

  \],

“default”: “normal”,

“visible_if”: “{{ block.settings.type_preset == ‘custom’ }}”

},

{

“type”: “select”,

“id”: “case”,

“label”: “t:settings.case”,

“options”: [

    {

“value”: “none”,

“label”: “t:options.default”

    },

    {

“value”: “uppercase”,

“label”: “t:options.uppercase”

    }

  \],

“default”: “none”,

“visible_if”: “{{ block.settings.type_preset == ‘custom’ }}”

},

{

“type”: “select”,

“id”: “wrap”,

“label”: “t:settings.wrap”,

“options”: [

    {

“value”: “pretty”,

“label”: “t:options.pretty”

    },

    {

“value”: “balance”,

“label”: “t:options.balance”

    },

    {

“value”: “nowrap”,

“label”: “t:options.none”

    }

  \],

“visible_if”: “{{ block.settings.type_preset == ‘custom’ }}”

},

{

“type”: “header”,

“content”: “t:content.appearance”

},

{

“type”: “color”,

“id”: “text_color”,

“label”: “t:settings.text_color”,

“placeholder”: “t:settings.default”

},

{

“type”: “checkbox”,

“id”: “background”,

“label”: “t:settings.background”,

“default”: false

},

{

“type”: “color”,

“id”: “background_color”,

“label”: “t:settings.background_color”,

“alpha”: true,

“default”: “#00000026”,

“visible_if”: “{{ block.settings.background }}”

},

{

“type”: “range”,

“id”: “corner_radius”,

“label”: “t:settings.corner_radius”,

“default”: 0,

“min”: 0,

“max”: 50,

“step”: 1,

“visible_if”: “{{ block.settings.background }}”

},

{

“type”: “header”,

“content”: “t:content.padding”

},

{

“type”: “range”,

“id”: “padding-block-start”,

“label”: “t:settings.top”,

“min”: 0,

“max”: 100,

“step”: 1,

“unit”: “px”,

“default”: 0

},

{

“type”: “range”,

“id”: “padding-block-end”,

“label”: “t:settings.bottom”,

“min”: 0,

“max”: 100,

“step”: 1,

“unit”: “px”,

“default”: 0

},

{

“type”: “range”,

“id”: “padding-inline-start”,

“label”: “t:settings.left”,

“min”: 0,

“max”: 100,

“step”: 1,

“unit”: “px”,

“default”: 0

},

{

“type”: “range”,

“id”: “padding-inline-end”,

“label”: “t:settings.right”,

“min”: 0,

“max”: 100,

“step”: 1,

“unit”: “px”,

“default”: 0

}

],

“presets”: [

{

“name”: “t:names.text”,

“category”: “t:categories.basic”,

“settings”: {

“text”: “t:html_defaults.make_things_better_extended”

  }

},

{

“name”: “t:names.heading”,

“category”: “t:categories.basic”,

“settings”: {

“text”: “t:html_defaults.new_arrivals_h2”

  }

}

]

}

{% endschema %}

Thanks for sharing the code. It looks like this is the schema section, which only defines the block settings for the Theme Editor. It doesn’t contain the markup that’s rendering the “Color” label.

Could you share the part of the swatches.liquid file (or the relevant snippet) where the swatches and the “Color” label are actually being rendered? That’s the section where the condition will need to be applied.

Once you share that part of the code, I’d be happy to point you to the exact line that needs updating.

Hi Steve. Please refer to the below for the swaches.liquid code:

{%- liquid

assign block_settings = block.settings

for product_option in closest.product.options_with_values

assign swatch_count = product_option.values | map: ‘swatch’ | compact | size

if swatch_count > 1

assign product_has_swatches = true

endif

endfor

-%}

{% stylesheet %}

product-swatches {

width: 100%;

display: flex;

position: relative;

overflow: hidden;

gap: 0;

flex-shrink: 0;

}

{% endstylesheet %}

{% if product_has_swatches %}

<product-swatches

data-product-id=“{{ closest.product.id }}”

data-product-url=“{{ closest.product.url }}”

style="

  --overflow-list-alignment: {{ block_settings.product_swatches_alignment }};

  --overflow-list-alignment-mobile: {{ block_settings.product_swatches_alignment_mobile }};

  --product-swatches-alignment: {% if block_settings.product_swatches_alignment == 'flex-start' %}start{% elsif block_settings.product_swatches_alignment == 'flex-end' %}end{% else %}center{% endif %};

  --product-swatches-alignment-mobile: {% if block_settings.product_swatches_alignment_mobile == 'flex-start' %}start{% elsif block_settings.product_swatches_alignment_mobile == 'flex-end' %}end{% else %}center{% endif %};

  --product-swatches-padding-block-start: {{ block_settings.product_swatches_padding_top }}px;

  --product-swatches-padding-block-end: {{ block_settings.product_swatches_padding_bottom }}px;

  --product-swatches-padding-inline-start: {{ block_settings.product_swatches_padding_left }}px;

  --product-swatches-padding-inline-end: {{ block_settings.product_swatches_padding_right }}px;

"

{{ block.shopify_attributes }}

>

{% render ‘variant-swatches’, product_resource: closest.product %}

{% endif %}

{% schema %}

{

“name”: “t:names.swatches”,

“tag”: null,

“settings”: [

{

“type”: “paragraph”,

“content”: “t:content.resource_reference_product_swatches”

},

{

“type”: “select”,

“id”: “product_swatches_alignment”,

“label”: “t:settings.alignment”,

“options”: [

    {

“value”: “flex-start”,

“label”: “t:options.left”

    },

    {

“value”: “center”,

“label”: “t:options.center”

    },

    {

“value”: “flex-end”,

“label”: “t:options.right”

    }

  \],

“default”: “flex-start”

},

{

“type”: “select”,

“id”: “product_swatches_alignment_mobile”,

“label”: “t:settings.alignment_mobile”,

“options”: [

    {

“value”: “flex-start”,

“label”: “t:options.left”

    },

    {

“value”: “center”,

“label”: “t:options.center”

    },

    {

“value”: “flex-end”,

“label”: “t:options.right”

    }

  \],

“default”: “flex-start”

},

{

“type”: “header”,

“content”: “t:content.padding”,

“visible_if”: “{{ block.settings.hide_padding == false }}”

},

{

“type”: “checkbox”,

“id”: “hide_padding”,

“label”: “t:settings.hide_padding”,

“default”: false,

“visible_if”: “{{ false }}”

},

{

“type”: “range”,

“id”: “product_swatches_padding_top”,

“label”: “t:settings.top”,

“min”: 0,

“max”: 50,

“step”: 1,

“unit”: “px”,

“default”: 4,

“visible_if”: “{{ block.settings.hide_padding == false }}”

},

{

“type”: “range”,

“id”: “product_swatches_padding_bottom”,

“label”: “t:settings.bottom”,

“min”: 0,

“max”: 50,

“step”: 1,

“unit”: “px”,

“default”: 0,

“visible_if”: “{{ block.settings.hide_padding == false }}”

},

{

“type”: “range”,

“id”: “product_swatches_padding_left”,

“label”: “t:settings.left”,

“min”: 0,

“max”: 50,

“step”: 1,

“unit”: “px”,

“default”: 0,

“visible_if”: “{{ block.settings.hide_padding == false }}”

},

{

“type”: “range”,

“id”: “product_swatches_padding_right”,

“label”: “t:settings.right”,

“min”: 0,

“max”: 50,

“step”: 1,

“unit”: “px”,

“default”: 0,

“visible_if”: “{{ block.settings.hide_padding == false }}”

}

],

“presets”: [

{

“name”: “t:names.swatches”,

“category”: “t:categories.product”

}

]

}

{% endschema %}

Thanks for sharing the code. I had a closer look, and this file only checks whether the swatches should be displayed before rendering the variant-swatches snippet.

From what I can see the “Color” label isn’t actually being rendered here so updating this file alone won’t hide it.

Could you share the variant-swatches.liquid snippet (or at least the part where the swatches and the “Color” label are rendered)? That’s most likely where the label is being generated, and we can then apply the same condition so it only appears when a product has more than one color.

That should help us pinpoint the exact line that needs to be updated.
I hope this helps you now.