> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Shopify/horizon/llms.txt
> Use this file to discover all available pages before exploring further.

# Product Page Features

> Product information section with media gallery, sticky add to cart, and flexible layout options

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:

```liquid theme={null}
{% 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

```liquid theme={null}
{% 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

```liquid theme={null}
{% 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 %}
```

### Sticky Bar Components

<Tabs>
  <Tab title="Product Image">
    ```liquid theme={null}
    {% 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
  </Tab>

  <Tab title="Product Info">
    ```liquid theme={null}
    <div class="sticky-add-to-cart__info"
         data-has-image="{{ has_image }}"
         data-singleton="{{ product.has_only_default_variant }}"
         data-single-option="{{ has_single_option }}">
      
      <h3 class="sticky-add-to-cart__title">{{ product.title | escape }}</h3>
      
      <div class="sticky-add-to-cart__variant">
        {{ current_variant.title | escape }}
      </div>
    </div>
    ```

    **Dynamic Attributes:**

    * `data-has-image`: Controls layout spacing
    * `data-singleton`: Hides variant text for single-variant products
    * `data-single-option`: Special handling for single-option products
  </Tab>

  <Tab title="Price Display">
    ```liquid theme={null}
    <div class="sticky-add-to-cart__price">
      {% render 'price',
        product_resource: product,
        show_sale_price_first: true
      %}
    </div>
    ```

    Displays current variant price with sale price handling.
  </Tab>

  <Tab title="Add to Cart Button">
    ```liquid theme={null}
    <button type="button"
            class="sticky-add-to-cart__button add-to-cart-button button"
            ref="addToCartButton"
            on:click="/handleAddToCartClick"
            {% unless current_variant.available %}disabled{% endunless %}>
      
      <span class="add-to-cart-text">
        {% if current_variant.available %}
          <span class="svg-wrapper add-to-cart-icon">
            {{ 'icon-add-to-cart.svg' | inline_asset_content }}
          </span>
        {% endif %}
        
        <span class="add-to-cart-text__content">
          {% if current_variant.available %}
            {{ 'products.product.add_to_cart' | t }}
          {% elsif current_variant == blank %}
            {{ 'products.product.unavailable' | t }}
          {% else %}
            {{ 'products.product.sold_out' | t }}
          {% endif %}
        </span>
      </span>
      
      <span class="add-to-cart__added">
        <span class="svg-wrapper add-to-cart__added-icon">
          {{ 'icon-checkmark-burst.svg' | inline_asset_content }}
        </span>
      </span>
    </button>
    ```

    **States:**

    * Available: Shows icon + "Add to cart" + quantity
    * Sold out: Shows "Sold out" (disabled)
    * Unavailable: Shows "Unavailable" (disabled)
    * Added: Checkmark animation
  </Tab>
</Tabs>

### Quantity Display

When quantity > 1, show the quantity in the button:

```liquid theme={null}
<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 Bar Positioning

```css theme={null}
.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:**

```css theme={null}
transition-property: transform, opacity, display;
transition-duration: 0.3s;
transition-timing-function: var(--ease-out-quad);
transition-behavior: allow-discrete;
```

### Mobile Responsiveness

<CodeGroup>
  ```css Desktop theme={null}
  .sticky-add-to-cart__bar {
    bottom: 20px;
    width: 600px;
    border-radius: var(--style-border-radius-buttons-primary);
  }
  ```

  ```css Mobile theme={null}
  @media screen and (max-width: 749px) {
    .sticky-add-to-cart__bar {
      bottom: 0;
      width: 100%;
      max-width: none;
      border-radius: 0;
    }
    
    /* Show only icon on mobile */
    .sticky-add-to-cart__bar .add-to-cart-text__content {
      display: none;
    }
    
    /* Icon-only button */
    .sticky-add-to-cart__button {
      width: var(--height-buy-buttons);
    }
  }
  ```

  ```css Small Mobile theme={null}
  @media screen and (max-width: 389px) {
    /* Hide title for multi-variant products */
    .sticky-add-to-cart__title {
      display: none;
    }
    
    /* Show title for single variant */
    .sticky-add-to-cart__info[data-singleton='true'] .sticky-add-to-cart__title {
      display: block;
    }
    
    /* Hide compare price on very small screens */
    .sticky-add-to-cart__bar .compare-at-price {
      display: none;
    }
  }
  ```
</CodeGroup>

## Layout Settings

### Content Width

```yaml theme={null}
content_width:
  options:
    - content-center-aligned (Page width)
    - content-full-width (Full width)
  default: content-center-aligned
```

### Media Position

```yaml theme={null}
desktop_media_position:
  options:
    - left (default)
    - right
```

**Controls which side the product media appears on desktop.**

### Equal Columns

```yaml theme={null}
equal_columns:
  type: checkbox
  default: false
  description: Make media and details columns equal width
```

When enabled:

```css theme={null}
.product-information {
  grid-template-columns: 1fr 1fr;
}
```

When disabled (default):

```css theme={null}
.product-information {
  grid-template-columns: 3fr 2fr; /* Media gets more space */
}
```

### Limit Details Width

```yaml theme={null}
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

```yaml theme={null}
gap:
  min: 0
  max: 48
  step: 4
  unit: px
  default: 16
```

## Structured Data

Automatic JSON-LD schema for SEO:

```liquid theme={null}
<script type="application/ld+json">
  {{ closest.product | structured_data }}
</script>
```

**Generates:**

```json theme={null}
{
  "@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

```yaml theme={null}
color_scheme:
  type: color_scheme
  default: scheme-1
```

Applied to the entire product information section.

## Padding Settings

<Tabs>
  <Tab title="Top Padding">
    ```yaml theme={null}
    padding-block-start:
      min: 0
      max: 100
      step: 1
      unit: px
      default: 0
    ```
  </Tab>

  <Tab title="Bottom Padding">
    ```yaml theme={null}
    padding-block-end:
      min: 0
      max: 100
      step: 1
      unit: px
      default: 0
    ```
  </Tab>
</Tabs>

## App Blocks

Supports app blocks for third-party integrations:

```json theme={null}
{
  "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 theme={null}
{% 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:

```css theme={null}
.sticky-add-to-cart__bar[data-stuck='true'] {
  view-transition-name: sticky-header;
}
```

## Accessibility

<AccordionGroup>
  <Accordion title="ARIA Labels">
    ```liquid theme={null}
    <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>
    ```
  </Accordion>

  <Accordion title="Keyboard Navigation">
    * Tab to navigate to sticky bar
    * Enter/Space to add to cart
    * Focus visible on all interactive elements
  </Accordion>

  <Accordion title="Screen Readers">
    * Product title announced
    * Variant selection announced
    * Price changes announced
    * Availability status announced
  </Accordion>
</AccordionGroup>

## Performance

### Image Optimization

```liquid theme={null}
{{ 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

```json theme={null}
{
  "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
    }
  ]
}
```
