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

# Catalog template

> Reference for Liquid tags supported in the Customer Portal product catalog template (catalog.liquid), grouped by default and optional usage.

`catalog.liquid` tags are grouped by default vs optional usage.

* Default components are already present in the shipped `catalog.liquid` template.
* Optional components can be added or used to replace parts of the default layout.

Shared page components are documented on [Shared page components](./common): `{% legacy_navbar %}`.

## Default components

## `{% additions_filter_aggregator %}`

```liquid theme={null}
{% additions_filter_aggregator %}
```

```html theme={null}
<div class="filter-aggregator-outlet" data-controller="ssc--filter-aggregator">
  <form id="filter-aggregator-form" style="display: none;">
    <input type="hidden" name="query" value="oat" />
    <input type="hidden" name="collection_id" value="2" />
  </form>
</div>
```

## `{% additions_search %}`

```liquid theme={null}
{% additions_search %}
```

```html theme={null}
<div data-controller="ssc--addition-search">
  <form id="additions-search-form">
    <input id="additions-search-input" type="search" placeholder="Search products" />
    <input type="hidden" name="collection_id" value="2" />
  </form>
</div>
```

## `{% additions_shopify_filters %}`

| Parameter            | Type                        | Default                                                                                |
| -------------------- | --------------------------- | -------------------------------------------------------------------------------------- |
| `slide_from`         | String (`"left"`/`"right"`) | `"right"`                                                                              |
| `button_classes`     | String                      | `"button button--dark px-6 py-3"`                                                      |
| `show_counts`        | Boolean                     | `false`                                                                                |
| `show_clear_filters` | Presence-based              | shown when omitted                                                                     |
| `clear_link_classes` | String                      | `"text-sm font-medium text-gray-600 hover:text-gray-900 underline underline-offset-2"` |
| `clear_link_text`    | String                      | `"Clear all"`                                                                          |

```liquid theme={null}
{% additions_shopify_filters slide_from: "left" show_counts: true %}
```

```html theme={null}
<div class="flex items-center gap-3">
  <button type="button" class="button">Filter</button>
  <a href="/self-service/additions/new" class="text-sm underline">Clear all</a>
</div>
```

## `{% additions_turbo_frame %} ... {% endadditions_turbo_frame %}`

Block tag for catalog results frame.

```liquid theme={null}
{% additions_turbo_frame %}
  ...
{% endadditions_turbo_frame %}
```

```html theme={null}
<turbo-frame id="search_results" src="/self-service/additions/catalog_frame?...">
  <div class="grid grid-cols-2 gap-4">
    <div>...product card...</div>
    <div>...product card...</div>
  </div>
</turbo-frame>
```

## `{% additions_collection_filter_pills %}`

```liquid theme={null}
{% additions_collection_filter_pills %}
```

```html theme={null}
<div class="mb-6">
  <div class="flex flex-wrap gap-2 items-center">
    <a class="pill pill--active" href="?collection_id=1">All</a>
    <a class="pill" href="?collection_id=2">Refills</a>
    <a class="pill" href="?collection_id=3">Accessories</a>
  </div>
</div>
```

## `{% additions_form_wrapper %} ... {% endadditions_form_wrapper %}`

Block tag that wraps selected products in confirmation form.

```liquid theme={null}
{% additions_form_wrapper %}
  ...
{% endadditions_form_wrapper %}
```

```html theme={null}
<form action="/self-service/additions/confirmation" method="get" data-controller="ssc--quantity-badge ssc--addition-status">
  <input type="hidden" name="ordered_products[][product_id]" value="44" />
  <input type="hidden" name="ordered_products[][quantity]" value="2" />
  <div>...product cards...</div>
</form>
```

## `{% additions_product_card %}`

| Parameter                                 | Type                   | Default                                                                                                                                                                 |
| ----------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `product`                                 | Additions product drop | required                                                                                                                                                                |
| `exclude_main_product_name_from_variants` | Boolean                | `true`                                                                                                                                                                  |
| `hide_frequency_label`                    | Boolean                | `false`                                                                                                                                                                 |
| `use_variant_pills`                       | Boolean                | `false`                                                                                                                                                                 |
| `hide_variant_selector`                   | Boolean                | `false`                                                                                                                                                                 |
| `hide_price`                              | Boolean                | `false`                                                                                                                                                                 |
| `show_info_icon`                          | Boolean                | `false`                                                                                                                                                                 |
| `info_open_event`                         | String                 | `"ssc:product-info:open"`                                                                                                                                               |
| `show_collection_title`                   | Boolean                | `false`                                                                                                                                                                 |
| `active_card_class`                       | String                 | `"ring-2 ring-emerald-500"`                                                                                                                                             |
| `add_button_classes`                      | String                 | `"text-xs font-medium text-gray-700 bg-white border border-gray-300 rounded-xl flex items-center justify-center gap-1 px-3 h-10 hover:bg-gray-100 hover:text-gray-900"` |
| `increment_button_classes`                | String                 | `"bg-white border border-gray-300 rounded-xl p-0 w-10 h-10 min-w-0 flex items-center justify-center hover:bg-gray-100"`                                                 |
| `decrement_button_classes`                | String                 | `"bg-white border border-gray-300 rounded-xl p-0 w-10 h-10 min-w-0 flex items-center justify-center hover:bg-gray-100"`                                                 |
| `quantity_input_classes`                  | String                 | `"form-input w-12 h-10 text-center text-sm shadow-none"`                                                                                                                |

