Crear botón de añadir al carrito

Muy buenas, estoy intentando crear un botón de añadir al carrito en el tema Horizon, quiero que se vea en el catalogo en cada producto para que no tengas que entrar dentro del producto y darle a agregar al carrito. Así es mas dinámico, el botón funciona, cuando le doy click a “Añadir al carrito” internamente si contabiliza pero hasta que no le doy yo click a recargar pagina no aparece el objeto en el carrito, hay forma de hacer que se actualice solo como cualquier pagina normal?

Gracias de antemano.

Hola @Thetutoseries

Lo que te pasa es que el botón está añadiendo el producto al carrito correctamente pero no está actualizando el contador del carrito en el header sin recargar la página. Esto es un tema de AJAX, el botón necesita decirle al header “oye, actualízate” después de añadir el producto.

¿Puedes compartir el código que usaste para crear el botón? Así puedo ver exactamente cómo estás haciendo el fetch/submit y te digo qué líneas agregar para que el carrito se actualice automáticamente.

En general, lo que necesitas es que después de que el producto se añada al carrito, tu código haga una de estas dos cosas:

  1. Disparar el evento que el tema Horizon escucha para actualizar el carrito. Normalmente es algo como document.dispatchEvent(new CustomEvent('cart:refresh')) o similar, pero el nombre exacto del evento depende del tema.

  2. O hacer un fetch a /cart.json después de añadir el producto y actualizar el contador del header manualmente con la respuesta.

Comparte tu código y la URL de tu tienda y vere si puedo darte la solucion exacta.

Okay espero que puedas ayudarme xd, este es el codigo del catalogo

