# Tamamlayıcı ürünler: ücretsiz Shopify bölümü

> Ürün sayfasında "Bununla iyi gider" kartları: Search & Discovery’de seçtiğiniz tamamlayıcı ürünler, hızlı sepete ekleme düğmesiyle. Ücretli uygulama gerekmez.

Tamamlayıcı ürünler, ürün sayfasında birlikte iyi giden ürünleri gösteren ücretsiz bir Shopify bölümüdür ve Shopify’ın kendi ürün önerilerini tamamlayıcı (complementary) amaçla kullanır. Her ürünün tamamlayıcı ürünlerini Shopify’ın ücretsiz Search & Discovery uygulamasında siz seçersiniz; bölüm ekrana yaklaştığında bunları mağazanın öneri adresinden çeker ve fiyat, indirim ve tükendi rozetleri ile isteğe bağlı sepete ekle düğmesi olan kartlar halinde gösterir. Tamamlayıcı ürün belirlenmemiş ürünlerde hiçbir şey görünmez. Online Store 2.0 temalarının ürün şablonlarında, ücretli bir uygulama olmadan çalışır.

- **Kategori:** Ürün ve koleksiyon keşfi
- **Kullanıldığı yerler:** Ürün
- **Okuduğu veri:** Shopify ürün önerileri (tamamlayıcı amaç)
- **Boyut:** 3 dosyada 30 KB, küçük bir betikle
- **Lisans:** MIT, © 2026 Lintel
- **Sayfa:** https://lintelagency.com/tr/kaynaklar/shopify-bolumleri/tamamlayici-urunler
- **Canlı demo:** https://lintelagency.com/resources/shopify-sections/demo/lib-complementary-products.html
- **Eklenme tarihi:** 2026-11-02

## Ne işe yarar

- Ürün açıklamasının altında aksesuar ızgarası: fincanın yanında tabağı, lambanın yanında ampulü
- Satın alma alanının altında, küçük ek ürünlerin tek tıkla sepete eklenebildiği kısa bir liste
- Bakım ürünlerini korudukları ürünle birlikte sunmak (deri çantayla bakım kremi)

## Tema düzenleyici ayarları

Bölüm tema düzenleyicide “Lib · Complementary” adıyla görünür. Kimlikler, türler, etiketler ve varsayılan değerler bölümün şemasından gelir. Etiketler tema düzenleyicide göründüğü gibi İngilizcedir.

### Content

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `heading` | `inline_richtext` | Heading | Pairs well with |  |
| `heading_tag` | `select` | Heading tag | h2 | `h1`, `h2`, `h3`, `p` |
| `heading_size` | `select` | Heading size | medium | `small`, `medium`, `large` |
| `text` | `richtext` | Intro text | – |  |

### Products

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `products_to_show` | `range` | Products to show | `4` | 1–10, step 1 |
| `layout` | `select` | Layout | grid | `grid`, `list` |
| `image_ratio` | `select` | Image shape | square | `square`, `portrait`, `tall`, `landscape` |
| `card_style` | `select` | Card style | plain | `plain`, `card` |
| `card_radius` | `range` | Corner radius | `12` | 0–24 px, step 2 |
| `show_vendor` | `checkbox` | Show vendor | `false` |  |
| `show_quick_add` | `checkbox` | Show quick add | `true` |  |
| `quick_add_label` | `text` | Quick add button | Add to cart |  |
| `choose_label` | `text` | Link for products with options | Choose options |  |
| `sale_label` | `text` | Sale badge | Sale |  |
| `sold_out_label` | `text` | Sold out badge | Sold out |  |
| `sale_price_label` | `text` | Sale price label (screen readers) | Sale price |  |
| `regular_price_label` | `text` | Regular price label (screen readers) | Regular price |  |

### Layout

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `columns` | `range` | Columns on desktop (grid) | `4` | 2–5, step 1 |
| `columns_mobile` | `range` | Columns on mobile (grid) | `2` | 1–2, step 1 |
| `section_width` | `select` | Width | page | `page`, `full` |
| `text_alignment` | `select` | Heading alignment on desktop | left | `left`, `center` |
| `text_alignment_mobile` | `select` | Heading alignment on mobile | left | `left`, `center` |

### Colors

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `color_bg` | `color` | Background | #FFFFFF |  |
| `color_bg_gradient` | `color_background` | Background gradient | – |  |
| `color_text` | `color` | Text | #1F1B17 |  |
| `color_accent` | `color` | Accent (buttons, badges) | #7A5A3A |  |
| `color_accent_text` | `color` | Text on accent | #FFFFFF |  |
| `color_card` | `color` | Card background | #FAF6F1 |  |

### Spacing and visibility

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `padding_top` | `range` | Top padding on desktop | `56` | 0–200 px, step 4 |
| `padding_bottom` | `range` | Bottom padding on desktop | `56` | 0–200 px, step 4 |
| `padding_top_mobile` | `range` | Top padding on mobile | `40` | 0–160 px, step 4 |
| `padding_bottom_mobile` | `range` | Bottom padding on mobile | `40` | 0–160 px, step 4 |
| `show_on` | `select` | Show on | all | `all`, `desktop`, `mobile` |
| `anchor_id` | `text` | Anchor ID | – |  |

