# Önce / sonra kaydırıcısı: ücretsiz Shopify bölümü

> Sürüklenebilir ayırıcıyla tek çerçevede iki fotoğraf; müşteriler önceyi ve sonrayı karşılaştırır. Klavye ve ekran okuyucu dostu, ayrı mobil görselli.

Önce / sonra kaydırıcısı, iki fotoğrafı tek çerçevede üst üste koyan ve müşterilerin karşılaştırmak için bir ayırıcıyı fare, dokunma ya da klavyeyle sürüklemesini sağlayan ücretsiz bir Shopify bölümüdür. Ayrı mobil görseller, yatay ya da dikey bölme ve metin ile düğmeli bir başlık sunar; Online Store 2.0 temalarında uygulama olmadan çalışır.

- **Kategori:** Ürün sayfası
- **Kullanıldığı yerler:** Ürün, Ana sayfa, Açılış sayfaları
- **Okuduğu veri:** Yalnızca içerik
- **Boyut:** 3 dosyada 33 KB, küçük bir betikle
- **Lisans:** MIT, © 2026 Lintel
- **Sayfa:** https://lintelagency.com/tr/kaynaklar/shopify-bolumleri/once-sonra-karsilastirma
- **Canlı demo:** https://lintelagency.com/resources/shopify-sections/demo/lib-before-after.html
- **Eklenme tarihi:** 2026-10-02

## Ne işe yarar

- Temizlik, bakım ya da onarım ürünlerinin ürün sayfasında, bir ürünün işlem öncesi ve sonrası
- Tamir, yenileme ya da ileri dönüşüm hizmeti için açılış sayfası
- Bir odanın mağazanın ürünleriyle düzenlenmeden önceki ve sonraki hâlini karşılaştıran ana sayfa bloğu

## Tema düzenleyici ayarları

Bölüm tema düzenleyicide “Lib · Before / after” 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 | `Ten minutes, <em>years</em> back` |  |
| `heading_tag` | `select` | Heading tag | h2 | `h1`, `h2`, `h3`, `p` |
| `heading_size` | `select` | Heading size | large | `small`, `medium`, `large`, `xlarge` |
| `text` | `richtext` | Text | `<p>Drag the divider to see one bag before and after a single treatment with our cleaner and beeswax conditioner. Same light, same bench, nothing retouched but the leather.</p>` |  |

### Images

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `image_before` | `image_picker` | Before image (desktop) | – |  |
| `image_before_mobile` | `image_picker` | Before image (mobile) | – |  |
| `image_after` | `image_picker` | After image (desktop) | – |  |
| `image_after_mobile` | `image_picker` | After image (mobile) | – |  |
| `image_ratio` | `select` | Image shape on desktop | landscape | `adapt`, `square`, `portrait`, `landscape`, `wide` |
| `image_ratio_mobile` | `select` | Image shape on mobile | portrait | `adapt`, `square`, `portrait`, `landscape`, `wide` |
| `image_radius` | `range` | Image corner radius | `16` | 0–40 px, step 2 |

### Slider

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `orientation` | `select` | Divider direction | horizontal | `horizontal`, `vertical` |
| `start_position` | `range` | Start position | `50` | 0–100 %, step 5 |
| `knob_style` | `select` | Handle style | circle | `circle`, `arrows`, `line` |
| `show_labels` | `checkbox` | Show labels on the images | `true` |  |
| `label_before` | `text` | Before label | Before |  |
| `label_after` | `text` | After label | After |  |
| `label_position` | `select` | Label position | top | `top`, `bottom` |

### Button

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `button_label` | `text` | Button label | Shop the care kit |  |
| `button_url` | `url` | Button link | – |  |
| `button_style` | `select` | Button style | primary | `primary`, `outline`, `text` |

### Layout

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `layout` | `select` | Layout | beside | `stacked`, `beside` |
| `image_position` | `select` | Image position on desktop | right | `right`, `left` |
| `mobile_order` | `select` | Order on mobile | text_first | `text_first`, `image_first` |
| `section_width` | `select` | Width | page | `page`, `full` |
| `text_alignment` | `text_alignment` | Text alignment on desktop | left |  |
| `text_alignment_mobile` | `select` | Text alignment on mobile | left | `left`, `center`, `right` |

### Colors

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `color_bg` | `color` | Background | #F6F1EA |  |
| `color_bg_gradient` | `color_background` | Background gradient | – |  |
| `color_text` | `color` | Text | #2B211A |  |
| `color_accent` | `color` | Accent (button, focus ring) | #8A4B22 |  |
| `color_accent_text` | `color` | Text on accent | #FFFFFF |  |
| `color_knob` | `color` | Divider, handle and label background | #FFFFFF |  |
| `color_knob_icon` | `color` | Handle arrows and label text | #2B211A |  |