```liquid theme={null}
{% additions_product_card product: product use_variant_pills: true show_info_icon: true %}
```

Set `show_info_icon: true` to show an information button on each product image in both the add-products and replacement catalogs. In replacement catalogs, customers can also inspect the product they are replacing. Opening product information does not select a replacement or change its quantity.

The button dispatches `ssc:product-info:open` on `window`. Your custom catalog template supplies the information panel and handles this event to display the dish description, ingredients, or other product details. Enabling the icon alone does not provide that content. If your template already handles this event for add-products cards, the same handler works for replacement cards.

The event's `detail` contains `productId` (the Firmhouse product ID), `shopifyProductId`, and `shopifyVariantId` for the selected variant. Shopify IDs are `null` when unavailable. Set `info_open_event` to use a different event name, and make sure your template listens for that name.

Set `hide_variant_selector: true` to render grouped products as a single card without variant pills or a dropdown, so customers add the product without choosing a variant. This applies to both the add-products and replacement catalogs, and it takes precedence over `use_variant_pills`.

Available variants are ordered by the customer's subscription price, cheapest first, including country overrides, Shopify pricing, and plan discounts, with the product ID breaking price ties. For a product without an existing selection, the first variant is selected by default in both catalogs. Existing customer selections are preserved.

In replacement catalogs, set `hide_price: true` to hide the card price. The default catalog template reads this from the replacement flow when the dashboard listing uses `hide_replacement_price: true`.

```html theme={null}
<div id="ordered_product_123" class="rounded-xl bg-white overflow-hidden">
  <img src="https://cdn.example.com/oat-drink.jpg" alt="Oat Drink" />
  <div class="p-4">
    <h3 class="font-bold">Oat Drink</h3>
    <p class="text-xs text-gray-500">Every 2 weeks</p>
    <div class="flex justify-between items-center mt-3">
      <span>EUR 7.50</span>
      <button type="button">+</button>
    </div>
  </div>
</div>
```

## `{% additions_lazy_page_frame %}`

Loads the next page of products when the bottom of the catalog scrolls into view. Place it directly after the product grid, as a sibling of the grid element, so each page renders as its own full-width responsive grid.

```liquid theme={null}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
  {% for product in products %}
    {% additions_product_card product: product %}
  {% endfor %}
</div>
{% additions_lazy_page_frame %}
```

The tag renders nothing when there are no more products.

## `{% additions_empty_state %}`

```liquid theme={null}
{% additions_empty_state %}
```

```html theme={null}
<div id="additions-empty-state" class="text-center py-16 px-8">
  <p class="text-lg text-gray-600">No products found</p>
  <p class="text-sm text-gray-500">Try another search term or filter.</p>
</div>
```

## `{% additions_action_bar %}`

Sticky bottom action bar for confirming product changes.

```liquid theme={null}
{% additions_action_bar %}
```

```html theme={null}
<div id="additions-action-bar" class="fixed bottom-0 left-0 right-0 bg-white py-4 border-y">
  <div class="max-w-6xl mx-auto flex items-center gap-4">
    <span id="addition-status-message">2 items added</span>
    <input id="additions-review-button" type="submit" value="Review changes" />
  </div>
</div>
```

## Optional components

## `{% additions_shopify_filter_list %}`

Used mainly when composing a custom sidebar or filter layout.

| Parameter     | Type    | Default |
| ------------- | ------- | ------- |
| `show_counts` | Boolean | `false` |

```liquid theme={null}
{% additions_shopify_filter_list show_counts: true %}
```

```html theme={null}
<turbo-frame id="shopify_filter_list" src="/self-service/additions/filter_list?...">
  <div class="filter-category">
    <h4>Diet</h4>
    <label><input type="checkbox" /> Vegan (12)</label>
    <label><input type="checkbox" /> Gluten-free (8)</label>
  </div>
</turbo-frame>
```

## `{% additions_sidebar_pane %} ... {% endadditions_sidebar_pane %}`

Block tag for custom side panel layouts.

| Parameter            | Type                        | Default                                                                                                                          |
| -------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `button_text`        | String                      | `"Menu"`                                                                                                                         |
| `button_classes`     | String                      | `"px-6 py-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-xl hover:bg-gray-100 hover:text-gray-900"` |
| `slide_from`         | String (`"left"`/`"right"`) | `"right"`                                                                                                                        |
| `sidebar_title`      | String                      | `button_text`                                                                                                                    |
| `sidebar_id`         | String                      | generated                                                                                                                        |
| `show_clear_filters` | Presence-based              | shown when omitted                                                                                                               |
