Skip to main content
The Horizon product page provides a flexible, responsive layout for showcasing products with advanced features like sticky add-to-cart, media galleries, and customizable product details.

Key Features

  • Flexible Layout: Choose media position (left/right), equal columns, and content width
  • Sticky Add to Cart: Floating bar that appears when scrolling
  • Media Gallery: Support for images, videos, and 3D models
  • Variant Selection: Real-time updates with quantity rules support
  • Responsive Design: Optimized for mobile, tablet, and desktop
  • Structured Data: Automatic JSON-LD schema markup

Product Information Structure

The product page is divided into three main sections:
{% capture media_gallery %}
  {% content_for 'block',
    type: '_product-media-gallery',
    id: 'media-gallery',
    closest.product: closest.product
  %}
{% endcapture %}

{% capture product_details %}
  {% content_for 'block',
    type: '_product-details',
    id: 'product-details',
    closest.product: closest.product
  %}
{% endcapture %}

{% capture additional_blocks %}
  {% content_for 'blocks' %}
{% endcapture %}

Rendering the Layout

{% render 'product-information-content',
  product_media_size: closest.product.media.size,
  section_id: section.id,
  settings: section.settings,
  media_gallery: media_gallery,
  product_details: product_details,
  additional_blocks: additional_blocks
%}

Sticky Add to Cart

The sticky add-to-cart bar appears when scrolling past the main product form.

Feature Overview

{% if section.settings.enable_sticky_add_to_cart %}
  <sticky-add-to-cart
    class="sticky-add-to-cart"
    data-variant-available="{{ current_variant.available }}"
    data-product-id="{{ product.id }}"
    data-current-variant-id="{{ current_variant.id }}"
    data-initial-quantity="{{ initial_quantity }}"
  >
    <div class="sticky-add-to-cart__bar" ref="stickyBar">
      <!-- Sticky bar content -->
    </div>
  </sticky-add-to-cart>
{% endif %}
{% assign image_to_show = current_variant.featured_image | default: product.featured_image %}
{% if image_to_show %}
  <div class="sticky-add-to-cart__image">
    {{ image_to_show
      | image_url: width: 120
      | image_tag:
        loading: 'lazy',
        alt: image_alt,
        class: 'sticky-add-to-cart__image-img',
        ref: 'productImage'
    }}
  </div>
{% endif %}
Features:
  • Shows variant image if available
  • Falls back to product featured image
  • Square aspect ratio
  • Lazy loaded

Quantity Display

When quantity > 1, show the quantity in the button:
<span ref="quantityDisplay"
      {% if current_variant.available and initial_quantity > 1 %}
        style="display: inline;"
      {% else %}
        style="display: none;"
      {% endif %}>
  {{ ' (' }}
  <span ref="quantityNumber">{{ initial_quantity }}</span>
  {{ ')' }}
</span>
.sticky-add-to-cart__bar {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%) translateY(calc(100% + 40px));
  z-index: calc(var(--layer-sticky) - 1);
  width: 600px;
  border-radius: calc(
    var(--style-border-radius-buttons-primary) + 
    min(var(--padding-sm), var(--style-border-radius-buttons-primary))
  );
  box-shadow: var(--shadow-popover);
  padding: var(--padding-sm);
  display: flex;
  align-items: center;
  gap: var(--gap-md);
}

.sticky-add-to-cart__bar[data-stuck='true'] {
  transform: translateX(-50%) translateY(0%);
  opacity: 1;
}
Transition:
transition-property: transform, opacity, display;
transition-duration: 0.3s;
transition-timing-function: var(--ease-out-quad);
transition-behavior: allow-discrete;

Mobile Responsiveness

.sticky-add-to-cart__bar {
  bottom: 20px;
  width: 600px;
  border-radius: var(--style-border-radius-buttons-primary);
}

Layout Settings

Content Width

content_width:
  options:
    - content-center-aligned (Page width)
    - content-full-width (Full width)
  default: content-center-aligned

Media Position

desktop_media_position:
  options:
    - left (default)
    - right
Controls which side the product media appears on desktop.

Equal Columns

equal_columns:
  type: checkbox
  default: false
  description: Make media and details columns equal width
When enabled:
.product-information {
  grid-template-columns: 1fr 1fr;
}
When disabled (default):
.product-information {
  grid-template-columns: 3fr 2fr; /* Media gets more space */
}

