Documentation

Theme mount points

Horizon needs these slots

Cart and policy embeds do not inject themselves into a stock Horizon theme. They copy their UI into empty elements the theme renders. Add the Liquid below once per theme. Turning an embed off hides that UI; the empty slots can stay.

Rules

  • Use data-vendr-cart-context="drawer" in the cart drawer and "page" on the cart page. Each embed’s Show in Cart Drawer and Show on Cart page settings follow that value.
  • Keep data-skip-subtree-update on the cart hosts so Horizon’s cart refresh does not wipe the live widget. The summary host is the exception: attributes go on the existing summary wrapper, which already contains .cart-totals.
  • Put drawer hosts in the non-empty cart branch, in the same order as below. Horizon only morphs markup the server still renders.
  • Render the subscription host only on lines that can subscribe. A host with no matching template makes the cart refresh loop.

Cart drawer

snippets/cart-drawer.liquid, inside the non-empty scroll-hint.cart-drawer__content. Free shipping and the gifts progress bar sit above the line items. The gifts picker and upsell sit inside .cart-drawer__items, after the products. Shipping estimate uses the summary wrapper.

snippets/cart-drawer.liquid

<div
  data-vendr-cart-header
  data-vendr-cart-context="drawer"
  data-skip-subtree-update
></div>

{%- comment -%} VENDR — App embed mount (free gifts progress bar). {%- endcomment -%}
<div
  data-vendr-cart-gifts-bar
  data-vendr-cart-context="drawer"
  data-skip-subtree-update
></div>

<scroll-hint class="cart-drawer__items">
  {% render 'cart-products', context: 'drawer' %}

  {%- comment -%} VENDR — App embed mount (free gifts picker). {%- endcomment -%}
  <div
    data-vendr-cart-gifts
    data-vendr-cart-context="drawer"
    data-skip-subtree-update
  ></div>

  <div
    data-vendr-cart-upsell
    data-vendr-cart-context="drawer"
    data-skip-subtree-update
  ></div>
</scroll-hint>

<div
  class="cart-drawer__summary"
  data-vendr-cart-summary
  data-vendr-cart-context="drawer"
>
  {% render 'cart-summary', section_id: 'cart-drawer-section' %}
</div>

Without the gifts slots, the app inserts them above the drawer title. The Liquid above is the placement to keep.

Cart subscription

snippets/cart-products.liquid, in place of the selling-plan name inside .cart-items__variants-wrapper. The same snippet serves the drawer and the cart page; context sets the host.

snippets/cart-products.liquid

{%- comment -%}
  VENDR — App embed mount (cart subscription). Only for lines that can subscribe;
  otherwise mount treats missing templates as "needs /cart refresh" and loops.
  data-skip-subtree-update keeps the live picker across in-place cart morphs.
{%- endcomment -%}
{%- if item.variant.selling_plan_allocations.size > 0 and item.parent_relationship.parent == null -%}
  <div
    data-vendr-cart-line
    data-vendr-cart-context="{% if context == 'drawer' %}drawer{% else %}page{% endif %}"
    data-line-key="{{ item.key | escape }}"
    data-variant-id="{{ item.variant_id }}"
    data-vendr-expects-subscription="true"
    data-skip-subtree-update
  >
    {%- if context != 'drawer' and item.selling_plan_allocation -%}
      <p class="cart-items__selling-plan">
        {{ item.selling_plan_allocation.selling_plan.name }}
      </p>
    {%- endif -%}
  </div>
{%- elsif context != 'drawer' and item.selling_plan_allocation -%}
  <p class="cart-items__selling-plan">
    {{ item.selling_plan_allocation.selling_plan.name }}
  </p>
{%- endif -%}

Cart page

sections/main-cart.liquid, inside .cart-page__main. Same widgets as the drawer, with data-vendr-cart-context="page". Show on Cart page must be on for an embed to fill these.

sections/main-cart.liquid

<div class="cart-page__main">
  <div
    data-vendr-cart-header
    data-vendr-cart-context="page"
    data-skip-subtree-update
  ></div>

  {%- comment -%} VENDR — App embed mount (free gifts progress bar). {%- endcomment -%}
  <div
    data-vendr-cart-gifts-bar
    data-vendr-cart-context="page"
    data-skip-subtree-update
  ></div>

  <div class="cart-page__items">
    {%- content_for 'block', id: 'cart-page-items', type: '_cart-products' %}
  </div>

  {%- comment -%} VENDR — App embed mount (free gifts picker). {%- endcomment -%}
  <div
    data-vendr-cart-gifts
    data-vendr-cart-context="page"
    data-skip-subtree-update
  ></div>

  <div
    data-vendr-cart-upsell
    data-vendr-cart-context="page"
    data-skip-subtree-update
  ></div>
</div>

Add the summary attributes to the existing cart summary block. Shipping estimate prepends into .cart-totals inside it.

blocks/_cart-summary.liquid

<div
  class="cart-summary border-style"
  data-vendr-cart-summary
  data-vendr-cart-context="page"
  {{ block.shopify_attributes }}
>

Policies Nav

layout/theme.liquid, as the first child of main. The embed replaces this host on policy pages. If the host is missing, the script prepends to main instead.

layout/theme.liquid

<main id="MainContent" class="content-for-layout" role="main">
  <div data-vendr-policies-nav></div>