Search for products, concerns and keywords.


Your Brand Consultant Agreement has been successfully renewed.
There was a problem processing your Brand Consultant Agreement renewal. Please try again.
There was a system error. Please try again.
There was an issue adding this product. Please refresh the page and try again

Shop All Haircare

Volume+

Our Volume+ Haircare Regimen gets to the root of thin hair using the right ingredients, in the right order to boost thickness + repair damage for more volume. Save 20% when you buy a regimen versus purchasing each product individually.

Smooth+

Our Smooth+ Haircare Regimen gets to the root of dry, frizzy hair using the right ingredients, in the right order to replenish + repair for smoother, shinier hair. Save 20% when you buy a regimen versus purchasing each product individually.

Moisture+

Our Moisture+ Haircare Regimen gets to the root of dry, brittle hair using the right ingredients, in the right order to condition, repair + strengthen for soft, healthy-looking tresses.

Styling Treatments

Our lightweight styling treatments nourish, protect + refresh hair instantly, to help you achieve the best hair of your life.

Common Questions About Volume

What causes flat hair?

Numerous factors can contribute to flat hair, such as using harsh shampoos containing sulfates, chemical treatments like coloring or bleaching, and even sleeping on your hair without a protective style. Other common causes of flatness include product buildup, extreme heat styling, using the wrong type of brush or comb, and over-brushing hair that is prone to tangles.

 

In addition to these external factors, genetics play a role in how much body and volume you have in your hair. If you have naturally fine or thin strands, they may not have enough strength to hold shape and can easily become flat. But even if your hair type makes you more susceptible to flatness, avoiding many of the above-mentioned triggers can help combat this issue.

How do I add volume to my curly hair?

Adding volume to curly hair requires a multifaceted approach that combines both hair care and styling techniques. On the hair care side, you'll want to use products with nourishing ingredients, such as amino acids, silk peptides, acetyl glutamine, and bio ceramide molecules. Stick to a sulfate-free shampoo to avoid stripping your hair of its natural oils, and condition regularly to add moisture and strength.

 

When styling, opt for products that don't weigh down the strands, such as light mousses, multi-tasking oils and leave-in conditioners. Additionally, you can use a styling cream specifically designed for curly hair to help define your curls and enhance their shape. After styling, use a diffuser attachment on your blow dryer to help shape your curls without over-drying them. Finally, finish with a light-hold hair spray to keep your look in place all day long. When using the Volume+ Regimen, you can layer in the Define+ Curl Cream to clean hair to help hydrate and define your curls for a healthy bounce or use on dry hair to refresh and redefine curls.

How do I add volume to straight hair?

Straight hair is notoriously difficult to add volume to, but with the right hair care and styling techniques, it's possible to achieve a fuller look. Start by using a volumizing shampoo and conditioner that contains nourishing ingredients, such as silk peptides, bio ceremide molecules, amino acids, and acetyl glutamine. You should also use a sulfate-free shampoo to avoid stripping away the hair's natural oils, which add vital volume and shine.

 

When styling, use products that won't weigh down the hair strands. Volumizing mousses, creams, and sprays are good options. The Rodan + Fields Densifying+ Treatment is lightweight and fast-absorbing and replenishes and nourishes as it helps to maintain a healthy scalp. It also repairs and prevents damage for hair that looks fuller, thicker and healthier. Additionally, try using a round brush while blow-drying to add lift to the root area. For an extra boost of volume, consider applying our Refresh+ Dry Shampoo to lift hair from the roots.

Why is my hair so flat after washing?

Ideally, shampoo should clean your hair of the unwanted buildup that weighs it down without removing too much of the natural oil that gives volume to the hair. Unfortunately, many shampoos are formulated with harsh ingredients like sulfates, which can strip away the moisture and oils from the hair shaft, leaving it looking flat and dull. To prevent this problem, look for a sulfate-free shampoo.

 

Your post-washing habits may also be contributing to flat locks. For instance, using a rough towel to dry the hair or brushing it too vigorously can damage the cuticles which might cause breakage and make the strands look limp. Additionally, using styling products with too much hold can weigh the hair down and make it look flat.

Why does shampoo make my hair flat?

Many shampoos contain sulfates, which are known to strip away the natural oils that give body and volume to the hair. If you're using a shampoo that contains sulfates, it may be removing too much of this beneficial oil, resulting in dry, flat, limp locks.

 