### Motion

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `animate` | `checkbox` | Fade cards in as they load | `true` |  |

## Kurulum

### Shopify yönetim panelinde

1. Canlı temanızda değil, bir kopyasında çalışın: Shopify yönetim panelinde **Online Mağaza > Temalar** sayfasına gidin, mevcut temanızın **…** menüsünü açın ve **Kopya oluştur**’u seçin.
2. Kopyada **…** menüsünü açın ve **Kodu düzenle**’yi seçin.
3. `sections` klasörüne `lib-complementary-products.liquid` adında yeni bir dosya ekleyin, aşağıdaki kaynak koddan `sections/lib-complementary-products.liquid` içeriğini yapıştırıp kaydedin.
4. `lib-product-card.liquid` ve `lib-image.liquid` dosyalarını da aynı şekilde `snippets` klasörüne ekleyin. Başka bir Lib bölümünden gelen aynı adlı bir snippet zaten varsa aynı dosyadır; tek kopya yeterli.
5. Kopyada **Özelleştir**’i açın, **Bölüm ekle**’yi seçin ve **Lib · Complementary** bölümünü ekleyin. Örnek içeriği kendi içeriğinizle değiştirin, renkleri ayarlayın.
6. Kopyayı önizleyin, her şey doğru göründüğünde yayınlayın.

### Shopify CLI ile

Yerel tema klasörünüzde dosyaları indirin ve yalnızca bunları yayında olmayan bir temaya gönderin (temanın kimliğini `shopify theme list` gösterir):

```sh
curl -fsSL -o sections/lib-complementary-products.liquid https://lintelagency.com/resources/shopify-sections/files/lib-complementary-products.liquid
curl -fsSL -o snippets/lib-product-card.liquid https://lintelagency.com/resources/shopify-sections/files/lib-product-card.liquid
curl -fsSL -o snippets/lib-image.liquid https://lintelagency.com/resources/shopify-sections/files/lib-image.liquid
shopify theme push --theme <unpublished-theme-id> --only sections/lib-complementary-products.liquid --only snippets/lib-product-card.liquid --only snippets/lib-image.liquid
```

Canlı temaya asla göndermeyin: her zaman yayında olmayan bir temanın kimliğini verin, `--live` ya da `--allow-live` kullanmayın. Ardından bölümü yukarıdaki gibi tema düzenleyicide ekleyin.

## Dosyalar

- [sections/lib-complementary-products.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-complementary-products.liquid): `sections/` klasörüne
- [snippets/lib-product-card.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-product-card.liquid): `snippets/` klasörüne
- [snippets/lib-image.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-image.liquid): `snippets/` klasörüne

## Sık sorulan sorular

### Tamamlayıcı ürünler nereden geliyor?

Shopify’ın intent=complementary ile çalışan ürün önerilerinden; bunlar Search & Discovery uygulamasında Öneriler altında her ürüne eklediğiniz tamamlayıcı ürünleri döndürür. Shopify bunları kendiliğinden üretmez: tamamlayıcı ürün belirlenmemiş bir üründe hiçbir şey görünmez.

### "Bunları da beğenebilirsiniz" ile aynı şey mi?

Hayır. "Bunları da beğenebilirsiniz" ilgili (related) amacı kullanır ve Shopify bunu satın alma ve ürün açıklaması verilerinden kendiliğinden doldurur. Tamamlayıcı ürünler ise birlikte satın alınması için sizin seçtiğiniz ürünlerdir; bu yüzden bölüm yalnızca bu amacı kullanır.

### Sepete ekleme nasıl çalışıyor?

Tek varyantlı ürünlerde düğme Shopify’ın kendi ürün formunu kullanır ve o varyantı doğrudan sepete ekler. Seçenekli ürünlerde ise "Seçenekleri belirle" bağlantısı ürün sayfasına götürür; müşteri bedeni ya da rengi orada seçer.

### Ürün sayfasını yavaşlatır mı?

Öneriler yalnızca bölüm ekrana yaklaştığında, mağazanıza gönderilen tek bir istekle yüklenir; kartlar duyarlı görseller kullanır. Sayfanın geri kalanı önerilerin yüklenmesini beklemez.

## Kaynak kod

### sections/lib-complementary-products.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{%- comment -%}
  Complementary products: "pairs well with" cards on a product page, from Shopify's own product
  recommendations with the complementary intent. You choose the products per product in Shopify's
  free Search & Discovery app; the section asks the store's recommendations endpoint for them after
  the page loads (Section Rendering API) and stays hidden when there are none. Cards show price,
  badges and an optional quick add (a plain product form for single-variant products).
  Depends on: snippets/lib-product-card.liquid, snippets/lib-image.liquid
{%- endcomment -%}

