TUTORIAL: Creating a Dynamic Product Variant Selector in Funnelish

This tutorial will guide you through integrating a dynamic product variant selector in your Funnelish landing page using HTML, CSS, and JavaScript. By the end, your product page will allow users to select variants and dynamically update the checkout link.


Overview

This implementation includes:

  1. HTML Structure: Creating radio buttons for variant selection.
  2. CSS Styling: Styling the variant selector for a polished look.
  3. JavaScript: Dynamically updating the checkout link based on the selected variant.

https://www.loom.com/share/39f48734fc7740e89356c972554be489


Step 1: HTML Structure

Here’s the HTML snippet for the product variant selector:

<div class="prod-variants2">
  <input type="radio" id="contactChoice5" name="id" value="47157892874547" data-gtm-form-interact-field-id="0">
  <label onclick="handleVarPriceChange2('$24.99','47157892874547')" for="contactChoice5" class="contactChoice5">
    <div class="var__inner">
      <div class="var__inner__check"></div>
      <div class="var__inner__info">
        <div class="var__info__left">
          <span class="vname_text">6 Filters</span>
          <span class="tapes_text">
            <span class="pricey">$5 Each</span>
          </span>
        </div>
        <div class="var__info__right">
          <div class="var__title var__price">
            <div class="var__save var__comp"></div>
            <div class="var__title var__price">$29.99</div>
          </div>
        </div>
      </div>
    </div>
  </label>
  <input type="radio" id="contactChoice6" name="id" value="47008684540211" data-gtm-form-interact-field-id="1">
  <label onclick="handleVarPriceChange2('$49.99','47008684540211')" for="contactChoice6" class="contactChoice6">
    <div class="var__inner">
      <div class="var__inner__check"></div>
      <div class="var__inner__info">
        <div class="var__info__left">
          <span class="vname_text">12 Filters</span>
          <span class="tapes_text">
            <span class="pricey">$4 Each</span>
          </span>
        </div>
        <div class="var__info__right">
          <div class="var__save percentage">SAVE 20%</div>
          <div class="var__title var__price">
            <div class="var__save var__comp">$59.99</div>
            <div class="var__title var__price">$47.99</div>
          </div>
        </div>
      </div>
    </div>
  </label>
  <input type="radio" id="contactChoice7" name="id" value="47008684474675" checked="" data-gtm-form-interact-field-id="2">
  <label onclick="handleVarPriceChange2('$89.99','47008684474675')" for="contactChoice7" class="contactChoice7">
    <div class="var__pop-text">
      <span class="tapes_text">MOST POPULAR</span>
    </div>
    <div class="var__inner">
      <div class="var__inner__check"></div>
      <div class="var__inner__info">
        <div class="var__info__left">
          <span class="vname_text">24 Filters</span>
          <span class="tapes_text">
            <span class="pricey">$3 Each</span>
          </span>
        </div>
        <div class="var__info__right">
          <div class="var__save percentage">SAVE 40%</div>
          <div class="var__title var__price">
            <div class="var__save var__comp">$119.99</div>
            <div class="var__title var__price">$71.99</div>
          </div>
        </div>
      </div>
    </div>
  </label>
</div>

<a class="add-to-cart2" href="#">Add to cart<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" data-pf-type="Icon2" class="sc-GEbAx guOkkD pf-c1bc5fee004b-441_ pf-icon2-1"a>
    <path d="M502.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-128-128c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L402.7 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l370.7 0-73.4 73.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l128-128z"></path>
  </svg></a>

<script id="variant_metafield_data2" type="application/json">
  {
    "47157892874547": "https://your-link-for-6-filters.com",
    "47008684540211": "https://your-link-for-12-filters.com",
    "47008684474675": "https://your-link-for-24-filters.com"
  }
</script>

Step 2: CSS Styling

Enhance the appearance of the variant selector:

