Articles on: Seller Cases

Adding Custom Variants Badge to your Product


Add visual labels to specific variant values (e.g., Bestsellers, Limited Edition, or Recommended) on your product page to help customers identify key options quickly.





Important Notes


  • Backup your theme: Always create a duplicate of your theme before editing code.
  • Liquid knowledge: Basic familiarity with Shopify Liquid is required.
  • Support: Contact the support team if you need assistance with this implementation.




Step 1: Create a Product Metafield


Define where the badge data will be stored in your Shopify admin.

  • Navigate to Shopify Admin > Settings > Metafields and metaobjects > Metafields definitions > Products.
  • Click Add definition.
  • Set the Name to: Teeinblue Custom Badges.
  • Set the Namespace and key to exactly: teeinblue.custom_badges.
  • Select Type: JSON.
  • Select One value.
  • Save.



Note: The metafield key must be exactly teeinblue.custom_badges for the script to function.


Tips: You can pin this metafield to quickly access it on your Shopify Admin > Product


Step 2: Add Badge Data to Products


Specify which variants get which badges for your chosen products.


  • Go to Shopify Admin > Products and select a product.
  • Scroll down to the Metafields section at the bottom.
  • In the Teeinblue Custom Badges field, enter your data in JSON format as below
    Key: The exact name of the variant option.
Value: The text you want displayed on the badge.
  • Save.


Example JSON value:


{
"15oz": "Best Seller"
}





Step 3: Add Liquid Code to the Theme


1. Create the Snippet file

  • Go to Online Store > Themes > Edit code.
  • In the left sidebar, locate the Snippets folder and click Add a new snippet.
  • Name the file: teeinblue-custom-badges.liquid.



  • Paste your snippet code into the file and Save.


{% if template.name == 'product' %}
{% assign custom_badges_data = product.metafields.teeinblue.custom_badges.value %}

<script>

/**
1. This variable will define the selectors of the variant picker label
2. You can add more selector to this variable (from the theme's variant picker
- for campaign by Shopify products
3. The below selectors will work for all campaign by Product base
*/
var VARIANT_LABEL_SELECTORS = ['.tee-radio-label', '.tee-clipart-label'];