{%- liquid
  assign s = section.settings
  assign heading_tag = s.heading_tag | default: 'h2'
  assign card_tag = 'h3'
  if heading_tag == 'h3' or heading_tag == 'p'
    assign card_tag = 'h4'
  endif
  assign cols = s.columns | plus: 0
  assign cols_m = s.columns_mobile | plus: 0
  assign img_vw = 100 | divided_by: cols
  assign img_sizes = '(min-width: 990px) ' | append: img_vw | append: 'vw, (min-width: 750px) 33vw, 50vw'
  if s.layout == 'list'
    assign img_sizes = '120px'
  endif
  assign has_results = false
  if recommendations.products_count > 0
    assign has_results = true
  endif
-%}

<lib-complementary-products
  class="lib-compl lib-compl--{{ s.section_width }}"
  {% if s.anchor_id != blank %}
    id="{{ s.anchor_id | handleize }}"
  {% endif %}
  data-show="{{ s.show_on }}"
  data-card="{{ s.card_style }}"
  data-layout="{{ s.layout }}"
  {% if has_results == false and product != blank %}
    data-url="{{ routes.product_recommendations_url }}?section_id={{ section.id }}&product_id={{ product.id }}&limit={{ s.products_to_show }}&intent=complementary"
  {% endif %}
  {% if s.animate %}
    data-animate
  {% endif %}
  {% if has_results == false and request.design_mode == false %}
    hidden
  {% endif %}
  style="
    --lib-bg: {{ s.color_bg }};
    {% if s.color_bg_gradient != blank %}--lib-bg-image: {{ s.color_bg_gradient }};{% endif %}
    --lib-text: {{ s.color_text }};
    --lib-accent: {{ s.color_accent }};
    --lib-accent-text: {{ s.color_accent_text }};
    --lib-card: {{ s.color_card }};
    --lib-cols: {{ cols }};
    --lib-cols-m: {{ cols_m }};
    --lib-radius-card: {{ s.card_radius }}px;
    --lib-pt: {{ s.padding_top }}px;
    --lib-pb: {{ s.padding_bottom }}px;
    --lib-pt-m: {{ s.padding_top_mobile }}px;
    --lib-pb-m: {{ s.padding_bottom_mobile }}px;
  "
>
  <div class="lib-compl__inner" data-results>
    {%- if has_results or request.design_mode -%}
      {%- if s.heading != blank or s.text != blank -%}
        <header class="lib-compl__header" data-align="{{ s.text_alignment }}" data-align-m="{{ s.text_alignment_mobile }}">
          {%- if s.heading != blank -%}
            <{{ heading_tag }} class="lib-compl__heading lib-compl__heading--{{ s.heading_size }}">
              {{- s.heading -}}
            </{{ heading_tag }}>
          {%- endif -%}
          {%- if s.text != blank -%}
            <div class="lib-compl__text">{{ s.text }}</div>
          {%- endif -%}
        </header>
      {%- endif -%}

      <ul class="lib-compl__list" role="list">
        {%- if has_results -%}
          {%- for item in recommendations.products -%}
            <li class="lib-compl__item">
              {%- assign id_prefix = section.id | append: '-compl' -%}
              {%- render 'lib-product-card',
                product: item,
                title_tag: card_tag,
                ratio: s.image_ratio,
                sizes: img_sizes,
                show_vendor: s.show_vendor,
                show_quick_add: s.show_quick_add,
                quick_add_label: s.quick_add_label,
                choose_label: s.choose_label,
                sold_out_label: s.sold_out_label,
                sale_label: s.sale_label,
                sale_price_label: s.sale_price_label,
                regular_price_label: s.regular_price_label,
                id_prefix: id_prefix
              -%}
            </li>
          {%- endfor -%}
        {%- else -%}
          {%- comment -%} Editor only, until the product has complementary products. {%- endcomment -%}
          {%- for i in (1..cols) -%}
            <li class="lib-compl__item">{%- render 'lib-product-card', title_tag: card_tag, ratio: s.image_ratio -%}</li>
          {%- endfor -%}
        {%- endif -%}
      </ul>
      {%- if has_results == false -%}
        <p class="lib-compl__note">
          Shown on product pages that have complementary products, set in Shopify's Search &amp; Discovery app.
        </p>
      {%- endif -%}
    {%- endif -%}
  </div>
</lib-complementary-products>