To avoid this issue, look for shampoos that are sulfate-free and contain nourishing ingredients like amino acids and bio-ceramide molecules. These types of shampoos are designed to cleanse the hair without stripping away its natural moisture, leaving it looking fuller, bouncier, and more voluminous. Additionally, consider adding a high-quality conditioner or leave-in treatment to your routine that will help protect against shampoo damage.

Why is my hair so flat?

Flat and lifeless hair may be caused by a combination of factors. One likely culprit is product buildup. Styling products like gel and hairspray can build up on the scalp over time, weighing the hair down and making it look dull. Another possible cause is excess oils on your roots creating heavier strands, preventing volume. Also, if your scalp is overly oily this might cause your hair to look heavy and flat. We recommend using our Refresh+ Dry Shampoo between wash days to absorb oil, add volume and balance the scalp for refreshed hair between washes.

 

Another likely cause is damage from heat-styling tools such as flat irons, curling wands, and blow-dryers. When exposed to high temperatures, the hair can become brittle and weak, resulting in limp locks. If you're using heat-styling tools regularly, it's important to always use a heat-protectant product before styling and to try not to exceed 350°F. When using the Volume+ Regimen, you can layer in the Defrizz+ Oil Treatment to nourish and protect strands.

Common Questions About Volume

What causes flat hair?

Numerous factors can contribute to flat hair, such as using harsh shampoos containing sulfates, chemical treatments like coloring or bleaching, and even sleeping on your hair without a protective style. Other common causes of flatness include product buildup, extreme heat styling, using the wrong type of brush or comb, and over-brushing hair that is prone to tangles.

 

In addition to these external factors, genetics play a role in how much body and volume you have in your hair. If you have naturally fine or thin strands, they may not have enough strength to hold shape and can easily become flat. But even if your hair type makes you more susceptible to flatness, avoiding many of the above-mentioned triggers can help combat this issue.

How do I add volume to my curly hair?

Adding volume to curly hair requires a multifaceted approach that combines both hair care and styling techniques. On the hair care side, you'll want to use products with nourishing ingredients, such as amino acids, silk peptides, acetyl glutamine, and bio ceramide molecules. Stick to a sulfate-free shampoo to avoid stripping your hair of its natural oils, and condition regularly to add moisture and strength.

 

When styling, opt for products that don't weigh down the strands, such as light mousses, multi-tasking oils and leave-in conditioners. Additionally, you can use a styling cream specifically designed for curly hair to help define your curls and enhance their shape. After styling, use a diffuser attachment on your blow dryer to help shape your curls without over-drying them. Finally, finish with a light-hold hair spray to keep your look in place all day long. When using the Volume+ Regimen, you can layer in the Define+ Curl Cream to clean hair to help hydrate and define your curls for a healthy bounce or use on dry hair to refresh and redefine curls.

How do I add volume to straight hair?

Straight hair is notoriously difficult to add volume to, but with the right hair care and styling techniques, it's possible to achieve a fuller look. Start by using a volumizing shampoo and conditioner that contains nourishing ingredients, such as silk peptides, bio ceremide molecules, amino acids, and acetyl glutamine. You should also use a sulfate-free shampoo to avoid stripping away the hair's natural oils, which add vital volume and shine.

 

When styling, use products that won't weigh down the hair strands. Volumizing mousses, creams, and sprays are good options. The Rodan + Fields Densifying+ Treatment is lightweight and fast-absorbing and replenishes and nourishes as it helps to maintain a healthy scalp. It also repairs and prevents damage for hair that looks fuller, thicker and healthier. Additionally, try using a round brush while blow-drying to add lift to the root area. For an extra boost of volume, consider applying our Refresh+ Dry Shampoo to lift hair from the roots.

Why is my hair so flat after washing?

Ideally, shampoo should clean your hair of the unwanted buildup that weighs it down without removing too much of the natural oil that gives volume to the hair. Unfortunately, many shampoos are formulated with harsh ingredients like sulfates, which can strip away the moisture and oils from the hair shaft, leaving it looking flat and dull. To prevent this problem, look for a sulfate-free shampoo.

 

Your post-washing habits may also be contributing to flat locks. For instance, using a rough towel to dry the hair or brushing it too vigorously can damage the cuticles which might cause breakage and make the strands look limp. Additionally, using styling products with too much hold can weigh the hair down and make it look flat.

Why does shampoo make my hair flat?