### Spacing and visibility

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

## 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-before-after.liquid` adında yeni bir dosya ekleyin, aşağıdaki kaynak koddan `sections/lib-before-after.liquid` içeriğini yapıştırıp kaydedin.
4. `lib-image.liquid` ve `lib-button.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 · Before / after** 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-before-after.liquid https://lintelagency.com/resources/shopify-sections/files/lib-before-after.liquid
curl -fsSL -o snippets/lib-image.liquid https://lintelagency.com/resources/shopify-sections/files/lib-image.liquid
curl -fsSL -o snippets/lib-button.liquid https://lintelagency.com/resources/shopify-sections/files/lib-button.liquid
shopify theme push --theme <unpublished-theme-id> --only sections/lib-before-after.liquid --only snippets/lib-image.liquid --only snippets/lib-button.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-before-after.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-before-after.liquid): `sections/` klasörüne
- [snippets/lib-image.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-image.liquid): `snippets/` klasörüne
- [snippets/lib-button.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-button.liquid): `snippets/` klasörüne

## Sık sorulan sorular

### Fare olmadan kullanılabilir mi?

Evet. Ayırıcı yerel bir range input ile çalışır; Tab ona odaklanır, ok, Home ve End tuşları onu hareket ettirir. Ekran okuyucular etiket adlarını ve her görselin ne kadarının göründüğünü duyar. Fare ve dokunmatik sürükleme görselin her yerinde çalışır.

### İki fotoğrafın şekli farklıysa ne olur?

İki görsel de aynı çerçeveyi doldurur ve ona göre kırpılır; böylece boyutları hep örtüşür. Masaüstü ve mobil için ayrı ayrı kare, dikey, yatay, geniş ya da uyum (sonra görselinin şeklini alır) seçin. Aynı noktadan çekilmiş fotoğraflar en temiz karşılaştırmayı verir.

### JavaScript kapalıysa müşteriler ne görür?

Yalnızca sonra görselini, tam olarak. Önce görseli, ayırıcı ve tutamaç küçük yerleşik script çalışınca görünür.

### Mobilde farklı görseller kullanabilir miyim?

Evet. Bir mobil önce ve bir mobil sonra görseli belirleyin; 750px altında, her biri kendi odak noktasıyla gösterilir. Çiftin yine eşleşmesi için ikisini birden belirleyin ya da hiçbirini belirlemeyin.

### Sayfayı yavaşlatır mı?

Script, kütüphane içermeyen 100 satırdan kısa vanilla JavaScript’tir. Bölüm sayfadaki ilk iki bölümden biriyse görselleri hemen yüklenir; daha aşağıdaysa lazy-load ile yüklenir.

## Kaynak kod

### sections/lib-before-after.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{%- comment -%}
  Before / after slider: two images in one frame, a draggable divider reveals the "before" image
  over the "after" image. A native range input drives the divider, so keyboard and screen readers
  work; pointer drag works anywhere on the frame. Horizontal or vertical split.
  Without JavaScript only the "after" image shows, in full.
  The divider moves along the physical x / y axis of the photo, so its CSS uses left / top on purpose.
  Depends on: snippets/lib-image.liquid, snippets/lib-button.liquid
{%- endcomment -%}

{%- comment -%} "Adapt" takes its shape from the after image; both images always share that frame. {%- endcomment -%}
{%- liquid
  assign s = section.settings
  assign heading_tag = s.heading_tag | default: 'h2'
  assign eager = false
  if section.index <= 2
    assign eager = true
  endif

  assign label_before = s.label_before | default: 'Before'
  assign label_after = s.label_after | default: 'After'

  assign ratio_d = '3 / 2'
  assign after_d = s.image_after
  if after_d == blank
    assign after_d = s.image_after_mobile
  endif
  if after_d != blank and after_d.width and after_d.height
    assign ratio_d = after_d.width | append: ' / ' | append: after_d.height
  endif
  assign ratio_m = ratio_d
  if s.image_after_mobile != blank and s.image_after_mobile.width and s.image_after_mobile.height
    assign ratio_m = s.image_after_mobile.width | append: ' / ' | append: s.image_after_mobile.height
  endif

  assign sizes = '(min-width: 1200px) 1200px, 100vw'
  if s.layout == 'beside'
    assign sizes = '(min-width: 990px) 60vw, 100vw'
  endif

  assign has_copy = false
  if s.heading != blank or s.text != blank or s.button_label != blank
    assign has_copy = true
  endif
-%}

<div
  class="lib-before-after lib-before-after--{{ s.layout }} lib-before-after--{{ s.section_width }}"
  {% if s.anchor_id != blank %}
    id="{{ s.anchor_id | handleize }}"
  {% endif %}
  data-show="{{ s.show_on }}"
  data-mobile-order="{{ s.mobile_order }}"
  data-image-position="{{ s.image_position }}"
  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-line: {{ s.color_knob }};
    --lib-knob: {{ s.color_knob }};
    --lib-knob-icon: {{ s.color_knob_icon }};
    --lib-radius-img: {{ s.image_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-before-after__inner">
    {%- if has_copy -%}
      <div
        class="lib-before-after__copy"
        data-align="{{ s.text_alignment }}"
        data-align-m="{{ s.text_alignment_mobile }}"
      >
        {%- if s.heading != blank -%}
          <{{ heading_tag }} class="lib-before-after__heading lib-before-after__heading--{{ s.heading_size }}">
            {{- s.heading -}}
          </{{ heading_tag }}>
        {%- endif -%}
        {%- if s.text != blank -%}
          <div class="lib-before-after__text">{{ s.text }}</div>
        {%- endif -%}
        {%- if s.button_label != blank -%}
          <div class="lib-before-after__actions">
            {%- render 'lib-button',
              label: s.button_label,
              url: s.button_url,
              style: s.button_style
            -%}
          </div>
        {%- endif -%}
      </div>
    {%- endif -%}

    <div class="lib-before-after__media">
      <lib-before-after-slider
        class="lib-before-after__compare"
        data-orientation="{{ s.orientation }}"
        data-knob="{{ s.knob_style }}"
        data-label-pos="{{ s.label_position }}"
        data-ratio="{{ s.image_ratio }}"
        data-ratio-m="{{ s.image_ratio_mobile }}"
        data-label-before="{{ label_before | escape }}"
        data-label-after="{{ label_after | escape }}"
        style="--lib-pos: {{ s.start_position }}%; --lib-adapt-d: {{ ratio_d }}; --lib-adapt-m: {{ ratio_m }};"
      >
        <div class="lib-before-after__layer lib-before-after__layer--after">
          {%- render 'lib-image',
            image: s.image_after,
            image_mobile: s.image_after_mobile,
            class: 'lib-before-after__img',
            sizes: sizes,
            eager: eager,
            placeholder: 'lifestyle-2'
          -%}
          {%- if s.show_labels -%}
            <span class="lib-before-after__label lib-before-after__label--after" aria-hidden="true">
              {{- label_after | escape -}}
            </span>
          {%- endif -%}
        </div>
        <div class="lib-before-after__layer lib-before-after__layer--before">
          {%- render 'lib-image',
            image: s.image_before,
            image_mobile: s.image_before_mobile,
            class: 'lib-before-after__img',
            sizes: sizes,
            eager: eager,
            placeholder: 'lifestyle-1'
          -%}
          {%- if s.show_labels -%}
            <span class="lib-before-after__label lib-before-after__label--before" aria-hidden="true">
              {{- label_before | escape -}}
            </span>
          {%- endif -%}
        </div>

        <input
          class="lib-before-after__range"
          type="range"
          min="0"
          max="100"
          step="1"
          value="{{ s.start_position }}"
          aria-label="{{ label_before | append: ' / ' | append: label_after | escape }}"
          {% if s.orientation == 'vertical' %}
            aria-orientation="vertical"
          {% endif %}
        >
        <span class="lib-before-after__handle" aria-hidden="true">
          {%- unless s.knob_style == 'line' -%}
            <span class="lib-before-after__knob">
              <svg viewBox="0 0 24 24" width="24" height="24" focusable="false">
                <path d="M9 7 4 12l5 5M15 7l5 5-5 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
              </svg>
            </span>
          {%- endunless -%}
        </span>
      </lib-before-after-slider>
    </div>
  </div>
</div>

{% stylesheet %}
  .lib-before-after {
    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-before-after__inner {
    max-width: 1200px;
    margin: 0 auto;
    display: grid;
    gap: clamp(1.75rem, 4vw, 3.5rem);
  }
  .lib-before-after--full .lib-before-after__inner { max-width: none; }
  .lib-before-after__inner > * { min-width: 0; }
  @media (max-width: 749px) {
    .lib-before-after { padding-top: var(--lib-pt-m); padding-bottom: var(--lib-pb-m); }
    .lib-before-after[data-show='desktop'] { display: none; }
    .lib-before-after[data-mobile-order='image_first'] .lib-before-after__media { order: -1; }
  }
  @media (min-width: 750px) {
    .lib-before-after[data-show='mobile'] { display: none; }
  }
  @media (min-width: 990px) {
    .lib-before-after--beside .lib-before-after__inner { grid-template-columns: 2fr 3fr; }
    .lib-before-after--beside[data-image-position='left'] .lib-before-after__inner { grid-template-columns: 3fr 2fr; }
    .lib-before-after--beside[data-image-position='left'] .lib-before-after__media { order: -1; }
    .lib-before-after--beside .lib-before-after__inner { align-items: center; }
  }

  /* Copy: alignment for desktop, then mobile override. */
  .lib-before-after__copy { display: flex; flex-direction: column; align-items: flex-start; text-align: left; }
  .lib-before-after__copy[data-align='center'] { align-items: center; text-align: center; }
  .lib-before-after__copy[data-align='right'] { align-items: flex-end; text-align: right; }
  .lib-before-after--stacked .lib-before-after__copy[data-align='center'] { margin-inline: auto; }
  .lib-before-after--stacked .lib-before-after__copy[data-align='right'] { margin-inline-start: auto; }
  .lib-before-after--stacked .lib-before-after__copy { max-width: 44rem; }
  @media (max-width: 749px) {
    .lib-before-after__copy[data-align-m='left'] { align-items: flex-start; text-align: left; }
    .lib-before-after__copy[data-align-m='center'] { align-items: center; text-align: center; }
    .lib-before-after__copy[data-align-m='right'] { align-items: flex-end; text-align: right; }
  }
  .lib-before-after__heading {
    margin: 0;
    font-size: clamp(2rem, 4vw, 3.25rem);
    line-height: 1.08;
    letter-spacing: -0.025em;
    text-wrap: balance;
  }
  .lib-before-after__heading--small { font-size: clamp(1.5rem, 2.5vw, 2rem); }
  .lib-before-after__heading--medium { font-size: clamp(1.75rem, 3vw, 2.5rem); }
  .lib-before-after__heading--xlarge { font-size: clamp(2.5rem, 6vw, 4.5rem); }
  .lib-before-after__text {
    margin-top: 1rem;
    max-width: 34rem;
    font-size: 1.0625rem;
    line-height: 1.6;
    opacity: 0.78;
  }
  .lib-before-after__text > :first-child { margin-top: 0; }
  .lib-before-after__text > :last-child { margin-bottom: 0; }
  .lib-before-after__actions { margin-top: 1.75rem; }

  /* Compare frame: both images fill the same box, so they always share one shape. */
  .lib-before-after__compare {
    --lib-ar-d: 3 / 2;
    --lib-ar-m: 4 / 5;
    position: relative;
    display: block;
    aspect-ratio: var(--lib-ar-d);
    overflow: hidden;
    border-radius: var(--lib-radius-img);
    background: color-mix(in srgb, var(--lib-text) 6%, transparent);
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
  }
  .lib-before-after__compare[data-ratio='square'] { --lib-ar-d: 1 / 1; }
  .lib-before-after__compare[data-ratio='portrait'] { --lib-ar-d: 4 / 5; }
  .lib-before-after__compare[data-ratio='landscape'] { --lib-ar-d: 3 / 2; }
  .lib-before-after__compare[data-ratio='wide'] { --lib-ar-d: 16 / 9; }
  .lib-before-after__compare[data-ratio='adapt'] { --lib-ar-d: var(--lib-adapt-d); }
  .lib-before-after__compare[data-ratio-m='square'] { --lib-ar-m: 1 / 1; }
  .lib-before-after__compare[data-ratio-m='portrait'] { --lib-ar-m: 4 / 5; }
  .lib-before-after__compare[data-ratio-m='landscape'] { --lib-ar-m: 3 / 2; }
  .lib-before-after__compare[data-ratio-m='wide'] { --lib-ar-m: 16 / 9; }
  .lib-before-after__compare[data-ratio-m='adapt'] { --lib-ar-m: var(--lib-adapt-m); }
  @media (max-width: 749px) {
    .lib-before-after__compare { aspect-ratio: var(--lib-ar-m); }
  }
  .lib-before-after__compare:defined { cursor: ew-resize; touch-action: pan-y; }
  .lib-before-after__compare[data-orientation='vertical']:defined { cursor: ns-resize; touch-action: pan-x; }

  .lib-before-after__layer { position: absolute; inset: 0; }
  .lib-before-after__layer--before { clip-path: inset(0 calc(100% - var(--lib-pos)) 0 0); }
  .lib-before-after__compare[data-orientation='vertical'] .lib-before-after__layer--before {
    clip-path: inset(0 0 calc(100% - var(--lib-pos)) 0);
  }
  .lib-before-after__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
    -webkit-user-drag: none;
  }
  .lib-before-after__img:is(svg) {
    background: color-mix(in srgb, var(--lib-text) 10%, transparent);
    fill: color-mix(in srgb, var(--lib-text) 25%, transparent);
  }
  .lib-before-after__layer--before .lib-before-after__img:is(svg) {
    background: color-mix(in srgb, var(--lib-text) 22%, transparent);
  }

  /* No JavaScript: show the after image only. */
  .lib-before-after__compare:not(:defined) .lib-before-after__layer--before,
  .lib-before-after__compare:not(:defined) .lib-before-after__handle,
  .lib-before-after__compare:not(:defined) .lib-before-after__range { display: none; }

  /* Labels: each sits inside its own image layer, so the divider uncovers it naturally. */
  .lib-before-after__label {
    position: absolute;
    top: clamp(0.75rem, 2vw, 1.25rem);
    padding: 0.4rem 0.8rem;
    border-radius: 999px;
    background: var(--lib-knob);
    color: var(--lib-knob-icon);
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    line-height: 1.2;
    white-space: nowrap;
  }
  .lib-before-after__compare[data-label-pos='bottom'] .lib-before-after__label { top: auto; bottom: clamp(0.75rem, 2vw, 1.25rem); }
  .lib-before-after__label--before { left: clamp(0.75rem, 2vw, 1.25rem); }
  .lib-before-after__label--after { right: clamp(0.75rem, 2vw, 1.25rem); }
  .lib-before-after__compare[data-orientation='vertical'] .lib-before-after__label { left: clamp(0.75rem, 2vw, 1.25rem); right: auto; }
  .lib-before-after__compare[data-orientation='vertical'] .lib-before-after__label--before { top: clamp(0.75rem, 2vw, 1.25rem); bottom: auto; }
  .lib-before-after__compare[data-orientation='vertical'] .lib-before-after__label--after { top: auto; bottom: clamp(0.75rem, 2vw, 1.25rem); }

  /* The range input is the real control: invisible, focusable, driven by keys and assistive tech. */
  .lib-before-after__range {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    pointer-events: none;
  }

  /* Divider line and knob. */
  .lib-before-after__handle {
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--lib-pos);
    width: 2px;
    transform: translateX(-50%);
    background: var(--lib-line);
    box-shadow: 0 0 6px rgb(0 0 0 / 0.25);
    pointer-events: none;
  }
  .lib-before-after__compare[data-orientation='vertical'] .lib-before-after__handle {
    top: var(--lib-pos);
    bottom: auto;
    left: 0;
    right: 0;
    width: auto;
    height: 2px;
    transform: translateY(-50%);
  }
  .lib-before-after__knob {
    position: absolute;
    top: 50%;
    left: 50%;
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    background: var(--lib-knob);
    color: var(--lib-knob-icon);
    box-shadow: 0 4px 14px rgb(0 0 0 / 0.25);
    transition: transform 0.2s ease;
  }
  .lib-before-after__compare[data-orientation='vertical'] .lib-before-after__knob svg { transform: rotate(90deg); }
  .lib-before-after__compare[data-knob='arrows'] .lib-before-after__knob {
    width: 3.25rem;
    background: none;
    box-shadow: none;
    color: var(--lib-line);
    filter: drop-shadow(0 1px 3px rgb(0 0 0 / 0.45));
  }
  .lib-before-after__compare[data-knob='arrows'] .lib-before-after__knob svg { width: 2.25rem; height: 2.25rem; }
  .lib-before-after__compare[data-dragging] .lib-before-after__knob { transform: translate(-50%, -50%) scale(1.08); }
  .lib-before-after__range:focus-visible + .lib-before-after__handle {
    outline: 2px solid var(--lib-accent);
    outline-offset: 2px;
  }
  .lib-before-after__range:focus-visible + .lib-before-after__handle .lib-before-after__knob {
    outline: 3px solid var(--lib-accent);
    outline-offset: 3px;
  }
  /* After a mouse or touch drag the input keeps focus for the keys, but the ring stays hidden until a key is pressed. */
  .lib-before-after__compare[data-pointer] .lib-before-after__range:focus-visible + .lib-before-after__handle,
  .lib-before-after__compare[data-pointer] .lib-before-after__range:focus-visible + .lib-before-after__handle .lib-before-after__knob {
    outline: none;
  }
  @media (prefers-reduced-motion: reduce) {
    .lib-before-after__knob { transition: none; }
    .lib-before-after__compare[data-dragging] .lib-before-after__knob { transform: translate(-50%, -50%); }
  }
{% endstylesheet %}

{% javascript %}
  if (!customElements.get('lib-before-after-slider')) {
    customElements.define(
      'lib-before-after-slider',
      class extends HTMLElement {
        connectedCallback() {
          if (this._ready) return;
          this.input = this.querySelector('.lib-before-after__range');
          if (!this.input) return;
          this._ready = true;
          this.vertical = this.dataset.orientation === 'vertical';
          this.onInput = () => this.set(Number(this.input.value));
          this.onDown = (e) => {
            if (e.pointerType === 'mouse' && e.button !== 0) return;
            if (e.pointerType === 'mouse') e.preventDefault();
            this.pointerId = e.pointerId;
            try {
              this.setPointerCapture(e.pointerId);
            } catch (err) {}
            this.setAttribute('data-dragging', '');
            this.setAttribute('data-pointer', '');
            this.input.focus({ preventScroll: true });
            this.fromPointer(e);
          };
          this.onMove = (e) => {
            if (e.pointerId === this.pointerId) this.fromPointer(e);
          };
          this.onUp = (e) => {
            if (e.pointerId !== this.pointerId) return;
            this.pointerId = null;
            this.removeAttribute('data-dragging');
          };
          this.onKey = () => this.removeAttribute('data-pointer');
          this.input.addEventListener('input', this.onInput);
          this.input.addEventListener('keydown', this.onKey);
          this.addEventListener('pointerdown', this.onDown);
          this.addEventListener('pointermove', this.onMove);
          this.addEventListener('pointerup', this.onUp);
          this.addEventListener('pointercancel', this.onUp);
          this.set(Number(this.input.value));
        }

        disconnectedCallback() {
          if (!this._ready) return;
          this._ready = false;
          this.input.removeEventListener('input', this.onInput);
          this.input.removeEventListener('keydown', this.onKey);
          this.removeEventListener('pointerdown', this.onDown);
          this.removeEventListener('pointermove', this.onMove);
          this.removeEventListener('pointerup', this.onUp);
          this.removeEventListener('pointercancel', this.onUp);
        }

        fromPointer(e) {
          const r = this.getBoundingClientRect();
          const size = this.vertical ? r.height : r.width;
          if (!size) return;
          const offset = this.vertical ? e.clientY - r.top : e.clientX - r.left;
          this.set((offset / size) * 100);
        }

        set(value) {
          const v = Math.min(100, Math.max(0, Number.isFinite(value) ? value : 50));
          const rounded = Math.round(v);
          this.style.setProperty('--lib-pos', `${v}%`);
          if (Number(this.input.value) !== rounded) this.input.value = String(rounded);
          const before = this.dataset.labelBefore || 'Before';
          const after = this.dataset.labelAfter || 'After';
          this.input.setAttribute('aria-valuetext', `${before} ${rounded}%, ${after} ${100 - rounded}%`);
        }
      }
    );
  }
{% endjavascript %}

{% schema %}
{
  "name": "Lib · Before / after",
  "tag": "section",
  "class": "lib-section",
  "settings": [
    { "type": "header", "content": "Content" },
    { "type": "inline_richtext", "id": "heading", "label": "Heading", "default": "Ten minutes, <em>years</em> back" },
    {
      "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" },
        { "value": "xlarge", "label": "Extra large" }
      ],
      "default": "large"
    },
    { "type": "richtext", "id": "text", "label": "Text", "default": "<p>Drag the divider to see one bag before and after a single treatment with our cleaner and beeswax conditioner. Same light, same bench, nothing retouched but the leather.</p>" },

    { "type": "header", "content": "Images" },
    { "type": "paragraph", "content": "Use two photos taken from the same spot. Both are shown in the same frame, so they always share one shape." },
    { "type": "image_picker", "id": "image_before", "label": "Before image (desktop)" },
    { "type": "image_picker", "id": "image_before_mobile", "label": "Before image (mobile)", "info": "Optional. Shown below 750px." },
    { "type": "image_picker", "id": "image_after", "label": "After image (desktop)", "info": "Shown on its own when JavaScript is off." },
    { "type": "image_picker", "id": "image_after_mobile", "label": "After image (mobile)", "info": "Optional. Shown below 750px. Set both mobile images or neither." },
    {
      "type": "select",
      "id": "image_ratio",
      "label": "Image shape on desktop",
      "info": "Adapt uses the shape of the after image.",
      "options": [
        { "value": "adapt", "label": "Adapt to image" },
        { "value": "square", "label": "Square" },
        { "value": "portrait", "label": "Portrait" },
        { "value": "landscape", "label": "Landscape" },
        { "value": "wide", "label": "Wide" }
      ],
      "default": "landscape"
    },
    {
      "type": "select",
      "id": "image_ratio_mobile",
      "label": "Image shape on mobile",
      "options": [
        { "value": "adapt", "label": "Adapt to image" },
        { "value": "square", "label": "Square" },
        { "value": "portrait", "label": "Portrait" },
        { "value": "landscape", "label": "Landscape" },
        { "value": "wide", "label": "Wide" }
      ],
      "default": "portrait"
    },
    { "type": "range", "id": "image_radius", "label": "Image corner radius", "min": 0, "max": 40, "step": 2, "unit": "px", "default": 16 },

    { "type": "header", "content": "Slider" },
    {
      "type": "select",
      "id": "orientation",
      "label": "Divider direction",
      "options": [
        { "value": "horizontal", "label": "Left to right" },
        { "value": "vertical", "label": "Top to bottom" }
      ],
      "default": "horizontal"
    },
    { "type": "range", "id": "start_position", "label": "Start position", "info": "How much of the before image shows at first.", "min": 0, "max": 100, "step": 5, "unit": "%", "default": 50 },
    {
      "type": "select",
      "id": "knob_style",
      "label": "Handle style",
      "options": [
        { "value": "circle", "label": "Circle with arrows" },
        { "value": "arrows", "label": "Arrows only" },
        { "value": "line", "label": "Line only" }
      ],
      "default": "circle"
    },
    { "type": "checkbox", "id": "show_labels", "label": "Show labels on the images", "default": true },
    { "type": "text", "id": "label_before", "label": "Before label", "default": "Before" },
    { "type": "text", "id": "label_after", "label": "After label", "default": "After" },
    {
      "type": "select",
      "id": "label_position",
      "label": "Label position",
      "visible_if": "{{ section.settings.show_labels and section.settings.orientation == 'horizontal' }}",
      "options": [
        { "value": "top", "label": "Top" },
        { "value": "bottom", "label": "Bottom" }
      ],
      "default": "top"
    },

    { "type": "header", "content": "Button" },
    { "type": "text", "id": "button_label", "label": "Button label", "default": "Shop the care kit" },
    { "type": "url", "id": "button_url", "label": "Button link" },
    {
      "type": "select",
      "id": "button_style",
      "label": "Button style",
      "options": [
        { "value": "primary", "label": "Solid" },
        { "value": "outline", "label": "Outline" },
        { "value": "text", "label": "Text link" }
      ],
      "default": "primary"
    },

    { "type": "header", "content": "Layout" },
    {
      "type": "select",
      "id": "layout",
      "label": "Layout",
      "options": [
        { "value": "stacked", "label": "Text above, image full width" },
        { "value": "beside", "label": "Image beside text" }
      ],
      "default": "beside"
    },
    {
      "type": "select",
      "id": "image_position",
      "label": "Image position on desktop",
      "visible_if": "{{ section.settings.layout == 'beside' }}",
      "options": [
        { "value": "right", "label": "Right" },
        { "value": "left", "label": "Left" }
      ],
      "default": "right"
    },
    {
      "type": "select",
      "id": "mobile_order",
      "label": "Order on mobile",
      "options": [
        { "value": "text_first", "label": "Text first" },
        { "value": "image_first", "label": "Image first" }
      ],
      "default": "text_first"
    },
    {
      "type": "select",
      "id": "section_width",
      "label": "Width",
      "options": [
        { "value": "page", "label": "Page width" },
        { "value": "full", "label": "Full width" }
      ],
      "default": "page"
    },
    { "type": "text_alignment", "id": "text_alignment", "label": "Text alignment on desktop", "default": "left" },
    {
      "type": "select",
      "id": "text_alignment_mobile",
      "label": "Text alignment on mobile",
      "options": [
        { "value": "left", "label": "Left" },
        { "value": "center", "label": "Center" },
        { "value": "right", "label": "Right" }
      ],
      "default": "left"
    },

    { "type": "header", "content": "Colors" },
    { "type": "color", "id": "color_bg", "label": "Background", "default": "#F6F1EA" },
    { "type": "color_background", "id": "color_bg_gradient", "label": "Background gradient", "info": "Optional. Covers the background color." },
    { "type": "color", "id": "color_text", "label": "Text", "default": "#2B211A" },
    { "type": "color", "id": "color_accent", "label": "Accent (button, focus ring)", "default": "#8A4B22" },
    { "type": "color", "id": "color_accent_text", "label": "Text on accent", "default": "#FFFFFF" },
    { "type": "color", "id": "color_knob", "label": "Divider, handle and label background", "default": "#FFFFFF" },
    { "type": "color", "id": "color_knob_icon", "label": "Handle arrows and label text", "default": "#2B211A" },

    { "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": 88 },
    { "type": "range", "id": "padding_bottom", "label": "Bottom padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 88 },
    { "type": "range", "id": "padding_top_mobile", "label": "Top padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 48 },
    { "type": "range", "id": "padding_bottom_mobile", "label": "Bottom padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 56 },
    {
      "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." }
  ],
  "presets": [
    { "name": "Lib · Before / after" }
  ]
}
{% endschema %}
```