{% stylesheet %}
  .lib-compl {
    display: block;
    background-color: var(--lib-bg);
    background-image: var(--lib-bg-image, none);
    color: var(--lib-text);
    padding: var(--lib-pt) 1rem var(--lib-pb);
  }
  .lib-compl[hidden] { display: none; }
  .lib-compl__inner { max-width: 1200px; margin: 0 auto; }
  .lib-compl--full .lib-compl__inner { max-width: none; }
  .lib-compl[data-layout='list'] .lib-compl__inner { max-width: 40rem; }
  @media (max-width: 749px) {
    .lib-compl { padding-top: var(--lib-pt-m); padding-bottom: var(--lib-pb-m); }
    .lib-compl[data-show='desktop'] { display: none; }
  }
  @media (min-width: 750px) {
    .lib-compl[data-show='mobile'] { display: none; }
  }

  .lib-compl__header { margin-bottom: clamp(1.25rem, 3vw, 2rem); }
  .lib-compl[data-layout='list'] .lib-compl__header { margin-bottom: 1rem; }
  .lib-compl__heading {
    margin: 0;
    font-size: clamp(1.5rem, 2.6vw, 2.125rem);
    line-height: 1.15;
    letter-spacing: -0.02em;
    text-wrap: balance;
  }
  .lib-compl__heading--small { font-size: clamp(1.0625rem, 1.5vw, 1.25rem); }
  .lib-compl__heading--large { font-size: clamp(2rem, 4vw, 3rem); }
  .lib-compl__text { margin-top: 0.5rem; font-size: 1rem; line-height: 1.6; opacity: 0.85; }
  .lib-compl__text > :first-child { margin-top: 0; }
  .lib-compl__text > :last-child { margin-bottom: 0; }
  .lib-compl__header[data-align='center'] { text-align: center; }
  @media (max-width: 749px) {
    .lib-compl__header[data-align-m='left'] { text-align: left; }
    .lib-compl__header[data-align-m='center'] { text-align: center; }
  }

  .lib-compl__list {
    display: grid;
    grid-template-columns: repeat(var(--lib-cols), minmax(0, 1fr));
    gap: clamp(1rem, 2vw, 1.5rem);
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .lib-compl__item { display: flex; min-width: 0; }
  @media (max-width: 749px) {
    .lib-compl__list { grid-template-columns: repeat(var(--lib-cols-m), minmax(0, 1fr)); }
  }

  /* List: a compact row per product, for the area under the buy box. */
  .lib-compl[data-layout='list'] .lib-compl__list { grid-template-columns: 1fr; gap: 0.75rem; }
  .lib-compl[data-layout='list'] .lib-card {
    flex-direction: row;
    align-items: center;
    gap: 1rem;
    padding: 0.625rem;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lib-text) 12%, transparent);
  }
  .lib-compl[data-layout='list'] .lib-card__media { flex: none; width: clamp(4.5rem, 18vw, 6rem); border-radius: calc(var(--lib-radius-card) * 0.66); }
  .lib-compl[data-layout='list'] .lib-card__body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.2rem 1rem;
    padding: 0;
  }
  .lib-compl[data-layout='list'] .lib-card__body > :not(.lib-card__cta) { grid-column: 1; }
  .lib-compl[data-layout='list'] .lib-card__cta { grid-column: 2; grid-row: 1 / span 3; margin: 0; padding: 0; }
  .lib-compl[data-layout='list'] .lib-card__add { width: auto; min-width: 6.5rem; }
  .lib-compl[data-layout='list'] .lib-card__badge { display: none; }
  @media (max-width: 479px) {
    .lib-compl[data-layout='list'] .lib-card__body { grid-template-columns: 1fr; }
    .lib-compl[data-layout='list'] .lib-card__cta { grid-column: 1; grid-row: auto; padding-top: 0.4rem; }
  }

  .lib-compl__note { margin: 1rem 0 0; font-size: 0.875rem; opacity: 0.75; }

  @media (prefers-reduced-motion: no-preference) {
    .lib-compl[data-animate][data-loaded] .lib-compl__item { animation: lib-compl-in 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
  }
  @keyframes lib-compl-in {
    from { opacity: 0; transform: translateY(12px); }
  }
{% endstylesheet %}

{% javascript %}
  (() => {
    if (customElements.get('lib-complementary-products')) return;

    class LibComplementaryProducts extends HTMLElement {
      connectedCallback() {
        const url = this.dataset.url;
        if (!url || this.dataset.requested) return;
        this.dataset.requested = 'true';
        // Load only when the section comes near the screen: product pages stay light.
        if (!('IntersectionObserver' in window)) {
          this.load(url);
          return;
        }
        const observer = new IntersectionObserver(
          (entries) => {
            if (!entries.some((e) => e.isIntersecting)) return;
            observer.disconnect();
            this.load(url);
          },
          {rootMargin: '0px 0px 400px 0px'},
        );
        // A hidden element never intersects, so watch its parent section instead.
        observer.observe(this.parentElement || this);
      }

      async load(url) {
        try {
          const response = await fetch(url);
          if (!response.ok) throw new Error(`recommendations answered ${response.status}`);
          const doc = new DOMParser().parseFromString(await response.text(), 'text/html');
          const results = doc.querySelector('.lib-compl [data-results]');
          if (!results || !results.querySelector('.lib-card')) return;
          this.querySelector('[data-results]').replaceWith(results);
          this.toggleAttribute('data-loaded', true);
          this.hidden = false;
        } catch (error) {
          // No recommendations is a normal state; the section just stays hidden.
          console.warn('Complementary products could not be loaded.', error);
        }
      }
    }
    customElements.define('lib-complementary-products', LibComplementaryProducts);
  })();
{% endjavascript %}

{% schema %}
{
  "name": "Lib · Complementary",
  "tag": "section",
  "class": "lib-section",
  "enabled_on": { "templates": ["product"] },
  "settings": [
    { "type": "paragraph", "content": "Choose each product's complementary products in Shopify's Search & Discovery app. The section stays hidden on products without any." },
    { "type": "header", "content": "Content" },
    { "type": "inline_richtext", "id": "heading", "label": "Heading", "default": "Pairs well with" },
    {
      "type": "select",
      "id": "heading_tag",
      "label": "Heading tag",
      "info": "Use H1 only when this is the page's main heading.",
      "options": [
        { "value": "h1", "label": "H1" },
        { "value": "h2", "label": "H2" },
        { "value": "h3", "label": "H3" },
        { "value": "p", "label": "Paragraph" }
      ],
      "default": "h2"
    },
    {
      "type": "select",
      "id": "heading_size",
      "label": "Heading size",
      "options": [
        { "value": "small", "label": "Small" },
        { "value": "medium", "label": "Medium" },
        { "value": "large", "label": "Large" }
      ],
      "default": "medium"
    },
    { "type": "richtext", "id": "text", "label": "Intro text" },

    { "type": "header", "content": "Products" },
    { "type": "range", "id": "products_to_show", "label": "Products to show", "min": 1, "max": 10, "step": 1, "default": 4 },
    {
      "type": "select",
      "id": "layout",
      "label": "Layout",
      "info": "List suits the area under the buy box; grid suits a full-width row.",
      "options": [
        { "value": "grid", "label": "Grid" },
        { "value": "list", "label": "List" }
      ],
      "default": "grid"
    },
    {
      "type": "select",
      "id": "image_ratio",
      "label": "Image shape",
      "options": [
        { "value": "square", "label": "Square" },
        { "value": "portrait", "label": "Portrait" },
        { "value": "tall", "label": "Tall" },
        { "value": "landscape", "label": "Landscape" }
      ],
      "default": "square"
    },
    {
      "type": "select",
      "id": "card_style",
      "label": "Card style",
      "options": [
        { "value": "plain", "label": "Plain" },
        { "value": "card", "label": "Card" }
      ],
      "default": "plain"
    },
    { "type": "range", "id": "card_radius", "label": "Corner radius", "min": 0, "max": 24, "step": 2, "unit": "px", "default": 12 },
    { "type": "checkbox", "id": "show_vendor", "label": "Show vendor", "default": false },
    { "type": "checkbox", "id": "show_quick_add", "label": "Show quick add", "info": "Products with options link to their page instead.", "default": true },
    { "type": "text", "id": "quick_add_label", "label": "Quick add button", "default": "Add to cart" },
    { "type": "text", "id": "choose_label", "label": "Link for products with options", "default": "Choose options" },
    { "type": "text", "id": "sale_label", "label": "Sale badge", "default": "Sale" },
    { "type": "text", "id": "sold_out_label", "label": "Sold out badge", "default": "Sold out" },
    { "type": "text", "id": "sale_price_label", "label": "Sale price label (screen readers)", "default": "Sale price" },
    { "type": "text", "id": "regular_price_label", "label": "Regular price label (screen readers)", "default": "Regular price" },

    { "type": "header", "content": "Layout" },
    { "type": "range", "id": "columns", "label": "Columns on desktop (grid)", "min": 2, "max": 5, "step": 1, "default": 4 },
    { "type": "range", "id": "columns_mobile", "label": "Columns on mobile (grid)", "min": 1, "max": 2, "step": 1, "default": 2 },
    {
      "type": "select",
      "id": "section_width",
      "label": "Width",
      "options": [
        { "value": "page", "label": "Page width" },
        { "value": "full", "label": "Full width" }
      ],
      "default": "page"
    },
    {
      "type": "select",
      "id": "text_alignment",
      "label": "Heading alignment on desktop",
      "options": [
        { "value": "left", "label": "Left" },
        { "value": "center", "label": "Center" }
      ],
      "default": "left"
    },
    {
      "type": "select",
      "id": "text_alignment_mobile",
      "label": "Heading alignment on mobile",
      "options": [
        { "value": "left", "label": "Left" },
        { "value": "center", "label": "Center" }
      ],
      "default": "left"
    },

    { "type": "header", "content": "Colors" },
    { "type": "color", "id": "color_bg", "label": "Background", "default": "#FFFFFF" },
    { "type": "color_background", "id": "color_bg_gradient", "label": "Background gradient", "info": "Optional. Covers the background color." },
    { "type": "color", "id": "color_text", "label": "Text", "default": "#1F1B17" },
    { "type": "color", "id": "color_accent", "label": "Accent (buttons, badges)", "default": "#7A5A3A" },
    { "type": "color", "id": "color_accent_text", "label": "Text on accent", "default": "#FFFFFF" },
    { "type": "color", "id": "color_card", "label": "Card background", "default": "#FAF6F1" },

    { "type": "header", "content": "Spacing and visibility" },
    { "type": "range", "id": "padding_top", "label": "Top padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 56 },
    { "type": "range", "id": "padding_bottom", "label": "Bottom padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 56 },
    { "type": "range", "id": "padding_top_mobile", "label": "Top padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 40 },
    { "type": "range", "id": "padding_bottom_mobile", "label": "Bottom padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 40 },
    {
      "type": "select",
      "id": "show_on",
      "label": "Show on",
      "options": [
        { "value": "all", "label": "All devices" },
        { "value": "desktop", "label": "Desktop only" },
        { "value": "mobile", "label": "Mobile only" }
      ],
      "default": "all"
    },
    { "type": "text", "id": "anchor_id", "label": "Anchor ID", "info": "Optional. Link to this section with #your-id." },

    { "type": "header", "content": "Motion" },
    { "type": "checkbox", "id": "animate", "label": "Fade cards in as they load", "info": "Never when reduced motion is on.", "default": true }
  ],
  "presets": [{ "name": "Lib · Complementary" }]
}
{% endschema %}
```

### snippets/lib-product-card.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{% doc %}
  Product card shared by the library's product sections: image (second image on hover), optional
  vendor, price with compare-at price, sale and sold-out badges, and an optional quick add. Quick add
  is a plain Shopify product form that posts the only variant to /cart/add; products with options
  link to the product page. Colors come from the parent section's --lib-* custom properties.
  With `template: true` it prints empty slots (data-slot) for a script to fill from product JSON,
  so cards built in the browser look exactly like cards built in Liquid.
  @param {product} [product] - The product; blank prints a placeholder card (theme editor only)
  @param {boolean} [template] - Print empty slots for a script instead of a product
  @param {string} [title_tag] - Tag for the product title (default 'h3')
  @param {string} [ratio] - Image shape: 'square' (default), 'portrait', 'tall' or 'landscape'
  @param {string} [sizes] - sizes attribute for the image
  @param {boolean} [eager] - Load the image immediately
  @param {boolean} [show_vendor] - Show the vendor above the title
  @param {boolean} [show_second_image] - Show the second image on hover
  @param {boolean} [show_quick_add] - Show the quick add button or the choose options link
  @param {string} [quick_add_label] - Quick add button text
  @param {string} [choose_label] - Link text for products with options
  @param {string} [sold_out_label] - Badge and button text for sold-out products
  @param {string} [sale_label] - Badge text for products on sale
  @param {string} [sale_price_label] - Screen-reader label before the sale price
  @param {string} [regular_price_label] - Screen-reader label before the compare-at price
  @param {string} [id_prefix] - Unique prefix for the title id (the quick add button points at it)
  @example
  {% render 'lib-product-card', product: product, ratio: 'portrait', show_quick_add: true, quick_add_label: 'Add to cart', id_prefix: section.id %}
{% enddoc %}
{%- liquid
  assign tag = title_tag | default: 'h3'
  assign shape = ratio | default: 'square'
  assign title_id = id_prefix | append: '-' | append: product.id
  assign on_sale = false
  if product.compare_at_price > product.price
    assign on_sale = true
  endif
  assign has_img2 = false
  if show_second_image and product.media.size > 1 and product.media[1].preview_image != blank
    assign has_img2 = true
  endif
-%}

{%- if template -%}
  <article class="lib-card">
    <div class="lib-card__media" data-ratio="{{ shape }}">
      <img class="lib-card__img" data-slot="image" alt="" loading="lazy" width="600" height="600" hidden>
      <span class="lib-card__badge lib-card__badge--sold-out" data-slot="sold-out" hidden>{{ sold_out_label | escape }}</span>
      <span class="lib-card__badge" data-slot="sale" hidden>{{ sale_label | escape }}</span>
    </div>
    <div class="lib-card__body">
      {%- if show_vendor -%}
        <p class="lib-card__vendor" data-slot="vendor"></p>
      {%- endif -%}
      <{{ tag }} class="lib-card__title"><a class="lib-card__link" data-slot="link" href=""></a></{{ tag }}>
      <p class="lib-card__price">
        <span class="lib-card__sr" data-slot="sale-label" hidden>{{ sale_price_label | escape }}</span>
        <span class="lib-card__price-now" data-slot="price"></span>
        <span class="lib-card__sr" data-slot="regular-label" hidden>{{ regular_price_label | escape }}</span>
        <s class="lib-card__price-was" data-slot="compare" hidden></s>
      </p>
    </div>
  </article>
{%- elsif product != blank -%}
  <article class="lib-card{% if product.available == false %} is-sold-out{% endif %}">
    <div class="lib-card__media" data-ratio="{{ shape }}">
      {%- render 'lib-image', image: product.featured_image, class: 'lib-card__img', sizes: sizes, eager: eager, placeholder: 'product-1' -%}
      {%- if has_img2 -%}
        <div class="lib-card__img2" aria-hidden="true">
          {%- render 'lib-image', image: product.media[1].preview_image, class: 'lib-card__img', sizes: sizes -%}
        </div>
      {%- endif -%}
      {%- if product.available == false and sold_out_label != blank -%}
        <span class="lib-card__badge lib-card__badge--sold-out">{{ sold_out_label | escape }}</span>
      {%- elsif on_sale and sale_label != blank -%}
        <span class="lib-card__badge">{{ sale_label | escape }}</span>
      {%- endif -%}
    </div>

    <div class="lib-card__body">
      {%- if show_vendor and product.vendor != blank -%}
        <p class="lib-card__vendor">{{ product.vendor | escape }}</p>
      {%- endif -%}
      <{{ tag }} class="lib-card__title" id="{{ title_id }}">
        <a class="lib-card__link" href="{{ product.url }}">{{ product.title | escape }}</a>
      </{{ tag }}>
      <p class="lib-card__price">
        {%- if on_sale -%}
          <span class="lib-card__sr">{{ sale_price_label | escape }}</span>
          <span class="lib-card__price-now lib-card__price-now--sale">{{ product.price | money }}</span>
          <span class="lib-card__sr">{{ regular_price_label | escape }}</span>
          <s class="lib-card__price-was">{{ product.compare_at_price | money }}</s>
        {%- else -%}
          <span class="lib-card__price-now">{{ product.price | money }}</span>
        {%- endif -%}
      </p>

      {%- if show_quick_add -%}
        <div class="lib-card__cta">
          {%- if product.has_only_default_variant -%}
            {%- form 'product', product, class: 'lib-card__form' -%}
              <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
              <input type="hidden" name="quantity" value="1">
              <button
                type="submit"
                name="add"
                class="lib-card__add"
                aria-describedby="{{ title_id }}"
                {% if product.available == false %}
                  disabled
                {% endif %}
              >
                {%- if product.available -%}
                  {{- quick_add_label | escape -}}
                {%- else -%}
                  {{- sold_out_label | default: quick_add_label | escape -}}
                {%- endif -%}
              </button>
            {%- endform -%}
          {%- else -%}
            <a class="lib-card__add" href="{{ product.url }}" aria-describedby="{{ title_id }}">
              {{- choose_label | escape -}}
            </a>
          {%- endif -%}
        </div>
      {%- endif -%}
    </div>
  </article>
{%- else -%}
  <article class="lib-card">
    <div class="lib-card__media" data-ratio="{{ shape }}">
      {{ 'product-1' | placeholder_svg_tag: 'lib-card__img' }}
    </div>
    <div class="lib-card__body">
      <{{ tag }} class="lib-card__title">Example product title</{{ tag }}>
      <p class="lib-card__price"><span class="lib-card__price-now">{{ 4800 | money }}</span></p>
    </div>
  </article>
{%- endif -%}

{% stylesheet %}
  .lib-card {
    position: relative;
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
    border-radius: var(--lib-radius-card, 12px);
    color: var(--lib-text, #1f1b17);
  }
  [data-card='card'] .lib-card { background: var(--lib-card, #faf6f1); }
  .lib-card__media {
    position: relative;
    overflow: hidden;
    aspect-ratio: 1;
    border-radius: var(--lib-radius-card, 12px);
    background: color-mix(in srgb, var(--lib-text, #1f1b17) 6%, transparent);
  }
  .lib-card__media[data-ratio='portrait'] { aspect-ratio: 4 / 5; }
  .lib-card__media[data-ratio='tall'] { aspect-ratio: 2 / 3; }
  .lib-card__media[data-ratio='landscape'] { aspect-ratio: 4 / 3; }
  [data-card='card'] .lib-card__media { border-end-start-radius: 0; border-end-end-radius: 0; }
  .lib-card__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  svg.lib-card__img { fill: color-mix(in srgb, var(--lib-text, #1f1b17) 22%, transparent); }
  .lib-card__img2 { position: absolute; inset: 0; opacity: 0; transition: opacity 0.4s ease; }
  @media (hover: hover) {
    .lib-card:hover .lib-card__img2 { opacity: 1; }
    .lib-card:hover .lib-card__img { transform: scale(1.03); }
  }
  .lib-card:focus-within .lib-card__img2 { opacity: 1; }
  .lib-card.is-sold-out .lib-card__media img { opacity: 0.6; }

  .lib-card__badge {
    position: absolute;
    z-index: 2;
    top: 0.75rem;
    left: 0.75rem;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    background: var(--lib-accent, #1f1b17);
    color: var(--lib-accent-text, #fff);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    line-height: 1.2;
    pointer-events: none;
  }
  .lib-card__badge[hidden] { display: none; }
  .lib-card__badge--sold-out {
    background: var(--lib-bg, #fff);
    color: var(--lib-text, #1f1b17);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lib-text, #1f1b17) 20%, transparent);
  }

  .lib-card__body { display: flex; flex: 1; flex-direction: column; gap: 0.3rem; padding-top: 0.9rem; }
  [data-card='card'] .lib-card__body { padding: 0.9rem 1rem 1rem; }
  .lib-card__vendor { margin: 0; font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.7; }
  .lib-card__vendor:empty { display: none; }
  .lib-card__title { margin: 0; font-size: 1rem; font-weight: 600; line-height: 1.3; }
  .lib-card__link { color: inherit; text-decoration: none; }
  /* The title link covers the whole card; the quick add button sits above it. */
  .lib-card__link::after { content: ''; position: absolute; inset: 0; z-index: 1; border-radius: var(--lib-radius-card, 12px); }
  .lib-card__link:hover { text-decoration: underline; text-underline-offset: 0.2em; }
  .lib-card__link:focus-visible { outline: none; }
  .lib-card__link:focus-visible::after { outline: 2px solid var(--lib-accent, #1f1b17); outline-offset: 3px; }
  .lib-card__price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; margin: 0; font-size: 0.9375rem; }
  .lib-card__price-now--sale { font-weight: 600; }
  .lib-card__price-was { opacity: 0.7; font-size: 0.875rem; }
  .lib-card__price-was[hidden] { display: none; }
  .lib-card__sr {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .lib-card__cta { position: relative; z-index: 2; margin-top: auto; padding-top: 0.75rem; }
  .lib-card__form { margin: 0; }
  .lib-card__add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 2.75rem;
    padding: 0.6rem 1rem;
    border: 1.5px solid var(--lib-text, #1f1b17);
    border-radius: min(var(--lib-radius-card, 12px), 999px);
    background: transparent;
    color: var(--lib-text, #1f1b17);
    font: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
  }
  .lib-card__add:hover:not(:disabled) {
    background: var(--lib-accent, #1f1b17);
    border-color: var(--lib-accent, #1f1b17);
    color: var(--lib-accent-text, #fff);
  }
  .lib-card__add:focus-visible { outline: 2px solid var(--lib-accent, #1f1b17); outline-offset: 2px; }
  .lib-card__add:disabled { opacity: 0.45; cursor: default; }

  @media (prefers-reduced-motion: reduce) {
    .lib-card__img,
    .lib-card__img2,
    .lib-card__add { transition: none; }
    .lib-card:hover .lib-card__img { transform: none; }
  }
{% endstylesheet %}
```

### snippets/lib-image.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{% doc %}
  Responsive image with optional art direction: a separate mobile image (below 750px),
  each honouring its own focal point from the theme editor. Falls back to a placeholder.

  @param {image} [image] - Desktop image (also used on mobile when no mobile image is set)
  @param {image} [image_mobile] - Mobile image, below 750px
  @param {string} [class] - Class for the img element
  @param {string} [sizes] - sizes attribute for the desktop image (default 100vw)
  @param {boolean} [eager] - Load immediately with high fetch priority (use for the first sections on a page)
  @param {string} [placeholder] - placeholder_svg_tag name when no image is set (default 'lifestyle-1')

  @example
  {% render 'lib-image', image: section.settings.image, image_mobile: section.settings.image_mobile, class: 'lib-hero__img', sizes: '(min-width: 990px) 50vw, 100vw', eager: true %}
{% enddoc %}

{%- liquid
  assign desktop = image
  if desktop == blank
    assign desktop = image_mobile
  endif
  assign img_sizes = sizes | default: '100vw'
  assign img_loading = 'lazy'
  assign img_priority = 'auto'
  if eager
    assign img_loading = 'eager'
    assign img_priority = 'high'
  endif
  assign art = false
  if image != blank and image_mobile != blank
    assign art = true
  endif
-%}

{%- if desktop != blank -%}
  <picture
    class="lib-picture{% if art %} lib-picture--art{% endif %}"
    {% if art %}
      style="--lib-pos-m: {{ image_mobile.presentation.focal_point | default: '50% 50%' }};"
    {% endif %}
  >
    {%- if art -%}
      <source
        media="(max-width: 749px)"
        srcset="
          {{ image_mobile | image_url: width: 375 }} 375w,
          {{ image_mobile | image_url: width: 750 }} 750w,
          {{ image_mobile | image_url: width: 1100 }} 1100w,
          {{ image_mobile | image_url: width: 1500 }} 1500w
        "
        sizes="100vw"
        width="{{ image_mobile.width }}"
        height="{{ image_mobile.height }}"
      >
    {%- endif -%}
    {{
      desktop
      | image_url: width: 2400
      | image_tag:
        class: class,
        loading: img_loading,
        fetchpriority: img_priority,
        sizes: img_sizes,
        widths: '600, 900, 1200, 1600, 2000, 2400'
    }}
  </picture>
{%- else -%}
  {{ placeholder | default: 'lifestyle-1' | placeholder_svg_tag: class }}
{%- endif -%}

{% stylesheet %}
  .lib-picture { display: contents; }
  @media (max-width: 749px) {
    /* image_tag sets the desktop focal point inline; the mobile image needs its own. */
    .lib-picture--art img { object-position: var(--lib-pos-m) !important; }
  }
{% endstylesheet %}
```

## Lisans

MIT Lisansı, © 2026 Lintel. Kendi mağazanızda ya da müşteri işlerinde ücretsiz kullanabilir, değiştirebilir ve yayınlayabilirsiniz; her dosyanın başındaki lisans satırını koruyun.