Many shampoos contain sulfates, which are known to strip away the natural oils that give body and volume to the hair. If you're using a shampoo that contains sulfates, it may be removing too much of this beneficial oil, resulting in dry, flat, limp locks.

 

To avoid this issue, look for shampoos that are sulfate-free and contain nourishing ingredients like amino acids and bio-ceramide molecules. These types of shampoos are designed to cleanse the hair without stripping away its natural moisture, leaving it looking fuller, bouncier, and more voluminous. Additionally, consider adding a high-quality conditioner or leave-in treatment to your routine that will help protect against shampoo damage.

Why is my hair so flat?

Flat and lifeless hair may be caused by a combination of factors. One likely culprit is product buildup. Styling products like gel and hairspray can build up on the scalp over time, weighing the hair down and making it look dull. Another possible cause is excess oils on your roots creating heavier strands, preventing volume. Also, if your scalp is overly oily this might cause your hair to look heavy and flat. We recommend using our Refresh+ Dry Shampoo between wash days to absorb oil, add volume and balance the scalp for refreshed hair between washes.

 

Another likely cause is damage from heat-styling tools such as flat irons, curling wands, and blow-dryers. When exposed to high temperatures, the hair can become brittle and weak, resulting in limp locks. If you're using heat-styling tools regularly, it's important to always use a heat-protectant product before styling and to try not to exceed 350°F. When using the Volume+ Regimen, you can layer in the Defrizz+ Oil Treatment to nourish and protect strands.