.add-to-cart2 {
    width: 100%;
    padding-top: 20px;
    padding-bottom: 20px;
    border-radius: 10px;
    background-color: #435274;
    font-size: 24px;
    position: relative !important;
    color: #fff !important;
    margin-top: 15px !important;
    font-family: Roboto !important;
    font-weight: 600 !important;
    padding-right: 45px !important;
    padding-left: 45px !important;
    display: table !important;
    margin-left: auto !important;
    margin-right: auto !important;
    text-align: center;
  }
  .add-to-cart2 svg {
    height: 24px;
    font-size: inherit;
    position: absolute;
    right: 25px;
    fill: #fff;
  }
  .prod-variants2 * {
    font-family: 'Roboto' !important;
    color: #435274;
  }
  .prod-variants2 input:checked+label,
  .appstle-active-option {
    background-color: #e1c7c2;
    background: linear-gradient(90deg, rgba(198, 215, 225, 0.7473039386653099) 10%, rgba(198, 215, 225, 0.25150561933757876) 100%) !important;
  }
  .prod-variants2 .var__inner {
    display: flex;
    align-items: flex-start;
    align-items: center;
  }
  .prod-variants2 .ship_mb_block {
    display: none;
  }
  .prod-variants2 .var__pop-text span {
    text-align: center;
    position: absolute;
    top: -10px;
    left: 3%;
    transform: translateX(-3%);
    background: #201547;
    padding: 3px 15px;
    border-radius: 20px;
    color: #FFF;
    font-weight: 600;
    font-size: 10px;
    font-family: Roboto;
    letter-spacing: 0px;
    line-height: 130%;
  }
  .prod-variants2 .var__bestV-text span {
    text-align: center;
    position: absolute;
    top: -10px;
    left: 6%;
    transform: translateX(-6%);
    background: #201547;
    padding: 3px 15px;
    border-radius: 20px;
    color: #FFF;
    font-weight: 600;
    font-size: 10px;
    font-family: Roboto;
    letter-spacing: 0px;
    line-height: 130%;
  }
  .prod-variants2 .var__inner__info {
    display: flex;
    justify-content: space-between;
    width: 100%;
    align-items: center;
    min-height: 72px;
  }
  .prod-variants2 .var__inner__check {
    display: block !important;
    width: 22px;
    height: 20px;
    border: 1px solid #201547;
    border-radius: 50%;
    margin-right: 10px;
  }
  .prod-variants2 .prod-variants2 input {
    appearance: none;
  }
  .prod-variants2 .vname_text {
    font-size: 18px;
    font-weight: 600;
  }
  .prod-variants2 .tapes_text {
    color: #000;
    font-size: 15px;
  }
  .prod-variants2 .var__info__left {
    display: flex;
    flex-direction: column;
    justify-content: center;
    line-height: 24px;
  }
  .prod-variants2 .var__info__right {
    line-height: 24px;
    text-align: center;
  }
  .prod-variants2 .var__price {
    font-size: 22px;
    font-weight: 600;
  }
  .prod-variants2 .var__comp {
    text-decoration: line-through;
    color: #f65b5b;
  }
  .prod-variants2 .var__save {
    font-size: 14px;
    font-weight: 600;
  }
  .prod-variants2 .percentage {
    background: #f7dc5a;
    border-radius: 8px;
    color: #000;
    font-size: 12px;
    letter-spacing: 0px !important;
    padding: 4px 8px;
    line-height: 100%;
    width: max-content;
    margin-left: auto;
  }
  .prod-variants2 {
    display: flex;
    flex-wrap: wrap;
    gap: 13px;
  }
  .prod-variants2 label {
    width: 100%;
    font-size: 16px;
    text-transform: inherit;
    display: block;
    padding: 0px 8px;
    position: relative;
    border: 2px solid #201547;
    border-radius: 10px;
    cursor: pointer;
  }
  .prod-variants2 .var__title.var__price {
    display: flex;
    flex-direction: row;
    align-items: flex-end;
    text-align: right;
    gap: 6px;
    margin-top: 2px;
  }
  .prod-variants2 .var__save.var__comp {
    text-decoration: line-through;
  }
  .prod-variants2 span.var__title__ship {
    font-size: 10px;
    color: #ccbdff;
    border: 1px solid #201547;
    font-weight: 700;
    padding: 5px 9px;
    border-radius: 10px;
    margin-left: 10px;
    display: inline-block;
    align-items: center;
  }
  .prod-variants2 input:checked+label .var__inner__check {
    background: #201547;
    border: 2px solid #fff;
  }
  .prod-variants2 input:checked+label {
    border: 4px solid #201547;
    border-radius: 10px;
  }
  .prod-variants2 input:checked+label span.var__title__ship,
  .prod-variants2 label:hover span.var__title__ship {
    color: #201547;
    border: 1px solid #201547;
    background-color: #fff;
  }
  .prod-variants2 span.popular_text {
    position: absolute;
    right: -29px;
    top: -9px;
    display: block;
    z-index: 9999999;
    width: 90px;
  }
  .prod-variants2 input {
    appearance: none;
    height: 0;
    width: 0;
    overflow: hidden;
    position: absolute;
  }
  @media screen and (max-width: 991px) {
    .prod-variants2 .ship_mb_block {
      display: block !important;
    }

    .prod-variants2 .ship_mb_none {
      display: none !important;
    }

    .prod-variants2 .vname_text {
      font-size: 16px !important;
    }

    .prod-variants2 .var__price,
    #appstle_subscription_widget0 .appstle_subscription_amount {
      font-size: 18px !important;
    }
  }

Step 3: JavaScript Functionality

Add the following JavaScript to handle dynamic link updates:

const allVariantMetafieldData2 = JSON.parse(document.querySelector('#variant_metafield_data2').textContent);
  const addToCartLink2 = document.querySelector('.add-to-cart2');

  const handleVarPriceChange2 = (price, id) => {
    console.log("Selected Price:", price);
    console.log("Selected ID:", id);
    history.pushState({}, null, `?variant=${id}`);
    handleLinkChange2(id);
  };

  const handleLinkChange2 = (id) => {
    const newHref = allVariantMetafieldData2[id];
    if (newHref) {
      addToCartLink2.href = newHref;
    }
  };

  document.addEventListener('DOMContentLoaded', () => {
    const selectedVariant = document.querySelector('input[name="id"]:checked').value;
    handleLinkChange2(selectedVariant);
  });

Step 4: Testing

  1. Load the page and confirm that the Add to Cart button points to the correct variant link by default.
  2. Click on different variants and verify that the link updates dynamically.

Conclusion

By following this tutorial, you’ve added a dynamic product variant selector to your Funnelish landing page. This feature enhances user experience by updating checkout links in real time.

1 Like

thank you. does this allow for a multi-select?

Hello, just updating this thread :slightly_smiling_face:

Funnelish currently supports up to 3 variants per product, which aligns with Shopify’s own variant limit.

However, if your product requires more than 3 variants, you can easily work around this using the dynamic variants dropdown. Full setup instructions are available above.

This is particularly useful for anything that requires additional variant combinations beyond the standard 3. The dynamic variants dropdown gives you the flexibility to handle more complex product configurations without any limitations.

If you’re running a Shopify store connected to Funnelish and need more than 3 variants, this is the recommended approach to keep your product pages fully functional and your checkout flow seamless.

For any questions, feel free to comment below or reach out to us at [email protected]

1 Like