Skip to main content

Overview

Horizon includes two main button snippets for different use cases:
  • button.liquid - Styled link buttons for navigation
  • add-to-cart-button.liquid - Product add to cart functionality

File Location: snippets/button.liquid Renders a styled link element that looks like a button. Used for navigation and CTAs throughout the theme.

Parameters

URL to link to. If blank, renders a disabled link with aria-disabled="true".
block
object
required
Block object containing settings for label, style, and behavior.Required Settings:
  • label - Button text
  • style_class - Button style class
  • open_in_new_tab - Whether to open in new tab

Usage

Basic Example

{% render 'button', link: '/collections/all' %}

Complete Block Example

blocks/button.liquid
{% render 'button', link: block.settings.link %}

{% schema %}
{
  "name": "Button",
  "settings": [
    {
      "type": "text",
      "id": "label",
      "label": "Label",
      "default": "Shop Now"
    },
    {
      "type": "url",
      "id": "link",
      "label": "Link"
    },
    {
      "type": "checkbox",
      "id": "open_in_new_tab",
      "label": "Open in new tab",
      "default": false
    },
    {
      "type": "select",
      "id": "style_class",
      "label": "Style",
      "options": [
        { "value": "button", "label": "Primary" },
        { "value": "button-secondary", "label": "Secondary" },
        { "value": "link", "label": "Link" }
      ],
      "default": "button"
    }
  ]
}
{% endschema %}

Button Styles

The snippet supports three button styles via block.settings.style_class:
{%- assign block.settings.style_class = "button" -%}
{% render 'button', link: '/collections/all' %}

<!-- Renders as -->
<a href="/collections/all" class="button">
  Shop Now
</a>

Open in New Tab

When block.settings.open_in_new_tab is enabled:
button.liquid
<a
  href="{{ link }}"
  {%- if block_settings.open_in_new_tab -%}
    target="_blank"
    rel="noopener noreferrer"
  {%- endif -%}
>
  {{ block_settings.label }}
</a>
The rel="noopener noreferrer" attribute is included for security when opening links in new tabs.

Disabled State

When link is blank, the button renders in a disabled state:
button.liquid
<a
  {% if link == blank %}
    role="link"
    aria-disabled="true"
  {% else %}
    href="{{ link }}"
  {% endif %}
>
  {{ block_settings.label }}
</a>

Styling

The snippet includes scoped styles for link buttons:
button.liquid (stylesheet)
.link {
  text-decoration: none;
  text-decoration-color: currentcolor;

  &:hover {
    color: var(--color-primary-hover);
    text-decoration-color: transparent;
  }
}

Add to Cart Button

File Location: snippets/add-to-cart-button.liquid Renders an “Add to Cart” button with dynamic states, animations, and visual feedback.

Parameters

can_add_to_cart
boolean
required
Whether the product can be added to the cart. When false, button is disabled.
add_to_cart_text
string
Custom button text. Falls back to translation 'actions.add_to_cart'.
product
object
required
The product object to be added to the cart.
icon_only_on_mobile
boolean
If true, only shows the cart icon on mobile devices (hides text).Default: false
class
string
Additional CSS classes to apply to the button.
id
string
The ID attribute for the button element.
data_testid
string
Testing identifier for automated tests.

Usage

Basic Example

{% render 'add-to-cart-button',
  can_add_to_cart: product.available,
  product: product
%}

Complete Product Form Example

blocks/add-to-cart.liquid
{% liquid
  assign class = 'add-to-cart-button ' | append: block.settings.style_class
  assign id = 'BuyButtons-ProductSubmitButton-' | append: block.id
%}

<span
  {{ block.shopify_attributes }}
  style="--add-to-cart-font-case: {{ settings.button_text_case }};"
>
  {% render 'add-to-cart-button',
    id: id,
    class: class,
    can_add_to_cart: can_add_to_cart,
    product: closest.product,
    add_to_cart_text: add_to_cart_text,
    data_testid: 'standalone-add-to-cart'
  %}
</span>

With Custom Text

{% render 'add-to-cart-button',
  can_add_to_cart: product.available,
  product: product,
  add_to_cart_text: 'Buy Now',
  class: 'button-large'
%}

Icon Only on Mobile

{% render 'add-to-cart-button',
  can_add_to_cart: product.available,
  product: product,
  icon_only_on_mobile: true,
  class: 'quick-add-button'
%}