Limit Details Width

limit_details_width:
  type: checkbox
  default: false
  visible_if: equal_columns == true
When using equal columns, optionally constrain the product details to a maximum width for better readability.

Gap Between Columns

gap:
  min: 0
  max: 48
  step: 4
  unit: px
  default: 16

Structured Data

Automatic JSON-LD schema for SEO:
<script type="application/ld+json">
  {{ closest.product | structured_data }}
</script>
Generates:
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Product Name",
  "image": [...],
  "description": "...",
  "sku": "...",
  "offers": {
    "@type": "Offer",
    "price": "29.99",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock"
  }
}

Color Scheme

color_scheme:
  type: color_scheme
  default: scheme-1
Applied to the entire product information section.

Padding Settings

padding-block-start:
  min: 0
  max: 100
  step: 1
  unit: px
  default: 0

App Blocks

Supports app blocks for third-party integrations:
{
  "blocks": [
    {
      "type": "@app"
    }
  ]
}
Use Cases:
  • Product reviews
  • Size guides
  • Wishlist buttons
  • Custom product options
  • Subscriptions

Quantity Rules Support

Automatic support for Shopify quantity rules:
{% liquid
  assign current_variant = product.selected_or_first_available_variant
  assign initial_quantity = current_variant.quantity_rule.min | default: 1
%}
Features:
  • Minimum quantity enforcement
  • Quantity increments
  • Maximum quantity limits

View Transitions

Smooth animations when variant changes:
.sticky-add-to-cart__bar[data-stuck='true'] {
  view-transition-name: sticky-header;
}

Accessibility

<div role="region" aria-label="{{ 'products.product.sticky_add_to_cart' | t }}">
  <button aria-label="{{ 'products.product.add_to_cart' | t }}">
    Add to Cart
  </button>
</div>
  • Tab to navigate to sticky bar
  • Enter/Space to add to cart
  • Focus visible on all interactive elements
  • Product title announced
  • Variant selection announced
  • Price changes announced
  • Availability status announced

Performance

Image Optimization

{{ image_to_show | image_url: width: 120 | image_tag: loading: 'lazy' }}
  • Lazy loading for images
  • Responsive image sizing
  • WebP format support (automatic)

JavaScript Loading

The sticky add-to-cart uses lightweight vanilla JavaScript with no dependencies.

Complete Schema

{
  "name": "Product Information",
  "blocks": [
    { "type": "@app" }
  ],
  "disabled_on": {
    "groups": ["header", "footer"]
  },
  "settings": [
    {
      "type": "select",
      "id": "content_width",
      "label": "Width",
      "options": [
        { "value": "content-center-aligned", "label": "Page" },
        { "value": "content-full-width", "label": "Full" }
      ],
      "default": "content-center-aligned"
    },
    {
      "type": "select",
      "id": "desktop_media_position",
      "label": "Media Position",
      "options": [
        { "value": "left", "label": "Left" },
        { "value": "right", "label": "Right" }
      ],
      "default": "left"
    },
    {
      "type": "checkbox",
      "id": "equal_columns",
      "label": "Equal columns",
      "default": false
    },
    {
      "type": "checkbox",
      "id": "limit_details_width",
      "label": "Limit product details width",
      "default": false,
      "visible_if": "{{ section.settings.equal_columns }}"
    },
    {
      "type": "range",
      "id": "gap",
      "label": "Gap",
      "min": 0,
      "max": 48,
      "step": 4,
      "unit": "px",
      "default": 16
    },
    {
      "type": "checkbox",
      "id": "enable_sticky_add_to_cart",
      "label": "Enable sticky add to cart",
      "default": true
    },
    {
      "type": "color_scheme",
      "id": "color_scheme",
      "label": "Color scheme",
      "default": "scheme-1"
    },
    {
      "type": "range",
      "id": "padding-block-start",
      "label": "Top padding",
      "min": 0,
      "max": 100,
      "step": 1,
      "unit": "px",
      "default": 0
    },
    {
      "type": "range",
      "id": "padding-block-end",
      "label": "Bottom padding",
      "min": 0,
      "max": 100,
      "step": 1,
      "unit": "px",
      "default": 0
    }
  ]
}

Build docs developers (and LLMs) love