(function () {
var rawBadgeMap = {{ custom_badges_data | json }};
var badgeMap = {};
var BADGE_ATTR = 'data-variant-badge';
var observer = null;
var isScheduled = false;

try {
badgeMap = typeof rawBadgeMap === 'string'
? JSON.parse(rawBadgeMap || '{}')
: (rawBadgeMap || {});
} catch (e) {
badgeMap = {};
}

function normalizeText(text) {
return String(text || '')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
}

function findBadgeText(labelText) {
var normalizedLabel = normalizeText(labelText);
if (!normalizedLabel) return '';

if (badgeMap[labelText] != null) return String(badgeMap[labelText]);
if (badgeMap[normalizedLabel] != null) return String(badgeMap[normalizedLabel]);

var keys = Object.keys(badgeMap);
for (var i = 0; i < keys.length; i++) {
if (normalizeText(keys[i]) === normalizedLabel) {
return String(badgeMap[keys[i]]);
}
}

return '';
}

function getLabelText(labelEl) {
if (!labelEl) return '';

var clone = labelEl.cloneNode(true);
var badgeEl = clone.querySelector('[' + BADGE_ATTR + '="1"]');
if (badgeEl) badgeEl.remove();

return (clone.textContent || clone.innerText || '')
.replace(/\s+/g, ' ')
.trim();
}

function getBadgeElement(labelEl) {
return labelEl.querySelector('[' + BADGE_ATTR + '="1"]');
}

function createBadgeElement() {
var badgeEl = document.createElement('span');
badgeEl.setAttribute(BADGE_ATTR, '1');
badgeEl.className = 'variant-badge';
return badgeEl;
}

function updateBadge(labelEl, badgeText) {
var badgeEl = getBadgeElement(labelEl);

if (!badgeText) {
if (badgeEl) {
badgeEl.remove();
return true;
}
return false;
}

if (!badgeEl) {
badgeEl = createBadgeElement();
labelEl.appendChild(badgeEl);
}

if (badgeEl.textContent !== badgeText) {
badgeEl.textContent = badgeText;
return true;
}

return !badgeEl.isConnected;
}

function getAllLabels() {
var uniqueLabels = new Map();

for (var i = 0; i < VARIANT_LABEL_SELECTORS.length; i++) {
var selector = VARIANT_LABEL_SELECTORS[i];
var elements = document.querySelectorAll(selector);

for (var j = 0; j < elements.length; j++) {
uniqueLabels.set(elements[j], true);
}
}

return Array.from(uniqueLabels.keys());
}

function disconnectObserver() {
if (observer) observer.disconnect();
}

function connectObserver() {
if (!observer) return;

var root = document.body || document.documentElement;
if (!root) return;

observer.observe(root, {
subtree: true,
childList: true
});
}

function applyBadges() {
if (!badgeMap || typeof badgeMap !== 'object') return;

disconnectObserver();

var labels = getAllLabels();

for (var i = 0; i < labels.length; i++) {
var labelEl = labels[i];
var labelText = getLabelText(labelEl);
var badgeText = findBadgeText(labelText);
updateBadge(labelEl, badgeText);
}

connectObserver();
}

function isRelevantNode(node) {
if (!node || node.nodeType !== 1) return false;
if (node.hasAttribute && node.hasAttribute(BADGE_ATTR)) return false;

for (var i = 0; i < VARIANT_LABEL_SELECTORS.length; i++) {
var selector = VARIANT_LABEL_SELECTORS[i];
if (!selector) continue;

if (node.matches && node.matches(selector)) return true;
if (node.querySelector && node.querySelector(selector)) return true;
}

return false;
}

function queueApply() {
if (isScheduled) return;

isScheduled = true;

requestAnimationFrame(function () {
isScheduled = false;
applyBadges();
});
}

function startObserver() {
observer = new MutationObserver(function (mutations) {
for (var i = 0; i < mutations.length; i++) {
var mutation = mutations[i];

for (var j = 0; j < mutation.addedNodes.length; j++) {
if (isRelevantNode(mutation.addedNodes[j])) {
queueApply();
return;
}
}

for (var k = 0; k < mutation.removedNodes.length; k++) {
if (isRelevantNode(mutation.removedNodes[k])) {
queueApply();
return;
}
}
}
});

connectObserver();
}

function init() {
applyBadges();
startObserver();
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>

<style>
:root {
--variant-badge-top: -12px;
--variant-badge-right: 10px;
--variant-badge-font-size: 12px;
--variant-badge-font-weight: 400;
--variant-badge-line-height: 1.2;
--variant-badge-text-color: #E02020; /* Badges' text color */
--variant-badge-bg: #FFD700; /* Badges' background color */
--variant-badge-border-color: #FFD700; /* Badges' border color color */
--variant-badge-border-width: 1px;
--variant-badge-border-radius: 5px;
--variant-badge-padding-y: 2px;
--variant-badge-padding-x: 4px;
--variant-badge-letter-spacing: 0;
--variant-badge-white-space: nowrap;
}

.variant-badge {
{% comment %} position: absolute; {% endcomment %}
top: var(--variant-badge-top);
right: var(--variant-badge-right);
font-size: var(--variant-badge-font-size);
font-weight: var(--variant-badge-font-weight);
line-height: var(--variant-badge-line-height);
letter-spacing: var(--variant-badge-letter-spacing);
color: var(--variant-badge-text-color);
background: var(--variant-badge-bg);
border: var(--variant-badge-border-width) solid var(--variant-badge-border-color);
border-radius: var(--variant-badge-border-radius);
padding: var(--variant-badge-padding-y) var(--variant-badge-padding-x);
white-space: var(--variant-badge-white-space);
}
.tee-clipart-label .variant-badge{
right: unset !important;
}
.variant-badge {
margin-left: 10px;
}
</style>
{% endif %}


2. Connect the snippet to your Theme

  • Open the Layout folder and select theme.liquid.
  • Locate the closing </head> tag.
  • Paste the following render tag immediately above the </head> tag:


{% render 'teeinblue-custom-badges' %}




3. Customize Styles (Optional)

You can adjust the colors, background, and borders by modifying the CSS variables within the <style> section of your snippet file.





Step 4: Verification


  • Open the product page on your storefront.
  • Verify that badges appear correctly next to the specified variant options.
  • Switch between different variant options to ensure badges update or disappear as expected.

Updated on: 20/03/2026

Was this article helpful?

Share your feedback

Cancel

Thank you!