Button States

The button supports multiple visual states:
{% render 'add-to-cart-button',
  can_add_to_cart: true,
  product: product
%}
Shows cart icon + text, fully interactive.
{% render 'add-to-cart-button',
  can_add_to_cart: false,
  product: product,
  add_to_cart_text: 'Sold Out'
%}
Button is disabled, no icon shown.
<button type="submit" disabled>
  <span class="add-to-cart-text__content">
    Sold Out
  </span>
</button>
Shows checkmark burst animation after successful add to cart:
<span class="add-to-cart__added">
  <span class="svg-wrapper add-to-cart__added-icon">
    {{- 'icon-checkmark-burst.svg' | inline_asset_content -}}
  </span>
</span>
Triggered by JavaScript on successful cart addition.

Add to Cart Animations

The button supports different animation styles via settings.add_to_cart_animation:
add-to-cart-button.liquid
<add-to-cart-component
  ref="addToCartButtonContainer"
  data-product-variant-media="{{ product_variant_media }}"
  data-add-to-cart-animation="{{ settings.add_to_cart_animation }}"
>
  <!-- button markup -->
</add-to-cart-component>
Animation Types:
  • Checkmark burst (default)
  • Product image fly-to-cart
  • Ripple effect

Product Variant Media

The snippet automatically determines the correct variant image:
add-to-cart-button.liquid
{%- liquid
  assign product_variant_media = product.selected_or_first_available_variant.featured_media.preview_image | image_url: width: 100
  if product.selected_or_first_available_variant.featured_media.preview_image == blank
    assign product_variant_media = product.featured_media.preview_image | image_url: width: 100
  endif
-%}

<add-to-cart-component
  data-product-variant-media="{{ product_variant_media }}"
>
This image is used for cart animations.

Icon Only Mobile

Hides text on mobile when icon_only_on_mobile is true:
add-to-cart-button.liquid
<span class="add-to-cart-text__content{% if icon_only_on_mobile %} is-visually-hidden-mobile{% endif %}">
  <span>
    <span>
      {{- add_to_cart_text | default: default_add_to_cart_text -}}
    </span>
  </span>
</span>
Useful for compact layouts like product cards or quick add buttons.

Button Markup

Complete button structure:
<add-to-cart-component
  ref="addToCartButtonContainer"
  data-product-variant-media="..."
  data-add-to-cart-animation="..."
>
  <button
    id="{{ id }}"
    type="submit"
    name="add"
    ref="addToCartButton"
    on:click="/handleClick"
    class="button {{ class }}"
    data-testid="{{ data_testid }}"
  >
    <span class="add-to-cart-text">
      <span class="svg-wrapper add-to-cart-icon">
        <!-- Cart icon SVG -->
      </span>
      <span class="add-to-cart-text__content">
        Add to Cart
      </span>
    </span>
    <span class="add-to-cart__added">
      <span class="svg-wrapper add-to-cart__added-icon">
        <!-- Checkmark burst SVG -->
      </span>
    </span>
  </button>
</add-to-cart-component>

JavaScript Events

The button emits events for cart updates:
button.on('click', '/handleClick')
Handled by the add-to-cart-component web component.

Accessibility

Uses proper <button type="submit"> element with name="add" for form submission.
<button disabled>
  Sold Out
</button>
Properly disabled when product unavailable.
Text remains accessible to screen readers even when hidden on mobile:
<span class="is-visually-hidden-mobile">
  Add to Cart
</span>

Examples

<product-form>
  <form method="post" action="/cart/add">
    <!-- Variant selector -->
    
    {% render 'add-to-cart-button',
      can_add_to_cart: product.available,
      product: product,
      add_to_cart_text: 'Add to Cart',
      class: 'button-primary'
    %}
  </form>
</product-form>

Block Schema Settings

When used in blocks, common settings include:
{
  "type": "add-to-cart",
  "name": "Add to Cart",
  "settings": [
    {
      "type": "select",
      "id": "style_class",
      "label": "Style",
      "options": [
        { "value": "button", "label": "Primary" },
        { "value": "button-secondary", "label": "Secondary" }
      ],
      "default": "button-secondary"
    }
  ]
}
  • quick-add.liquid - Quick add modal functionality
  • Cart Summary - Cart totals and checkout
  • quantity-selector.liquid - Product quantity input

Build docs developers (and LLMs) love