### 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 %}
```

### snippets/lib-button.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{% doc %}
  Animated call-to-action link. Light sweep on hover, arrow slides right.
  Colors come from the parent section's --lib-accent / --lib-accent-text / --lib-text vars.
  Inspired by Uiverse.io button patterns; rewritten for themes, no dependencies.

  @param {string} label - Button text
  @param {string} [url] - Link target; renders a disabled-looking span when blank
  @param {string} [style] - 'primary' (default), 'outline' or 'text'
  @param {boolean} [new_tab] - Open the link in a new tab
  @param {boolean} [arrow] - Show the trailing arrow (default true)

  @example
  {% render 'lib-button', label: section.settings.button_label, url: section.settings.button_url, style: 'outline' %}
{% enddoc %}

{%- liquid
  assign btn_style = style | default: 'primary'
  assign show_arrow = true
  if arrow == false
    assign show_arrow = false
  endif
-%}

{%- unless label == blank -%}
{%- capture inner -%}
  <span class="lib-btn__label">{{ label | escape }}</span>
  {%- if show_arrow -%}
    <svg class="lib-btn__arrow" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false">
      <path d="M3 8h9M8.5 4.5 12 8l-3.5 3.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>
  {%- endif -%}
{%- endcapture -%}

{%- if url != blank -%}
  <a
    class="lib-btn lib-btn--{{ btn_style }}"
    href="{{ url }}"
    {% if new_tab %}
      target="_blank" rel="noopener"
    {% endif %}
  >
    {{- inner -}}
  </a>
{%- else -%}
  <span class="lib-btn lib-btn--{{ btn_style }}" aria-disabled="true">{{ inner }}</span>
{%- endif -%}
{%- endunless -%}

{% stylesheet %}
  .lib-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    padding: 0.85em 1.5em;
    border-radius: var(--lib-radius, 999px);
    font: inherit;
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    overflow: hidden;
    isolation: isolate;
    transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
  }
  .lib-btn--primary {
    background: var(--lib-accent, #111);
    color: var(--lib-accent-text, #fff);
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.12);
  }
  .lib-btn--outline {
    background: transparent;
    color: var(--lib-text, #111);
    box-shadow: inset 0 0 0 1.5px currentColor;
  }
  .lib-btn--text {
    padding-inline: 0;
    background: none;
    color: var(--lib-text, #111);
    text-decoration: underline;
    text-underline-offset: 0.3em;
    overflow: visible;
  }
  .lib-btn--text::before { display: none; }
  .lib-btn--text:hover { transform: none; box-shadow: none; }
  .lib-btn::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(110deg, transparent 30%, rgb(255 255 255 / 0.35) 50%, transparent 70%);
    transform: translateX(-120%);
    transition: transform 0.6s ease;
  }
  .lib-btn--outline::before {
    background: linear-gradient(110deg, transparent 30%, rgb(127 127 127 / 0.18) 50%, transparent 70%);
  }
  .lib-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px -8px rgb(0 0 0 / 0.35);
  }
  .lib-btn:hover::before { transform: translateX(120%); }
  .lib-btn__arrow { transition: transform 0.2s ease; }
  .lib-btn:hover .lib-btn__arrow { transform: translateX(3px); }
  .lib-btn:focus-visible {
    outline: 2px solid var(--lib-accent, #111);
    outline-offset: 3px;
  }
  .lib-btn[aria-disabled='true'] { pointer-events: none; }
  @media (prefers-reduced-motion: reduce) {
    .lib-btn, .lib-btn::before, .lib-btn__arrow { transition: none; }
    .lib-btn:hover { transform: none; }
  }
{% 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.