(function() { 'use strict'; // ============================================ // CONFIGURATION - EDIT THESE VALUES // ============================================ const marketingCards = [ // ============================================ // MARKETING CARDS - EVERGREEN - "Lash Day Offer" // ============================================ { // URLS to match for immediate render urlMatches: ['beauty-enhancements','lash-serum-and-eyebrow-tint'], // PLACEMENT: Choose ONE method (comment out the others) insertAtPosition: 3, // Insert AFTER this position in the grid // insertAfterProductCode: 'HRVRGG01', // Insert AFTER this CAT ID // insertBeforeProductCode: 'HAAGR', // OR insert BEFORE this CAT ID // POSITION: 'Start' or 'End' (if not set, uses insertAtPosition. If insertAtPosition exceeds product count, defaults to 'End') // placement: 'End', // 'Start' or 'End' titleText: 'YOUR LASHES.
BUT BETTER.
', bodyText: 'Celebrate National Lash Day with a boost. Get 20% off Lash Boost + a FREE Mini Eye Cream, for a limited time.', ctaText: 'USE CODE LASHDAY', ctaHref: '/shop/r-f-lash-boost/p/ENHLSH01', cardHref: '/shop/r-f-lash-boost/p/ENHLSH01', disclaimerText: '', textShadowEnabled: false, // BACKGROUND: Image URL or null for solid color backgroundImage: 'https://www.rodanandfields.com/en-us/medias/LASH-DAY-marketing-module-horizontal-image.jpg?context=bWFzdGVyfGltYWdlc3wxMTMwOTh8aW1hZ2UvanBlZ3xhRGczTDJneFppOHlPRFExTVRReE1UY3lNakkzTUM5TVFWTklMVVJCV1MxdFlYSnJaWFJwYm1jdGJXOWtkV3hsWDJodmNtbDZiMjUwWVd4ZmFXMWhaMlV1YW5CbnxhNTZkMTEyODgxNzdhM2NkNGQxODNkYmRjNWRkNGUyZDVjYjNmYjRlYWQxZjc0NjRhMGJjMjZjZjI5N2Y2YTI0', backgroundColor: '#FFFFFF', // Used only if backgroundImage is null copyBackgroundColor: '#FFFFFF', splitImageHalf: true, // LAYOUT copyAlign: 'left' }, // ============================================ // MARKETING CARDS - EVERGREEN - "Pure C Promo MC" // ============================================ { // URLS to match for immediate render urlMatches: ['best-sellers','redefine','skin-care-regimens'], // PLACEMENT: Choose ONE method (comment out the others) insertAtPosition: 3, // Insert AFTER this position in the grid // insertAfterProductCode: 'HRVRGG01', // Insert AFTER this CAT ID // insertBeforeProductCode: 'HAAGR', // OR insert BEFORE this CAT ID // POSITION: 'Start' or 'End' (if not set, uses insertAtPosition. If insertAtPosition exceeds product count, defaults to 'End') // placement: 'End', // 'Start' or 'End' titleText: 'LIMITED-TIME: PURE C EXCLUSIVE OFFERS', bodyText: 'Pair with Active Hydration Serum and save $45 or buy Pure C, get a complimentary Hydra Mask ($54 Value)', ctaText: 'Shop Now', ctaHref: '/category/face-serums', cardHref: '/category/face-serums', disclaimerText: 'Valid thru 2/28/26. See Terms & Conditions', textShadowEnabled: false, // BACKGROUND: Image URL or null for solid color backgroundImage: 'https://www.rodanandfields.com/en-us/medias/Pure-C-Promo-Marketing-Module.jpg?context=bWFzdGVyfGltYWdlc3w2NTk0MHxpbWFnZS9qcGVnfGFESXdMMmhoWmk4eU9ETTVOems1TnpjeE9UVTRNaTlRZFhKbExVTXRVSEp2Ylc4dFRXRnlhMlYwYVc1bkxVMXZaSFZzWlM1cWNHY3xhNmFmZWY2YjU3MzM5ZWY2NWNlN2Q5Njk1MWFjYTY1MzE0MWI4NzQ3OWY1YWUyNTM0NDY4YzUwMzcyYzcwY2Mz', backgroundColor: '#FFFFFF', // Used only if backgroundImage is null copyBackgroundColor: '#FFFFFF', splitImageHalf: true, // LAYOUT copyAlign: 'left' }, // ============================================ // MARKETING CARDS - EVERGREEN - "Pure C Evergreen MC" // ============================================ { // URLS to match for immediate render urlMatches: ['face-serums','anti-aging-skin-care'], // PLACEMENT: Choose ONE method (comment out the others) insertAtPosition: 3, // Insert AFTER this position in the grid // insertAfterProductCode: 'HRVRGG01', // Insert AFTER this CAT ID // insertBeforeProductCode: 'HAAGR', // OR insert BEFORE this CAT ID // POSITION: 'Start' or 'End' (if not set, uses insertAtPosition. If insertAtPosition exceeds product count, defaults to 'End') // placement: 'End', // 'Start' or 'End' titleText: 'NEW PURE C SERUM', bodyText: 'Activate your skin’s longevity for firmer, smoother, brighter skin...now and for years to come', ctaText: 'Learn More', ctaHref: '/discover/purec', cardHref: '/discover/purec', disclaimerText: '', textShadowEnabled: false, // BACKGROUND: Image URL or null for solid color backgroundImage: 'https://www.rodanandfields.com/en-us/medias/Pure-C-Evergreen-Marketing-Module.jpg?context=bWFzdGVyfGltYWdlc3w5MjQxOXxpbWFnZS9qcGVnfGFEVXhMMmhoTlM4eU9ETTVOems1TnpRMU56UXpPQzlRZFhKbExVTXRSWFpsY21keVpXVnVMVTFoY210bGRHbHVaeTFOYjJSMWJHVXVhbkJufDNkZTgzNTUzYTFjY2QwMjY2OWVlZjljODYwZmNjYWY5YzRmNGFjNWE2Y2I3YjM4YjczNTdjODhmOTQzZDcwMDY', backgroundColor: '#FFFFFF', // Used only if backgroundImage is null copyBackgroundColor: '#FFFFFF', splitImageHalf: true, // LAYOUT copyAlign: 'left' }, // ============================================ // MARKETING CARDS - EVERGREEN - "Money Back Guarantee" // ============================================ { // URLS to match for immediate render urlMatches: ['exfoliants','eye-creams-and-treatments','neck-and-decollete','reverse','soothe','unblemish'], // PLACEMENT: Choose ONE method (comment out the others) insertAtPosition: 3, // Insert AFTER this position in the grid // insertAfterProductCode: 'HRVRGG01', // Insert AFTER this CAT ID // insertBeforeProductCode: 'HAAGR', // OR insert BEFORE this CAT ID // POSITION: 'Start' or 'End' (if not set, uses insertAtPosition. If insertAtPosition exceeds product count, defaults to 'End') // placement: 'End', // 'Start' or 'End' titleText: 'Love it or get your money back.', bodyText: 'We stand behind our science, formulas + results with a 60-Day Money Back Guarantee.', ctaText: 'Learn More', ctaHref: '/satisfaction-guarantee', cardHref: '/satisfaction-guarantee', disclaimerText: '', textShadowEnabled: false, // BACKGROUND: Image URL or null for solid color backgroundImage: 'https://www.rodanandfields.com/en-us/medias/60-day-Guarantee.jpg?context=bWFzdGVyfGltYWdlc3wyNDc0OTd8aW1hZ2UvanBlZ3xhREppTDJnek1TOHlPRE01TnprNU5qRTBOamN4T0M4Mk1DMWtZWGt0UjNWaGNtRnVkR1ZsTG1wd1p3fDM1ZDU3NGEwZDllNWMxZDUwYzk1NDJhZjMwNGZkM2Y5NGEwMmM3NTQ2MzhjYTI0ZDk2NWQ4ZjJlOTY2NTJmYWE', backgroundColor: '#F5F4F2', // Used only if backgroundImage is null copyBackgroundColor: '#F5F4F2', splitImageHalf: true, // LAYOUT copyAlign: 'left' }, // ============================================ // MARKETING CARDS - EVERGREEN - "#1 Female Dermatologist Founded Skincare Brand" // ============================================= { // URLS to match for immediate render urlMatches: ['acne-pores-blackheads-skin-care','dry-skin-care','skin-care-essentials','face-moisturizers','lip-care-and-color','sensitive-skin-care','sun-protection','toners','uneven-skin-tone-and-dark-marks-skin-care'], // PLACEMENT: Choose ONE method (comment out the others) insertAtPosition: 3, // Insert AFTER this position in the grid // insertAfterProductCode: 'HRVRGG01', // Insert AFTER this CAT ID // insertBeforeProductCode: 'HAAGR', // OR insert BEFORE this CAT ID // POSITION: 'Start' or 'End' (if not set, uses insertAtPosition. If insertAtPosition exceeds product count, defaults to 'End') // placement: 'End', // 'Start' or 'End' titleText: '#1 Female Dermatologist Founded Skincare Brand in the U.S.*', bodyText: '', ctaText: 'Learn More', ctaHref: '/nx/our-story', cardHref: '/nx/our-story', disclaimerText: '*Source Euromonitor Int’l Ltd. 2024; See rodanandfields.com for details.', textShadowEnabled: false, // BACKGROUND: Image URL or null for solid color backgroundImage: 'https://www.rodanandfields.com/en-us/medias/Female-Derm.jpg?context=bWFzdGVyfGltYWdlc3wyNDQ2MTR8aW1hZ2UvanBlZ3xhR1k1TDJnellTOHlPRE01TnprNU5qUXdPRGcyTWk5R1pXMWhiR1V0UkdWeWJTNXFjR2N8NDc0NTdhYzliNzNhMTA3ZGJkYzc5NzVmNjMzNzcyMGUwYzE5OGQzMDk3MDE3ZWRhNTk2NTM3ZjZlZGZhNjdjMQ', backgroundColor: '#F5F4F2', // Used only if backgroundImage is null copyBackgroundColor: '#F5F4F2', splitImageHalf: true, // LAYOUT copyAlign: 'left' }, // ============================================ // MARKETING CARDS - EVERGREEN - "Rodan + Fields is the #1 Premium Lash Serum" // ============================================= { // URLS to match for immediate render urlMatches: [], // PLACEMENT: Choose ONE method (comment out the others) insertAtPosition: 3, // Insert AFTER this position in the grid // insertAfterProductCode: 'HRVRGG01', // Insert AFTER this CAT ID // insertBeforeProductCode: 'HAAGR', // OR insert BEFORE this CAT ID // POSITION: 'Start' or 'End' (if not set, uses insertAtPosition. If insertAtPosition exceeds product count, defaults to 'End') // placement: 'End', // 'Start' or 'End' titleText: 'Rodan + Fields is the #1 Premium Lash Serum Brand in the US in 2024*', bodyText: '', ctaText: '', ctaHref: '', cardHref: '', disclaimerText: '*Source Euromonitor International Limited; Custom research conducted June-Aug 2025, retail value RSP terms; all channels; Lash Serum including Lash Serum products sold as part of Sets and Kits', textShadowEnabled: false, // BACKGROUND: Image URL or null for solid color backgroundImage: 'https://www.rodanandfields.com/en-us/medias/Lash-Boost-MC-Image.jpg?context=bWFzdGVyfGltYWdlc3wyMDE1Mjd8aW1hZ2UvanBlZ3xhRFJsTDJneU1DOHlPRE01TnprNU5UZzRORFUzTkM5TVlYTm9MVUp2YjNOMExVMURMVWx0WVdkbExtcHdad3w2MTVjMTdmN2Q2MDJlYmIzMzhmOWQ2OTg0MmRlNTFiODg5N2Q5OTBhNDFlODQwMmNjODliMzM3Yjk3NGJiNjIz', backgroundColor: '#F5F4F2', // Used only if backgroundImage is null copyBackgroundColor: '#F5F4F2', splitImageHalf: true, // LAYOUT copyAlign: 'left' }, // ============================================ // MARKETING CARDS - EVERGREEN - "Klarna" // ============================================= { // URLS to match for immediate render urlMatches: ['regimen-sets','rf-paired-multi-action-regimens','skin-care-tools-and-accessories'], // PLACEMENT: Choose ONE method (comment out the others) insertAtPosition: 3, // Insert AFTER this position in the grid // insertAfterProductCode: 'HRVRGG01', // Insert AFTER this CAT ID // insertBeforeProductCode: 'HAAGR', // OR insert BEFORE this CAT ID // POSITION: 'Start' or 'End' (if not set, uses insertAtPosition. If insertAtPosition exceeds product count, defaults to 'End') // placement: 'End', // 'Start' or 'End' titleText: 'Buy now. Pay later.', bodyText: 'Select Klarna at checkout to split your purchase into 4 interest-free payments.', ctaText: '', ctaHref: '', cardHref: '', disclaimerText: '', textShadowEnabled: false, // BACKGROUND: Image URL or null for solid color backgroundImage: 'https://www.rodanandfields.com/en-us/medias/Klarna.jpg?context=bWFzdGVyfGltYWdlc3wzMTE3NnxpbWFnZS9qcGVnfGFETmlMMmc1TVM4eU9ETTVOems1T1RFMk1UTTNOQzlMYkdGeWJtRXVhbkJufDUwMzJkNzBjNzRjZmMxYzc2MjE5NWEyNjIwOTJmYzg5ZDE1NjQ1Y2U4MDQ3ZTg2OTI3ZjIxMzE1OGU3OGE2NWI', backgroundColor: '#F5F4F2', // Used only if backgroundImage is null copyBackgroundColor: '#F5F4F2', splitImageHalf: true, // LAYOUT copyAlign: 'left' } ]; // ============================================ // MODULE HTML GENERATOR // ============================================ function generateModuleHTML(cfg) { const alignMap = { left: 'flex-start', center: 'center', right: 'flex-end' }; const safeAlign = ['left','center','right'].includes(cfg.copyAlign) ? cfg.copyAlign : 'center'; const bgStyle = cfg.backgroundImage ? `background: url('${cfg.backgroundImage}') center/cover no-repeat;` : `background: ${cfg.backgroundColor}`; const shadowStyle = cfg.textShadowEnabled ? 'text-shadow: 0 1px 2px rgba(0,0,0,0.6);' : ''; // Split Image Half Layout if (cfg.splitImageHalf) { const splitImageStyle = `${cfg.backgroundImage ? `--split-image-url: url('${cfg.backgroundImage}');` : ''}`; const cardAttr = cfg.cardHref ? `data-card-href="${cfg.cardHref}"` : ''; const cardClass = cfg.cardHref ? ' rf-card-clickable' : ''; return `

${cfg.titleText.replace(/\n/g, '
')}

${cfg.bodyText ? `

${cfg.bodyText}

` : ''} ${cfg.ctaText && cfg.ctaText.trim() ? `${cfg.ctaText}` : ''} ${cfg.disclaimerText ? `
${cfg.disclaimerText}
` : ''}
`; } // Default horizontal layout const cardAttr = cfg.cardHref ? `data-card-href="${cfg.cardHref}"` : ''; const cardClass = cfg.cardHref ? ' rf-card-clickable' : ''; return `

${cfg.titleText.replace(/\n/g, '
')}

${cfg.bodyText ? `

${cfg.bodyText}

` : ''} ${cfg.ctaText && cfg.ctaText.trim() ? `${cfg.ctaText}` : ''} ${cfg.disclaimerText ? `
${cfg.disclaimerText}
` : ''}
`; } // PDP detection helper function isPDP() { try { const path = (window.location && window.location.pathname) || ''; // PDP URLs include "/p/" followed by a code return path.includes('/p/'); } catch (e) { return false; } } // Return cards whose urlMatches match a full path segment (not substring) function getActiveCards() { const href = (window.location && window.location.href || '').toLowerCase(); const path = (window.location && window.location.pathname || '').toLowerCase(); const segments = path.split('/').filter(Boolean); // path parts without empty strings const matchesSegment = (needle) => { if (!needle) return false; const n = String(needle).toLowerCase(); // exact segment match, or common category patterns return segments.includes(n) || path.endsWith('/' + n) || path.includes('/shop/' + n + '/') || href.includes('cgid=' + encodeURIComponent(n)); }; return marketingCards.filter(card => Array.isArray(card.urlMatches) && card.urlMatches.some(matchesSegment)); } // ============================================ // PRODUCT DETECTION // ============================================ let injected = false; function filterProductElements(elements) { return elements.filter(el => { const tag = el.tagName ? el.tagName.toLowerCase() : ''; if (tag === 'button' || tag === 'a') return false; const role = (el.getAttribute && el.getAttribute('role')) || ''; if (role === 'button' || role === 'link') return false; if (el.hasAttribute && el.hasAttribute('data-copilot-module')) return false; if (el.closest && el.closest('.add-to-cart, .product-cta, .product-actions, .pdp-add-to-cart, .quickbuy')) return false; return true; }); } function findProducts() { const selectors = [ '[data-product-code]', '[data-product-id]', '.product-item', '.product-card', '.product-tile', '.product', '.js-product', 'article.product', '.product-grid-item', '.product-list-item' ]; for (let selector of selectors) { const elements = document.querySelectorAll(selector); if (elements.length > 0) { console.log(`Found ${elements.length} products using: ${selector}`); return filterProductElements(Array.from(elements)); } } console.warn('No products found'); return []; } function getProductCode(el) { return el.getAttribute('data-product-code') || el.getAttribute('data-product-id') || el.getAttribute('data-sku') || el.querySelector('[data-product-code]')?.getAttribute('data-product-code') || el.querySelector('[data-product-id]')?.getAttribute('data-product-id') || el.querySelector('a[href*="/shop/"]')?.href.split('/p/')[1]?.split('?')[0] || null; } // ============================================ // INJECTION LOGIC // ============================================ function injectModule() { if (injected) return; // Guard: never inject on PDPs if (isPDP()) { console.warn('PDP detected; skipping marketing card injection.'); return; } const cards = getActiveCards(); if (!cards || cards.length === 0) { console.warn('No marketing card configured for this URL'); return; } // Only consider products inside .mdc-layout-grid__inner if present let products = []; const gridInner = document.querySelector('.mdc-layout-grid__inner'); if (gridInner) { // Find all product elements inside the grid products = filterProductElements(Array.from(gridInner.querySelectorAll('[data-product-code], [data-product-id], .product-item, .product-card, .product-tile, .product, .js-product, article.product, .product-grid-item, .product-list-item'))); } else { products = findProducts(); } if (products.length === 0) return; console.log(`${cards.length} marketing card(s) matched this URL`); // Log all found product codes and positions for debugging console.log('Product codes and positions found:'); products.forEach((p, i) => { const pos = p.getAttribute('data-position'); const code = getProductCode(p); console.log(` Index ${i + 1} | Position: ${pos} | Code: ${code || 'N/A'}`); }); // Try to inject each matching card let anyInjected = false; cards.forEach(card => { let placed = false; // Placement logic if (!placed && card.placement) { if (card.placement.toLowerCase() === 'start' && products.length > 0) { console.log('Inserting module at START (before first product)'); insertModule(products[0], 'before', card); placed = true; } else if (card.placement.toLowerCase() === 'end' && products.length > 0) { console.log('Inserting module at END (after last product)'); insertModule(products[products.length - 1], 'after', card); placed = true; } } // Fallbacks: by product code or position if (!placed) { for (let i = 0; i < products.length; i++) { const code = getProductCode(products[i]); if (card.insertAfterProductCode && code === card.insertAfterProductCode) { console.log(`Inserting module AFTER product code: ${code}`); insertModule(products[i], 'after', card); placed = true; break; } if (card.insertBeforeProductCode && code === card.insertBeforeProductCode) { console.log(`Inserting module BEFORE product code: ${code}`); insertModule(products[i], 'before', card); placed = true; break; } } } if (!placed && card.insertAtPosition) { let targetProduct = null; // Try to find by data-position attribute first targetProduct = Array.from(products).find(p => parseInt(p.getAttribute('data-position')) === card.insertAtPosition ); // If not found, fallback to Nth product in the array (1-based) if (!targetProduct && products.length >= card.insertAtPosition) { targetProduct = products[card.insertAtPosition - 1]; } // If still not found, fallback to last product in gridInner if (!targetProduct && products.length > 0) { targetProduct = products[products.length - 1]; } if (targetProduct) { console.log(`Inserting module after product (insertAtPosition=${card.insertAtPosition})`); insertModule(targetProduct, 'after', card); placed = true; } } if (!placed) { console.warn(`Target not found for card. Check insertAfterProductCode, insertBeforeProductCode, or insertAtPosition configuration.`); } else { anyInjected = true; } }); if (anyInjected) { injected = true; // Ensure any end-placed cards are positioned after the latest product set repositionEndCards(); } } function insertModule(target, position, cardConfig) { const wrapper = document.createElement('div'); wrapper.innerHTML = generateModuleHTML(cardConfig).trim(); const base = target.closest && target.closest('[data-product-code], [data-product-id], .product-item, .product-card, .product-tile, .product, .js-product, article.product, .product-grid-item, .product-list-item') || target; const baseTag = (base.tagName || '').toLowerCase(); const safeTag = (baseTag === 'button' || baseTag === 'a') ? 'div' : (baseTag || 'div'); const classList = (base.className || '') .split(/\s+/) .filter(Boolean) .filter(c => !/(^|-)btn($|-)|button|cta|add-to-cart|purchase|subscribe|link/i.test(c)) .join(' '); const container = document.createElement(safeTag); if (classList) container.className = classList; container.setAttribute('data-copilot-module', 'true'); if (cardConfig && cardConfig.placement) { container.setAttribute('data-copilot-placement', String(cardConfig.placement).toLowerCase()); } container.appendChild(wrapper.firstElementChild); if (position === 'after') { base.parentNode.insertBefore(container, base.nextSibling); } else { base.parentNode.insertBefore(container, base); } // Make entire card clickable if data-card-href is present const clickable = container.querySelector('.rf-card-clickable[data-card-href]'); if (clickable) { const href = clickable.getAttribute('data-card-href'); clickable.setAttribute('role', 'link'); clickable.setAttribute('tabindex', '0'); const navigate = () => { try { window.location.assign(href); } catch(e) { window.location.href = href; } }; clickable.addEventListener('click', (e) => { const insideAnchor = e.target.closest('a'); if (insideAnchor) return; // allow inner links like CTA/disclaimer navigate(); }); clickable.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); navigate(); } }); } console.log('Marketing module injected successfully!'); } // Reposition any cards configured with placement: 'End' after dynamic loads function repositionEndCards() { const endCards = Array.from(document.querySelectorAll('[data-copilot-module="true"][data-copilot-placement="end"]')); if (endCards.length === 0) return; // Determine current product list inside grid const gridInner = document.querySelector('.mdc-layout-grid__inner'); const productSelector = '[data-product-code], [data-product-id], .product-item, .product-card, .product-tile, .product, .js-product, article.product, .product-grid-item, .product-list-item'; let products = []; if (gridInner) { products = Array.from(gridInner.querySelectorAll(productSelector)); } else { products = Array.from(document.querySelectorAll(productSelector)); } products = products.filter(el => !el.hasAttribute('data-copilot-module')); if (products.length === 0) return; const lastProduct = products[products.length - 1]; endCards.forEach(card => { if (lastProduct.nextSibling !== card) { try { lastProduct.parentNode.insertBefore(card, lastProduct.nextSibling); } catch (e) {} } }); } function isModulePresent() { return document.querySelector('[data-copilot-module="true"]') !== null; } // ============================================ // INITIALIZATION - IMMEDIATE LOAD // ============================================ function init() { injectModule(); // Quick retries for fast page load if (!injected) { setTimeout(injectModule, 100); setTimeout(injectModule, 300); setTimeout(injectModule, 500); setTimeout(injectModule, 1000); } } // Run immediately init(); // Also run on DOMContentLoaded as backup if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } // Watch for dynamically loaded products const observer = new MutationObserver(() => { clearTimeout(window.moduleInjectionTimeout); window.moduleInjectionTimeout = setTimeout(() => { if (!isModulePresent()) { injected = false; injectModule(); } repositionEndCards(); }, 120); }); // Start observing immediately const startObserver = () => { const container = document.querySelector('.product-grid, .product-list, .products, main, body'); if (container) { observer.observe(container, { childList: true, subtree: true }); } }; startObserver(); setTimeout(startObserver, 500); })();