<style>
  .lcc-wrap {
    font-family: 'Inter', sans-serif;
    color: #2C1A0E;
    max-width: 1200px;
    margin: 0 auto;
    padding: 40px 40px 80px;
    background: #FDFAF5;
  }

  /* BREADCRUMB */
  .lcc-breadcrumb {
    font-size: 12px;
    color: #9C7A55;
    margin-bottom: 28px;
    letter-spacing: 0.5px;
  }
  .lcc-breadcrumb a { color: #9C7A55; text-decoration: none; }
  .lcc-breadcrumb a:hover { color: #5C3D1E; }
  .lcc-breadcrumb span { margin: 0 8px; }

  /* LAYOUT */
  .lcc-layout {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: 40px;
    align-items: start;
  }

  /* SIDEBAR */
  .lcc-sidebar {
    position: sticky;
    top: 24px;
    background: #FFFAF3;
    border: 1px solid #D4B896;
    border-radius: 16px;
    overflow: hidden;
  }
  .lcc-sidebar__title {
    font-family: 'Playfair Display', serif;
    font-size: 18px;
    font-weight: 700;
    color: #2C1A0E;
    padding: 20px 22px 16px;
    border-bottom: 1px solid #E8D9C4;
  }

  /* CATEGORY GROUP */
  .lcc-cat {
    border-bottom: 1px solid #E8D9C4;
  }
  .lcc-cat:last-child { border-bottom: none; }

  .lcc-cat__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 22px;
    cursor: pointer;
    user-select: none;
    transition: background 0.15s;
  }
  .lcc-cat__header:hover { background: #F5ECD8; }
  .lcc-cat__header.active { background: #F5ECD8; }

  .lcc-cat__name {
    font-size: 14px;
    font-weight: 500;
    color: #2C1A0E;
  }
  .lcc-cat__arrow {
    font-size: 10px;
    color: #9C7A55;
    transition: transform 0.2s;
  }
  .lcc-cat__header.active .lcc-cat__arrow {
    transform: rotate(180deg);
  }

  .lcc-cat__subs {
    display: none;
    background: #FDF6EC;
    border-top: 1px solid #E8D9C4;
  }
  .lcc-cat__subs.open { display: block; }

  .lcc-cat__sub {
    display: block;
    padding: 10px 22px 10px 34px;
    font-size: 13px;
    color: #6B4C30;
    text-decoration: none;
    border-bottom: 1px solid #EEE3D0;
    transition: background 0.15s, color 0.15s;
  }
  .lcc-cat__sub:last-child { border-bottom: none; }
  .lcc-cat__sub:hover { background: #EED9B8; color: #2C1A0E; }
  .lcc-cat__sub.active {
    background: #E8CFA0;
    color: #5C3D1E;
    font-weight: 500;
  }

  /* ALL PRODUCTS LINK */
  .lcc-cat__all {
    display: block;
    padding: 14px 22px;
    font-size: 13px;
    color: #9C7A55;
    text-decoration: none;
    border-top: 1px solid #E8D9C4;
    transition: color 0.15s;
  }
  .lcc-cat__all:hover { color: #5C3D1E; }

  /* MAIN CONTENT */
  .lcc-main {}

  .lcc-main__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 24px;
    padding-bottom: 16px;
    border-bottom: 1px solid #D4B896;
  }
  .lcc-main__title {
    font-family: 'Playfair Display', serif;
    font-size: 30px;
    font-weight: 700;
    color: #2C1A0E;
  }
  .lcc-main__count {
    font-size: 13px;
    color: #9C7A55;
  }

  /* PRODUCTS GRID */
  .lcc-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
  }

  .lcc-pcard {
    background: #FFFAF3;
    border: 1px solid #D4B896;
    border-radius: 14px;
    overflow: hidden;
    transition: border-color 0.2s, transform 0.2s;
    display: flex;
    flex-direction: column;
  }
  .lcc-pcard:hover {
    border-color: #8B5E2A;
    transform: translateY(-2px);
  }
  .lcc-pcard__link {
    text-decoration: none;
    color: inherit;
    display: block;
    flex: 1;
  }
  .lcc-pcard__img {
    background: #F0E6D3;
    height: 190px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
  }
  .lcc-pcard__img img { width: 100%; height: 100%; object-fit: cover; }
  .lcc-pcard__img-placeholder { font-size: 56px; opacity: 0.4; }
  .lcc-pcard__body { padding: 14px 16px 10px; }
  .lcc-pcard__tag {
    font-size: 10px;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: #7A4A1E;
    background: #F5E6CC;
    padding: 3px 9px;
    border-radius: 10px;
    display: inline-block;
    margin-bottom: 8px;
  }
  .lcc-pcard__name {
    font-family: 'Playfair Display', serif;
    font-size: 17px;
    font-weight: 600;
    margin-bottom: 4px;
    line-height: 1.2;
    color: #2C1A0E;
  }
  .lcc-pcard__mat { font-size: 12px; color: #9C7A55; margin-bottom: 10px; }
  .lcc-pcard__price {
    font-family: 'Playfair Display', serif;
    font-size: 19px;
    font-weight: 600;
    color: #2C1A0E;
  }

  /* ACTION BUTTON */
  .lcc-pcard__action { padding: 0 16px 14px; }
  .lcc-pcard__form { margin: 0; }
  .lcc-pcard__btn {
    width: 100%;
    background: #5C3D1E;
    color: #F5DEB3;
    border: none;
    padding: 10px 14px;
    font-size: 13px;
    font-weight: 500;
    border-radius: 8px;
    cursor: pointer;
    font-family: 'Inter', sans-serif;
    transition: background 0.15s, transform 0.1s;
    letter-spacing: 0.3px;
  }
  .lcc-pcard__btn:hover { background: #7A4A1E; }
  .lcc-pcard__btn:active { transform: scale(0.98); }
  .lcc-pcard__btn:disabled { opacity: 0.6; cursor: not-allowed; }
  .lcc-pcard__btn--out { background: #999; }
  .lcc-pcard__btn--added { background: #4A8B5A !important; }

  /* EMPTY STATE */
  .lcc-empty {
    text-align: center;
    padding: 60px 20px;
    color: #9C7A55;
  }
  .lcc-empty__icon { font-size: 48px; margin-bottom: 16px; opacity: 0.4; }
  .lcc-empty__text {
    font-family: 'Playfair Display', serif;
    font-size: 20px;
    color: #6B4C30;
    margin-bottom: 8px;
  }
  .lcc-empty__sub { font-size: 14px; color: #9C7A55; }

  /* RESPONSIVE */
  @media (max-width: 900px) {
    .lcc-layout { grid-template-columns: 1fr; }
    .lcc-sidebar { position: static; }
    .lcc-grid { grid-template-columns: repeat(2, 1fr); }
    .lcc-wrap { padding: 24px 16px 60px; }
  }
  @media (max-width: 480px) {
    .lcc-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
    .lcc-pcard__img { height: 140px; }
    .lcc-pcard__body { padding: 10px 12px 8px; }
    .lcc-pcard__name { font-size: 14px; }
    .lcc-pcard__price { font-size: 16px; }
    .lcc-pcard__action { padding: 0 12px 12px; }
    .lcc-pcard__btn { padding: 8px 10px; font-size: 12px; }
  }
</style>

<div class="lcc-wrap">

<!-- BREADCRUMB -->

<div class="lcc-breadcrumb">
    <a href="{{ routes.root_url }}">Inicio</a>
    <span>&rsaquo;</span>
    <a href="{{ routes.root_url }}collections/all">Catálogo</a>
    {% if collection.title != 'all' %}
      <span>&rsaquo;</span>
      <span>{{ collection.title }}</span>
    {% endif %}
  </div>

<div class="lcc-layout">

<!-- SIDEBAR -->
<aside class="lcc-sidebar">
  <div class="lcc-sidebar__title">Categorías</div>

  <!-- LÁMPARAS -->
  <div class="lcc-cat">
    <div class="lcc-cat__header {% if collection.handle == 'lamparas' or collection.handle == 'sobremesa' or collection.handle == 'techo-y-arana' or collection.handle == 'apliques-de-pared' or collection.handle == 'pie' or collection.handle == 'quinques' %}active{% endif %}" onclick="toggleCat(this)">
      <span class="lcc-cat__name">🪔 Lámparas</span>
      <span class="lcc-cat__arrow">▼</span>
    </div>
    <div class="lcc-cat__subs {% if collection.handle == 'lamparas' or collection.handle == 'sobremesa' or collection.handle == 'techo-y-arana' or collection.handle == 'apliques-de-pared' or collection.handle == 'pie' or collection.handle == 'quinques' %}open{% endif %}">
      <a href="{{ routes.root_url }}collections/sobremesa" class="lcc-cat__sub {% if collection.handle == 'sobremesa' %}active{% endif %}">Sobremesa</a>
      <a href="{{ routes.root_url }}collections/techo-y-arana" class="lcc-cat__sub {% if collection.handle == 'techo-y-arana' %}active{% endif %}">Techo y araña</a>
      <a href="{{ routes.root_url }}collections/apliques-de-pared" class="lcc-cat__sub {% if collection.handle == 'apliques-de-pared' %}active{% endif %}">Apliques de pared</a>
      <a href="{{ routes.root_url }}collections/pie" class="lcc-cat__sub {% if collection.handle == 'pie' %}active{% endif %}">Pie</a>
      <a href="{{ routes.root_url }}collections/quinques" class="lcc-cat__sub {% if collection.handle == 'quinques' %}active{% endif %}">Quinqués</a>
    </div>
  </div>

  <!-- REPUESTOS -->
  <div class="lcc-cat">
    <div class="lcc-cat__header {% if collection.handle == 'repuestos-y-componentes' or collection.handle == 'casquillos' or collection.handle == 'tulipas' or collection.handle == 'globos-de-cristal' or collection.handle == 'pantallas' or collection.handle == 'cable-textil-y-electrico' or collection.handle == 'florones-y-rosetones' or collection.handle == 'portalamparas' or collection.handle == 'cadenas-y-soportes' or collection.handle == 'interruptores-y-enchufes' %}active{% endif %}" onclick="toggleCat(this)">
      <span class="lcc-cat__name">🔧 Repuestos</span>
      <span class="lcc-cat__arrow">▼</span>
    </div>
    <div class="lcc-cat__subs {% if collection.handle == 'repuestos-y-componentes' or collection.handle == 'casquillos' or collection.handle == 'tulipas' or collection.handle == 'globos-de-cristal' or collection.handle == 'pantallas' or collection.handle == 'cable-textil-y-electrico' or collection.handle == 'florones-y-rosetones' or collection.handle == 'portalamparas' or collection.handle == 'cadenas-y-soportes' or collection.handle == 'interruptores-y-enchufes' %}open{% endif %}">
      <a href="{{ routes.root_url }}collections/casquillos" class="lcc-cat__sub {% if collection.handle == 'casquillos' %}active{% endif %}">Casquillos</a>
      <a href="{{ routes.root_url }}collections/tulipas" class="lcc-cat__sub {% if collection.handle == 'tulipas' %}active{% endif %}">Tulipas</a>
      <a href="{{ routes.root_url }}collections/globos-de-cristal" class="lcc-cat__sub {% if collection.handle == 'globos-de-cristal' %}active{% endif %}">Globos de cristal</a>
      <a href="{{ routes.root_url }}collections/pantallas" class="lcc-cat__sub {% if collection.handle == 'pantallas' %}active{% endif %}">Pantallas</a>
      <a href="{{ routes.root_url }}collections/cable-textil-y-electrico" class="lcc-cat__sub {% if collection.handle == 'cable-textil-y-electrico' %}active{% endif %}">Cable textil y eléctrico</a>
      <a href="{{ routes.root_url }}collections/florones-y-rosetones" class="lcc-cat__sub {% if collection.handle == 'florones-y-rosetones' %}active{% endif %}">Florones y rosetones</a>
      <a href="{{ routes.root_url }}collections/portalamparas" class="lcc-cat__sub {% if collection.handle == 'portalamparas' %}active{% endif %}">Portalámparas</a>
      <a href="{{ routes.root_url }}collections/cadenas-y-soportes" class="lcc-cat__sub {% if collection.handle == 'cadenas-y-soportes' %}active{% endif %}">Cadenas y soportes</a>
      <a href="{{ routes.root_url }}collections/interruptores-y-enchufes" class="lcc-cat__sub {% if collection.handle == 'interruptores-y-enchufes' %}active{% endif %}">Interruptores y enchufes</a>
    </div>
  </div>

  <!-- RESTAURACIÓN -->
  <div class="lcc-cat">
    <div class="lcc-cat__header" onclick="window.location='/pages/servicio-de-restauracion'">
      <span class="lcc-cat__name">✨ Restauración</span>
      <span class="lcc-cat__arrow">›</span>
    </div>
  </div>

  <a href="{{ routes.all_products_collection_url }}" class="lcc-cat__all">Ver todo el catálogo →</a>
</aside>

<!-- MAIN -->
<main class="lcc-main">
  <div class="lcc-main__header">
    <h1 class="lcc-main__title">{{ collection.title | default: 'Todo el catálogo' }}</h1>
    <span class="lcc-main__count">{{ collection.products_count }} {{ collection.products_count | pluralize: 'pieza', 'piezas' }}</span>
  </div>

  {% if collection.products.size > 0 %}
    <div class="lcc-grid">
      {% for product in collection.products %}
        <div class="lcc-pcard">
          <a class="lcc-pcard__link" href="{{ product.url }}">
            <div class="lcc-pcard__img">
              {% if product.featured_image %}
                <img src="{{ product.featured_image | image_url: width: 400 }}" alt="{{ product.title }}" width="400" height="400" loading="lazy">
              {% else %}
                <div class="lcc-pcard__img-placeholder">🪔</div>
              {% endif %}
            </div>
            <div class="lcc-pcard__body">
              {% if product.tags.first %}
                <div class="lcc-pcard__tag">{{ product.tags.first }}</div>
              {% endif %}
              <div class="lcc-pcard__name">{{ product.title }}</div>
              <div class="lcc-pcard__mat">{{ product.metafields.custom.materiales }}</div>
              <div class="lcc-pcard__price">{{ product.price | money }}</div>
            </div>
          </a>
          <div class="lcc-pcard__action">
            {% if product.available %}
              <form action="{{ routes.cart_add_url }}" method="post" class="lcc-pcard__form" data-cart-form>
                <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
                <input type="hidden" name="quantity" value="1">
                <button type="submit" class="lcc-pcard__btn">Añadir a la cesta</button>
              </form>
            {% else %}
              <button class="lcc-pcard__btn lcc-pcard__btn--out" disabled>Agotado</button>
            {% endif %}
          </div>
        </div>
      {% endfor %}
    </div>
  {% else %}
    <div class="lcc-empty">
      <div class="lcc-empty__icon">🪔</div>
      <div class="lcc-empty__text">Próximamente</div>
      <div class="lcc-empty__sub">Estamos preparando esta sección. Vuelve pronto.</div>
    </div>
  {% endif %}
</main>

</div>
</div>

<script>
  function toggleCat(header) {
    const subs = header.nextElementSibling;
    const isOpen = subs.classList.contains('open');
    subs.classList.toggle('open', !isOpen);
    header.classList.toggle('active', !isOpen);
  }
  document.querySelectorAll('[data-cart-form]').forEach(form => {
    form.addEventListener('submit', async (e) => {
      e.preventDefault();
      const btn = form.querySelector('button');
      const variantId = form.querySelector('input[name="id"]').value;
      const originalText = btn.textContent;
      if (!variantId) {
        btn.textContent = 'Sin variante';
        setTimeout(() => { btn.textContent = originalText; }, 1800);
        return;
      }
      btn.textContent = 'Añadiendo...';
      btn.disabled = true;
      try {
        // 1) Añadir al carrito
        const addRes = await fetch('/cart/add.js', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-Requested-With': 'XMLHttpRequest'
          },
          body: JSON.stringify({ items: [{ id: parseInt(variantId, 10), quantity: 1 }] })
        });
        if (!addRes.ok) {
          const errData = await addRes.json().catch(() => ({}));
          console.error('Cart add error:', errData);
          btn.textContent = errData.description || 'Error';
          setTimeout(() => { btn.textContent = originalText; btn.disabled = false; }, 2500);
          return;
        }
        // 2) Obtener carrito actualizado
        const cart = await fetch('/cart.js').then(r => r.json());
        console.log('Carrito actualizado:', cart);
        // 3) Avisar al tema (varios eventos para máxima compatibilidad)
        const eventNames = [
          'cart:refresh', 'cart:updated', 'cart:update', 'cart-update',
          'cart:item-added', 'cart:added', 'cart:change', 'cart:build',
          'cart-drawer:refresh'
        ];
        eventNames.forEach(name => {
          document.dispatchEvent(new CustomEvent(name, { detail: { cart }, bubbles: true }));
          window.dispatchEvent(new CustomEvent(name, { detail: { cart }, bubbles: true }));
        });
        // 4) Sistema PubSub de los temas nuevos de Shopify (Horizon)
        if (window.publish && window.PUB_SUB_EVENTS) {
          try { window.publish(window.PUB_SUB_EVENTS.cartUpdate, { cart, source: 'cespedes-catalog' }); } catch(e) {}
        }
        // 5) Actualizar contador del carrito manualmente (por si los eventos no llegan)
        const countSelectors = [
          '[data-cart-count]', '.cart-count', '.cart-item-count',
          'cart-icon-bubble', '.cart-icon-bubble', '#CartCount',
          '[data-cart-icon-bubble]', '.header__cart-count',
          '[ref="cartBubbleCount"]', '.cart-bubble__count'
        ];
        countSelectors.forEach(sel => {
          document.querySelectorAll(sel).forEach(el => {
            el.textContent = cart.item_count;
            el.classList.remove('hidden', 'cart-count--empty');
            el.removeAttribute('hidden');
          });
        });
        // 6) Refrescar la sección del header completa via Section Rendering API
        try {
          const headerRes = await fetch(window.location.pathname + '?sections=header');
          const sectionData = await headerRes.json();
          if (sectionData.header) {
            const headerEl = document.querySelector('#shopify-section-header, [id*="header"]');
            if (headerEl) {
              const newHTML = new DOMParser().parseFromString(sectionData.header, 'text/html');
              const newHeader = newHTML.querySelector('#shopify-section-header, [id*="header"]');
              if (newHeader) headerEl.innerHTML = newHeader.innerHTML;
            }
          }
        } catch(err) { console.log('Section refresh skipped:', err); }
        // Feedback visual en el botón
        btn.textContent = '✓ Añadido';
        btn.classList.add('lcc-pcard__btn--added');
        setTimeout(() => {
          btn.textContent = originalText;
          btn.classList.remove('lcc-pcard__btn--added');
          btn.disabled = false;
        }, 1800);
      } catch (err) {
        console.error('Cart error:', err);
        btn.textContent = 'Error';
        setTimeout(() => { btn.textContent = originalText; btn.disabled = false; }, 1800);
      }
    });
  });
</script>

{% schema %}
{
“name”: “Céspedes Catálogo”,
“tag”: “section”,
“settings”: 
,
“presets”: [
{
“name”: “Céspedes Catálogo”
}
]
}
{% endschema %}

Este es el url de la pagina: – Lámparas Céspedes

y la contraseña es esta: Rayoestrafalario

gracias de antemano.

Horizon theme already have Quick add to cart button for the product card. So you just need to enable it.

Or you can watch this video insead: https://www.youtube.com/watch?v=Ft9fRnHNckU

If this doesn’t help, then it’s requires to do the custom code in the theme file.

Y si, como te comento @SectionKit Shopify nativamente te permite añadir el boton de add-to-cart a las secciones de colecciones que añadas, aunque creo que lo que estas haciendo es full custom.

Ya estuve revisando a fondo el código que compartiste y la estructura en vivo de tu tienda. Encontré exactamente por qué no funciona y por qué te redirige a la página del producto. Tienes dos detalles clave que debemos ajustar:

1. El problema del HTML (La redirección): Actualmente, tienes toda la tarjeta (incluyendo el botón de añadir) envuelta dentro del enlace principal (<a class="lc-pcard" href="...">). Por reglas estrictas de HTML, no puedes poner un botón interactivo dentro de un enlace. Cuando haces clic en “Añadir”, el navegador ignora tu script y ejecuta el enlace que lo envuelve.

Para solucionarlo, debes cerrar el enlace </a> antes de llegar al botón, y envolver el botón en un <form>. Busca la estructura de tu tarjeta y modifícala para que quede exactamente así:

<a class="lc-pcard" href="{{ product.url }}">
  <div class="lc-pcard_img">
    {% if product.featured_image %}
      <img src="{{ product.featured_image | image_url: width: 400 }}" alt="{{ product.title }}" width="400" height="400" loading="lazy">
    {% else %}
      <div class="lcc-pcard__img-placeholder">🪔</div>
    {% endif %}
  </div>
  <div class="lc-pcard_body">
    {% if product.tags.first %}
      <div class="lcc-pcard__tag">{{ product.tags.first }}</div>
    {% endif %}
    <div class="lc-pcard_name">{{ product.title }}</div>
    <div class="lc-pcard_mat">{{ product.metafields.custom.materiales }}</div>
  </div>
</a> <div class="lc-pcard_foot">
  <span class="lc-pcard_price">{{ product.price | money }}</span>
  
  {% if product.available %}
    <form action="{{ routes.cart_add_url }}" method="post" data-cart-form style="margin: 0;">
      <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
      <button type="submit" class="lc-pcard_btn">Añadir a la cesta</button>
    </form>
  {% else %}
    <button class="lc-pcard__btn lcc-pcard__btn--out" disabled>Agotado</button>
  {% endif %}
</div>

2. El problema del Script (La actualización del carrito): En tu script actual estás intentando recargar todo el <header>. En temas modernos como Horizon, esto “mata” los clics del carrito. La forma perfecta de hacerlo es usar la API nativa de Shopify para pedir solo la burbuja y el carrito lateral.

Reemplaza tu etiqueta <script> ... </script> por esta versión optimizada:

<script>
  function toggleCat(header) {
    const subs = header.nextElementSibling;
    const isOpen = subs.classList.contains('open');
    subs.classList.toggle('open', !isOpen);
    header.classList.toggle('active', !isOpen);
  }

  document.querySelectorAll('[data-cart-form]').forEach(form => {
    form.addEventListener('submit', async (e) => {
      e.preventDefault();
      const btn = form.querySelector('button');
      const variantId = form.querySelector('input[name="id"]').value;
      const originalText = btn.textContent;

      if (!variantId) return;

      btn.textContent = 'Añadiendo...';
      btn.disabled = true;

      try {
        // Pedimos a Shopify que al añadir nos devuelva las secciones del carrito actualizadas
        const addRes = await fetch(window.Shopify.routes.root + 'cart/add.js', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
          },
          body: JSON.stringify({
            items: [{ id: parseInt(variantId, 10), quantity: 1 }],
            sections: 'cart-icon-bubble,cart-drawer,cart-notification',
            sections_url: window.location.pathname
          })
        });

        const parsedState = await addRes.json();

        if (parsedState.status) {
          btn.textContent = parsedState.description || 'Error';
          setTimeout(() => { btn.textContent = originalText; btn.disabled = false; }, 2500);
          return;
        }

        // 1. Reemplazamos la burbuja del carrito limpiamente
        const cartBubble = document.querySelector('#cart-icon-bubble');
        if (cartBubble && parsedState.sections['cart-icon-bubble']) {
          const newBubbleHTML = new DOMParser()
            .parseFromString(parsedState.sections['cart-icon-bubble'], 'text/html')
            .getElementById('cart-icon-bubble').innerHTML;
          cartBubble.innerHTML = newBubbleHTML;
        }

        // 2. Avisamos al tema (Horizon) para que actualice y abra el drawer
        if (window.publish && window.PUB_SUB_EVENTS) {
          window.publish(window.PUB_SUB_EVENTS.cartUpdate, { source: 'custom-catalog', cartData: parsedState });
        } else {
          document.dispatchEvent(new CustomEvent('cart:build'));
          document.dispatchEvent(new CustomEvent('cart:refresh'));
        }

        // Feedback visual
        btn.textContent = '✓ Añadido';
        btn.classList.add('lcc-pcard__btn--added');
        setTimeout(() => {
          btn.textContent = originalText;
          btn.classList.remove('lcc-pcard__btn--added');
          btn.disabled = false;
        }, 1800);

      } catch (err) {
        console.error('Cart error:', err);
        btn.textContent = 'Error';
        setTimeout(() => { btn.textContent = originalText; btn.disabled = false; }, 1800);
      }
    });
  });
</script>

Haz estos dos cambios, guarda y pruébalo. Tu botón atrapará el clic perfectamente y el carrito se actualizará al instante sin recargar la página.

Si esto finalmente lo resuelve, te agradezco si lo marcas como Solución Aceptada así otros en la comunidad pueden encontrar la respuesta más rápido.

¡Mucho éxito con el catálogo, se ve genial!

Muchas gracias a ambos por contestar, @Mateo-Penida he probado y al darle click a agregar al carrito no se actualiza el carrito tengo que actualizarlo yo recargando la pagina, disculpame estoy aprendiendo aun

He conseguido arreglarlo, si alguien tiene el mismo problema quizá le sirva esto, lo dejo por aquí

<script>
  function toggleCat(header) {
    const subs = header.nextElementSibling;
    const isOpen = subs.classList.contains('open');
    subs.classList.toggle('open', !isOpen);
    header.classList.toggle('active', !isOpen);
  }

  document.querySelectorAll('[data-cart-form]').forEach(form => {
  form.addEventListener('submit', async (e) => {
    e.preventDefault();
    const btn = form.querySelector('button');
    const variantId = form.querySelector('input[name="id"]').value;
    const originalText = btn.textContent;
    if (!variantId) return;

    btn.textContent = 'Añadiendo...';
    btn.disabled = true;

    try {
      const res = await fetch(window.Shopify.routes.root + 'cart/add.js', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
        body: JSON.stringify({ items: [{ id: parseInt(variantId, 10), quantity: 1 }] })
      });

      const data = await res.json();

      if (data.status) {
        btn.textContent = data.description || 'Error';
        setTimeout(() => { btn.textContent = originalText; btn.disabled = false; }, 2500);
        return;
      }

      // Aviso a Horizon: actualiza el contador y abre el drawer
      document.dispatchEvent(new CustomEvent('cart:update', {
        bubbles: true,
        detail: { data: { itemCount: data.quantity || 1, source: 'custom-catalog' } }
      }));

      // Feedback visual
      btn.textContent = '✓ Añadido';
      btn.classList.add('lcc-pcard__btn--added');
      setTimeout(() => {
        btn.textContent = originalText;
        btn.classList.remove('lcc-pcard__btn--added');
        btn.disabled = false;
      }, 1800);

    } catch (err) {
      console.error('Cart error:', err);
      btn.textContent = 'Error';
      setTimeout(() => { btn.textContent = originalText; btn.disabled = false; }, 1800);
    }
  });
});
</script>

Ah genial, justo estaba escribiendote un mensaje para resolverlo, me alegra que ya lo hayas conseguido!

Cualquier cosa, aqui en la comunidad estamos para ayudarte!

Exitos

Muchísimas gracias por tu tiempo, sin ti habría estado aquí mucho tiempo, de verdad, mil gracias!!