# Bülten pop-up’ı: ücretsiz Shopify bölümü

> İsteğe bağlı indirim kodlu e-posta kayıt pop-up’ı; bir süre sonra, çıkış niyetinde ya da kaydırınca açılır, sayfa yüklenirken asla. Mobilde küçük bir kart.

Bülten pop-up’ı, isteğe bağlı bir görsel ve indirim koduyla bülten kaydı sunan ücretsiz bir Shopify bölümüdür. Pop-up bir süre geçtikten sonra, masaüstünde imleç pencereden çıkarken ya da müşteri sayfanın bir kısmını kaydırdıktan sonra açılır; sayfa yüklenirken asla açılmaz. Shopify’ın kendi müşteri formunu kullanır; her e-posta "newsletter" ve sizin seçtiğiniz bir etiketle müşteri olarak kaydedilir. Telefonlarda tam ekran bir katman değil, ekranın altında küçük bir karttır; masaüstünde ortada bir pencere ya da köşede bir karttır. Bir düğmeyle, Esc ile ya da dışarıya tıklayınca kapanır; kapatıldıktan sonra belirlediğiniz gün sayısı kadar, kayıttan sonra ise tamamen gizli kalır ve her ziyarette en fazla bir kez görünür. İndirimin kendisi Shopify’da oluşturulur; bölüm yalnızca kodu gösterir.

- **Kategori:** Dönüşüm ve kampanyalar
- **Kullanıldığı yerler:** Ana sayfa, Koleksiyon, Ürün, Her sayfa
- **Okuduğu veri:** Shopify müşteri formu
- **Boyut:** 1 dosyada 25 KB, küçük bir betikle
- **Lisans:** MIT, © 2026 Lintel
- **Sayfa:** https://lintelagency.com/tr/kaynaklar/shopify-bolumleri/bulten-popup
- **Canlı demo:** https://lintelagency.com/resources/shopify-sections/demo/lib-newsletter-popup.html
- **Eklenme tarihi:** 2026-11-02

## Ne işe yarar

- Yeni ziyaretçilere ilk siparişe özel bir kod gibi hoş geldin teklifi sunmak
- Bir lansman ya da sezon indirimi öncesinde e-posta toplamak
- Koleksiyon ve ürün sayfalarında çıkış niyetinde bir hatırlatma

## Tema düzenleyici ayarları

Bölüm tema düzenleyicide “Lib · Newsletter popup” 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.

### Offer

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `heading` | `inline_richtext` | Heading | 10% off your first order |  |
| `heading_tag` | `select` | Heading tag | h2 | `h2`, `h3`, `p` |
| `heading_size` | `select` | Heading size | medium | `small`, `medium`, `large` |
| `text` | `richtext` | Text | `<p>Join the studio letter for new pieces and kiln days. One email a month.</p>` |  |
| `image` | `image_picker` | Image | – |  |
| `discount_code` | `text` | Discount code | – |  |
| `code_label` | `text` | Code label | Use code |  |
| `copy_label` | `text` | Copy button | Copy |  |
| `copied_label` | `text` | Copied message | Code copied |  |

### Form

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `input_label` | `text` | Field label | Email address |  |
| `placeholder` | `text` | Field placeholder | you@example.com |  |
| `button_label` | `text` | Button label | Subscribe |  |
| `customer_tag` | `text` | Extra customer tag | popup |  |
| `consent` | `inline_richtext` | Consent text | `Unsubscribe any time. See our <a href="/policies/privacy-policy">privacy policy</a>.` |  |
| `success_message` | `text` | Success message | Thanks for joining. Check your inbox for the code. |  |
| `close_label` | `text` | Close button label | Close |  |

### When it opens

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `trigger_delay` | `checkbox` | After a delay | `true` |  |
| `delay_seconds` | `range` | Delay | `20` | 10–120 s, step 5 |
| `trigger_exit` | `checkbox` | When the pointer leaves the window | `true` |  |
| `trigger_scroll` | `checkbox` | After scrolling | `false` |  |
| `scroll_percent` | `range` | Scrolled share of the page | `50` | 20–90 %, step 5 |
| `remember_days` | `range` | Stay closed for | `14` | 1–90 d, step 1 |

### Layout

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `desktop_style` | `select` | On desktop | modal | `modal`, `corner` |
| `text_alignment` | `text_alignment` | Text alignment on desktop | left |  |
| `text_alignment_mobile` | `select` | Text alignment on mobile | left | `left`, `center`, `right` |
| `show_on` | `select` | Show on | all | `all`, `desktop`, `mobile` |

### Colors

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `color_bg` | `color` | Background | #FFFDF9 |  |
| `color_bg_gradient` | `color_background` | Background gradient | – |  |
| `color_text` | `color` | Text | #2A2420 |  |
| `color_accent` | `color` | Accent (button, focus ring) | #2A2420 |  |
| `color_accent_text` | `color` | Text on accent | #FFFDF9 |  |
| `backdrop_opacity` | `range` | Page dimming behind the dialog | `45` | 0–80 %, step 5 |

### Motion

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `animate` | `checkbox` | Fade in | `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-newsletter-popup.liquid` adında yeni bir dosya ekleyin, aşağıdaki kaynak koddan `sections/lib-newsletter-popup.liquid` içeriğini yapıştırıp kaydedin.
4. Kopyada **Özelleştir**’i açın, **Bölüm ekle**’yi seçin ve **Lib · Newsletter popup** bölümünü ekleyin. Örnek içeriği kendi içeriğinizle değiştirin, renkleri ayarlayın.
5. 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-newsletter-popup.liquid https://lintelagency.com/resources/shopify-sections/files/lib-newsletter-popup.liquid
shopify theme push --theme <unpublished-theme-id> --only sections/lib-newsletter-popup.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-newsletter-popup.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-newsletter-popup.liquid): `sections/` klasörüne

## Sık sorulan sorular

### SEO’ya zarar verir ya da mobil müşterileri rahatsız eder mi?

Sayfa yüklenirken hiç açılmaz; ziyaretçinin aradığı içerik önce görünür. Google, özellikle mobilde, aramadan gelen ziyaretçi sayfaya girer girmez ana içeriği örten pop-up’lara karşı uyarır. 750px’in altında bu bölüm ekranın altında küçük bir karttır ve sayfa okunabilir, kullanılabilir kalır; tam ekran bir ara sayfa değildir.

### E-postalar nereye gidiyor?

Shopify’ın müşteri formu üzerinden Shopify müşterilerinize; "newsletter" ve belirlediğiniz ek etiketle (varsayılan "popup") kaydedilir, böylece bu kayıtları ayırt edebilirsiniz. Ardından Shopify Email ya da müşterileri eşitleyen bir e-posta uygulamasıyla onlara e-posta gönderebilirsiniz.

### İndirim kodu kendiliğinden çalışır mı?

Hayır. İndirimi Shopify’da İndirimler bölümünden oluşturun; bölüm yalnızca yazdığınız kodu bir kopyalama düğmesiyle gösterir.

### Ne sıklıkla görünüyor?

Her ziyarette en fazla bir kez. Müşteri kapattıktan sonra belirlediğiniz gün sayısı (1–90) kadar uzak durur; kayıttan sonra bir yıl boyunca görünmez. Başlığı ya da kodu değiştirirseniz bu yeni bir teklif sayılır ve eskisini kapatanlara yeniden görünür. Müşterinin tarayıcısında yalnızca bu tercih saklanır, kişisel veri tutulmaz.

### Erişilebilir mi?

Masaüstünde başlığıyla etiketlenmiş yerleşik bir modal penceredir: odak içine taşınır, Esc ya da kapat düğmesi onu kapatır ve odak önceki yerine döner. Mobil ve köşe kartları da Esc ile kapanır. E-posta alanının gerçek bir etiketi vardır, her kelime bir ayardır ve müşteri hareketin azaltılmasını tercih ettiğinde belirme efekti kapalıdır. JavaScript olmadan pop-up hiç görünmez, yani sayfayı asla engellemez.

### Tema düzenleyicide nasıl düzenlerim?

Pop-up, bölümü tema düzenleyicide seçtiğinizde açılır, başka bir şey seçtiğinizde kapanır; düzenleyicide zamanlayıcılar çalışmaz.

## Kaynak kod

### sections/lib-newsletter-popup.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{%- comment -%}
  Newsletter popup: an offer with Shopify's customer form (the email is added as a customer tagged
  "newsletter" plus a tag you choose), an optional image and an optional discount code with a copy
  button. It never shows on first paint: it opens after a delay, when the pointer leaves the top of
  the window (desktop only) and/or after the shopper scrolls part of the page, whichever comes
  first. On screens below 750px it is a small card at the bottom of the screen that leaves the page
  usable, never a full-screen overlay; on desktop a centered modal dialog or a corner card.
  Closes with the close button, Esc or a click outside; a closed offer stays away for the days you
  set, a sign-up for good, and it shows at most once per visit. After a sign-up the page reloads
  with the success message in the popup. Without JavaScript it does not appear at all.
  The discount itself is created in Shopify (Discounts); this section only shows the code.
  Depends on: none
{%- endcomment -%}

{%- liquid
  assign s = section.settings
  assign heading_tag = s.heading_tag | default: 'h2'
  assign form_id = 'lib-popup-form-' | append: section.id
  assign title_id = 'LibPopupTitle-' | append: section.id
  assign input_id = 'LibPopupEmail-' | append: section.id
  assign error_id = 'LibPopupError-' | append: section.id
  assign consent_id = 'LibPopupConsent-' | append: section.id
  assign tags = 'newsletter'
  if s.customer_tag != blank
    assign tags = tags | append: ',' | append: s.customer_tag
  endif
  assign offer_key = s.heading | strip_html | append: '|' | append: s.discount_code
  assign has_image = false
  if s.image != blank
    assign has_image = true
  endif
-%}

<lib-newsletter-popup
  class="lib-popup"
  data-section-id="{{ section.id }}"
  data-form-id="{{ form_id }}"
  data-show="{{ s.show_on }}"
  data-desktop="{{ s.desktop_style }}"
  data-offer="{{ offer_key | escape }}"
  data-days="{{ s.remember_days }}"
  {% if s.trigger_delay %}
    data-delay="{{ s.delay_seconds }}"
  {% endif %}
  {% if s.trigger_exit %}
    data-exit
  {% endif %}
  {% if s.trigger_scroll %}
    data-scroll="{{ s.scroll_percent }}"
  {% endif %}
  {% if s.animate %}
    data-animate
  {% 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-backdrop-o: {{ s.backdrop_opacity | divided_by: 100.0 }};
  "
>
  <dialog class="lib-popup__dialog" aria-labelledby="{{ title_id }}" data-has-image="{{ has_image }}">
    <button type="button" class="lib-popup__close" aria-label="{{ s.close_label | default: 'Close' | escape }}">
      <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6 6 18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
    </button>

    {%- if s.image != blank -%}
      <div class="lib-popup__media">
        {{
          s.image
          | image_url: width: 900
          | image_tag: class: 'lib-popup__img', loading: 'lazy', sizes: '360px', widths: '360, 540, 720, 900'
        }}
      </div>
    {%- endif -%}

    <div class="lib-popup__body" data-align="{{ s.text_alignment }}" data-align-m="{{ s.text_alignment_mobile }}">
      <{{ heading_tag }} class="lib-popup__heading lib-popup__heading--{{ s.heading_size }}" id="{{ title_id }}">
        {{- s.heading | default: 'Newsletter' -}}
      </{{ heading_tag }}>
      {%- if s.text != blank -%}
        <div class="lib-popup__text">{{ s.text }}</div>
      {%- endif -%}

      {%- if s.discount_code != blank -%}
        <div class="lib-popup__code">
          <span class="lib-popup__code-label">{{ s.code_label | escape }}</span>
          <span class="lib-popup__code-value">{{ s.discount_code | escape }}</span>
          <button
            type="button"
            class="lib-popup__copy"
            data-code="{{ s.discount_code | escape }}"
            data-copied="{{ s.copied_label | escape }}"
            hidden
          >
            {{- s.copy_label | escape -}}
          </button>
          <span class="lib-popup__sr" role="status" data-copy-status></span>
        </div>
      {%- endif -%}

      {%- form 'customer', id: form_id, class: 'lib-popup__form' -%}
        <input type="hidden" name="contact[tags]" value="{{ tags | escape }}">
        {%- if form.posted_successfully -%}
          <p class="lib-popup__msg lib-popup__msg--success" role="status">{{ s.success_message | escape }}</p>
        {%- else -%}
          {%- if form.errors -%}
            <div class="lib-popup__msg lib-popup__msg--error" id="{{ error_id }}" role="alert">
              {%- for field in form.errors -%}
                <p>
                  {%- if field == 'form' -%}
                    {{ form.errors.messages[field] }}
                  {%- else -%}
                    {{ form.errors.translated_fields[field] | capitalize }}
                    {{ form.errors.messages[field] }}
                  {%- endif -%}
                </p>
              {%- endfor -%}
            </div>
          {%- endif -%}
          <label class="lib-popup__label" for="{{ input_id }}">{{ s.input_label | default: 'Email' | escape }}</label>
          <div class="lib-popup__row">
            <input
              class="lib-popup__input"
              id="{{ input_id }}"
              type="email"
              name="contact[email]"
              required
              autocomplete="email"
              autocapitalize="off"
              spellcheck="false"
              {% if s.placeholder != blank %}
                placeholder="{{ s.placeholder | escape }}"
              {% endif %}
              {% if form.errors %}
                aria-invalid="true"
              {% endif %}
              {% if form.errors or s.consent != blank %}
                aria-describedby="{% if form.errors %}{{ error_id }} {% endif %}{% if s.consent != blank %}{{ consent_id }}{% endif %}"
              {% endif %}
            >
            <button type="submit" class="lib-popup__submit">{{ s.button_label | default: 'Subscribe' | escape }}</button>
          </div>
          {%- if s.consent != blank -%}
            <p class="lib-popup__consent" id="{{ consent_id }}">{{ s.consent }}</p>
          {%- endif -%}
        {%- endif -%}
      {%- endform -%}
    </div>
  </dialog>
</lib-newsletter-popup>

{% stylesheet %}
  .lib-popup { display: contents; }
  .lib-popup__dialog {
    box-sizing: border-box;
    width: min(100vw - 2rem, 46rem);
    max-height: calc(100vh - 2rem);
    margin: auto;
    padding: 0;
    border: 0;
    border-radius: 16px;
    overflow: auto;
    background-color: var(--lib-bg);
    background-image: var(--lib-bg-image, none);
    color: var(--lib-text);
    box-shadow: 0 30px 80px -30px rgb(0 0 0 / 0.45);
  }
  .lib-popup__dialog[open] { display: grid; grid-template-columns: 1fr; }
  .lib-popup__dialog[open][data-has-image='true'] { grid-template-columns: 2fr 3fr; }
  .lib-popup__dialog::backdrop { background: rgb(12 10 9 / var(--lib-backdrop-o, 0.5)); }
  .lib-popup__dialog:not([data-has-image='true']) { width: min(100vw - 2rem, 30rem); }

  /* Non-modal cards: the desktop corner card and, below 750px, the bottom card. */
  .lib-popup__dialog[data-mode='corner'],
  .lib-popup__dialog[data-mode='sheet'] {
    position: fixed;
    z-index: 2147483000;
    inset: auto 1.25rem 1.25rem auto;
    margin: 0;
    width: min(100vw - 2.5rem, 24rem);
  }
  .lib-popup__dialog[data-mode='corner'][open],
  .lib-popup__dialog[data-mode='sheet'][open] { grid-template-columns: 1fr; }
  .lib-popup__dialog[data-mode='corner'] .lib-popup__media,
  .lib-popup__dialog[data-mode='sheet'] .lib-popup__media { display: none; }
  .lib-popup__dialog[data-mode='sheet'] {
    inset: auto 0.75rem 0.75rem 0.75rem;
    width: auto;
    max-height: 45vh;
    border-radius: 14px;
  }
  .lib-popup__dialog[data-mode='sheet'] .lib-popup__body { padding: 1.125rem 1.125rem 1rem; }
  .lib-popup__dialog[data-mode='sheet'] .lib-popup__text { font-size: 0.9375rem; margin-top: 0.375rem; }
  .lib-popup__dialog[data-mode='sheet'] .lib-popup__heading { font-size: 1.25rem; padding-right: 2.5rem; }

  .lib-popup__media { position: relative; min-height: 100%; background: color-mix(in srgb, var(--lib-text) 8%, transparent); }
  .lib-popup__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
  .lib-popup__body { display: flex; flex-direction: column; align-items: flex-start; text-align: left; padding: clamp(1.5rem, 4vw, 2.5rem); }
  .lib-popup__body[data-align='center'] { align-items: center; text-align: center; }
  .lib-popup__body[data-align='right'] { align-items: flex-end; text-align: right; }
  @media (max-width: 749px) {
    .lib-popup__body[data-align-m='left'] { align-items: flex-start; text-align: left; }
    .lib-popup__body[data-align-m='center'] { align-items: center; text-align: center; }
    .lib-popup__body[data-align-m='right'] { align-items: flex-end; text-align: right; }
  }
  .lib-popup__close {
    position: absolute;
    top: 0.625rem;
    right: 0.625rem;
    z-index: 1;
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border: 0;
    border-radius: 50%;
    background: color-mix(in srgb, var(--lib-bg) 85%, transparent);
    color: var(--lib-text);
    cursor: pointer;
  }
  .lib-popup__close:hover { background: color-mix(in srgb, var(--lib-text) 10%, var(--lib-bg)); }
  .lib-popup__close:focus-visible,
  .lib-popup__copy:focus-visible,
  .lib-popup__submit:focus-visible { outline: 2px solid var(--lib-accent); outline-offset: 2px; }

  .lib-popup__heading { margin: 0; padding-right: 2rem; font-size: clamp(1.5rem, 2.6vw, 2rem); line-height: 1.15; letter-spacing: -0.02em; text-wrap: balance; }
  .lib-popup__heading--small { font-size: 1.25rem; }
  .lib-popup__heading--large { font-size: clamp(1.75rem, 3.2vw, 2.5rem); }
  .lib-popup__text { margin-top: 0.75rem; font-size: 1rem; line-height: 1.55; opacity: 0.85; }
  .lib-popup__text > :first-child { margin-top: 0; }
  .lib-popup__text > :last-child { margin-bottom: 0; }

  .lib-popup__code {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.75rem;
    margin-top: 1rem;
    padding: 0.625rem 0.875rem;
    border: 1.5px dashed color-mix(in srgb, var(--lib-text) 40%, transparent);
    border-radius: 10px;
  }
  .lib-popup__code-label { font-size: 0.875rem; opacity: 0.8; }
  .lib-popup__code-value { font-weight: 700; letter-spacing: 0.08em; }
  .lib-popup__copy {
    min-height: 2rem;
    padding: 0.25rem 0.75rem;
    border: 1.5px solid currentColor;
    border-radius: 999px;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
  }

  .lib-popup__form { width: 100%; margin-top: 1.125rem; }
  .lib-popup__label { display: block; margin-bottom: 0.375rem; font-size: 0.875rem; font-weight: 600; text-align: left; }
  .lib-popup__row { display: flex; gap: 0.5rem; }
  .lib-popup__input {
    flex: 1;
    min-width: 0;
    min-height: 2.875rem;
    padding: 0 0.875rem;
    border: 1.5px solid color-mix(in srgb, var(--lib-text) 35%, transparent);
    border-radius: 999px;
    background: #fff;
    color: #1c1917;
    font: inherit;
  }
  .lib-popup__input:focus-visible { outline: 2px solid var(--lib-accent); outline-offset: 1px; }
  .lib-popup__submit {
    flex: none;
    min-height: 2.875rem;
    padding: 0 1.25rem;
    border: 0;
    border-radius: 999px;
    background: var(--lib-accent);
    color: var(--lib-accent-text);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
  }
  .lib-popup__consent { margin: 0.75rem 0 0; font-size: 0.8125rem; line-height: 1.45; opacity: 0.8; text-align: left; }
  .lib-popup__consent a { color: inherit; text-underline-offset: 0.2em; }
  .lib-popup__msg { margin: 0 0 0.75rem; font-weight: 600; text-align: left; }
  .lib-popup__msg p { margin: 0; }
  .lib-popup__msg--success { margin: 0; padding: 0.75rem 1rem; border-radius: 10px; background: color-mix(in srgb, var(--lib-accent) 12%, transparent); }
  .lib-popup__msg--error { color: #b42318; }
  .lib-popup__sr {
    position: absolute !important;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  @media (prefers-reduced-motion: no-preference) {
    .lib-popup[data-animate] .lib-popup__dialog[open] { animation: lib-popup-in 0.35s ease-out; }
    .lib-popup[data-animate] .lib-popup__dialog[data-mode='sheet'][open],
    .lib-popup[data-animate] .lib-popup__dialog[data-mode='corner'][open] { animation-name: lib-popup-up; }
  }
  @keyframes lib-popup-in {
    from { opacity: 0; transform: scale(0.97); }
  }
  @keyframes lib-popup-up {
    from { opacity: 0; transform: translateY(16px); }
  }
{% endstylesheet %}

{% javascript %}
  if (!customElements.get('lib-newsletter-popup')) {
    customElements.define(
      'lib-newsletter-popup',
      class extends HTMLElement {
        connectedCallback() {
          if (this._ready) return;
          this.dialog = this.querySelector('dialog');
          if (!this.dialog || typeof this.dialog.showModal !== 'function') return;
          this._ready = true;
          this.storeKey = `lib-popup:${this.dataset.sectionId}`;
          this.cleanup = [];
          this.listen(this.dialog.querySelector('.lib-popup__close'), 'click', () => this.close(true));
          this.listen(this.dialog, 'cancel', (event) => {
            event.preventDefault();
            this.close(true);
          });
          // A click on the backdrop lands on the dialog itself.
          this.listen(this.dialog, 'click', (event) => {
            if (event.target === this.dialog) this.close(true);
          });
          // Non-modal cards don't get Esc from the browser.
          this.listen(document, 'keydown', (event) => {
            if (event.key === 'Escape' && this.dialog.open && this.dialog.dataset.mode !== 'modal') this.close(true);
          });
          this.setupCopy();

          // Shopify renders the success message only after a successful post.
          if (this.dialog.querySelector('.lib-popup__msg--success')) this.setAttribute('data-posted', '');
          if (window.Shopify && window.Shopify.designMode) {
            this.setupEditor();
            return;
          }
          // Back from a sign-up (or a form error): show the result right away.
          if (location.hash === `#${this.dataset.formId}` && (this.hasAttribute('data-posted') || this.dialog.querySelector('[role="alert"]'))) {
            if (this.hasAttribute('data-posted')) this.remember('signed-up', 365);
            this.open();
            return;
          }
          if (this.isSuppressed()) return;
          this.setupTriggers();
        }

        disconnectedCallback() {
          (this.cleanup || []).forEach((undo) => undo());
          this.cleanup = [];
          clearTimeout(this.timer);
          this._ready = false;
        }

        listen(target, type, handler, options) {
          if (!target) return;
          target.addEventListener(type, handler, options);
          this.cleanup.push(() => target.removeEventListener(type, handler, options));
        }

        storage(kind) {
          try {
            return kind === 'session' ? window.sessionStorage : window.localStorage;
          } catch (error) {
            return null;
          }
        }

        isSuppressed() {
          if (this.dataset.show === 'desktop' && this.isSmall()) return true;
          if (this.dataset.show === 'mobile' && !this.isSmall()) return true;
          const session = this.storage('session');
          if (session && session.getItem(this.storeKey)) return true;
          const local = this.storage('local');
          if (!local) return false;
          try {
            const saved = JSON.parse(local.getItem(this.storeKey) || 'null');
            if (!saved) return false;
            if (saved.state === 'signed-up') return saved.until > Date.now();
            return saved.offer === this.dataset.offer && saved.until > Date.now();
          } catch (error) {
            return false;
          }
        }

        remember(state, days) {
          const local = this.storage('local');
          if (!local) return;
          const until = Date.now() + days * 24 * 60 * 60 * 1000;
          try {
            local.setItem(this.storeKey, JSON.stringify({ state, offer: this.dataset.offer, until }));
          } catch (error) {
            /* Storage full or blocked: the popup may show again, nothing else breaks. */
          }
        }

        setupTriggers() {
          const fire = () => this.open();
          const delay = Number(this.dataset.delay);
          if (delay > 0) this.timer = setTimeout(fire, delay * 1000);
          if (this.hasAttribute('data-exit') && window.matchMedia('(hover: hover) and (pointer: fine)').matches) {
            this.listen(document, 'mouseout', (event) => {
              if (!event.relatedTarget && event.clientY <= 0) fire();
            });
          }
          const share = Number(this.dataset.scroll);
          if (share > 0) {
            this.listen(
              window,
              'scroll',
              () => {
                const room = document.documentElement.scrollHeight - window.innerHeight;
                if (room > 0 && (window.scrollY / room) * 100 >= share) fire();
              },
              { passive: true }
            );
          }
        }

        setupEditor() {
          const id = this.dataset.sectionId;
          this.listen(document, 'shopify:section:select', (event) => {
            if (event.detail && event.detail.sectionId === id) this.open();
          });
          this.listen(document, 'shopify:section:deselect', (event) => {
            if (event.detail && event.detail.sectionId === id) this.close(false);
          });
        }

        setupCopy() {
          const button = this.querySelector('.lib-popup__copy');
          if (!button || !navigator.clipboard) return;
          button.hidden = false;
          const status = this.querySelector('[data-copy-status]');
          this.listen(button, 'click', () => {
            navigator.clipboard.writeText(button.dataset.code).then(
              () => {
                if (status) status.textContent = button.dataset.copied;
              },
              () => {
                /* Clipboard refused: the code stays visible to copy by hand. */
              }
            );
          });
        }

        isSmall() {
          return window.matchMedia('(max-width: 749px)').matches;
        }

        open() {
          if (this.dialog.open) return;
          clearTimeout(this.timer);
          const session = this.storage('session');
          if (session) {
            try {
              session.setItem(this.storeKey, '1');
            } catch (error) {
              /* Blocked storage only means it may show again on the next page. */
            }
          }
          this.returnFocus = document.activeElement;
          const mode = this.isSmall() ? 'sheet' : this.dataset.desktop === 'corner' ? 'corner' : 'modal';
          this.dialog.dataset.mode = mode;
          if (mode === 'modal') this.dialog.showModal();
          else this.dialog.show();
        }

        close(remember) {
          if (!this.dialog.open) return;
          this.dialog.close();
          if (remember && !this.hasAttribute('data-posted')) this.remember('closed', Number(this.dataset.days) || 1);
          if (this.returnFocus && this.returnFocus.isConnected && typeof this.returnFocus.focus === 'function') this.returnFocus.focus();
          this.returnFocus = null;
        }
      }
    );
  }
{% endjavascript %}

{% schema %}
{
  "name": "Lib · Newsletter popup",
  "tag": "section",
  "class": "lib-section",
  "settings": [
    { "type": "paragraph", "content": "Opens after a delay, on exit intent or after scrolling, never on page load. Below 750px it is a small card at the bottom of the screen. In the editor it shows while this section is selected." },
    { "type": "header", "content": "Offer" },
    { "type": "inline_richtext", "id": "heading", "label": "Heading", "default": "10% off your first order" },
    {
      "type": "select",
      "id": "heading_tag",
      "label": "Heading tag",
      "options": [
        { "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": "Text", "default": "<p>Join the studio letter for new pieces and kiln days. One email a month.</p>" },
    { "type": "image_picker", "id": "image", "label": "Image", "info": "Optional. Desktop modal only; the small cards leave it out." },
    { "type": "text", "id": "discount_code", "label": "Discount code", "info": "Optional. Create the discount in Shopify under Discounts; this only shows the code." },
    { "type": "text", "id": "code_label", "label": "Code label", "default": "Use code" },
    { "type": "text", "id": "copy_label", "label": "Copy button", "default": "Copy" },
    { "type": "text", "id": "copied_label", "label": "Copied message", "info": "Read by screen readers after copying.", "default": "Code copied" },

    { "type": "header", "content": "Form" },
    { "type": "text", "id": "input_label", "label": "Field label", "default": "Email address" },
    { "type": "text", "id": "placeholder", "label": "Field placeholder", "default": "you@example.com" },
    { "type": "text", "id": "button_label", "label": "Button label", "default": "Subscribe" },
    { "type": "text", "id": "customer_tag", "label": "Extra customer tag", "info": "Added next to \"newsletter\", so you can tell popup sign-ups apart.", "default": "popup" },
    { "type": "inline_richtext", "id": "consent", "label": "Consent text", "default": "Unsubscribe any time. See our <a href=\"/policies/privacy-policy\">privacy policy</a>." },
    { "type": "text", "id": "success_message", "label": "Success message", "default": "Thanks for joining. Check your inbox for the code." },
    { "type": "text", "id": "close_label", "label": "Close button label", "default": "Close" },

    { "type": "header", "content": "When it opens" },
    { "type": "checkbox", "id": "trigger_delay", "label": "After a delay", "default": true },
    { "type": "range", "id": "delay_seconds", "label": "Delay", "min": 10, "max": 120, "step": 5, "unit": "s", "default": 20, "visible_if": "{{ section.settings.trigger_delay }}" },
    { "type": "checkbox", "id": "trigger_exit", "label": "When the pointer leaves the window", "info": "Desktop only.", "default": true },
    { "type": "checkbox", "id": "trigger_scroll", "label": "After scrolling", "default": false },
    { "type": "range", "id": "scroll_percent", "label": "Scrolled share of the page", "min": 20, "max": 90, "step": 5, "unit": "%", "default": 50, "visible_if": "{{ section.settings.trigger_scroll }}" },
    { "type": "range", "id": "remember_days", "label": "Stay closed for", "info": "After a shopper closes it. A sign-up hides it for a year.", "min": 1, "max": 90, "step": 1, "unit": "d", "default": 14 },

    { "type": "header", "content": "Layout" },
    {
      "type": "select",
      "id": "desktop_style",
      "label": "On desktop",
      "options": [
        { "value": "modal", "label": "Centered dialog" },
        { "value": "corner", "label": "Corner card" }
      ],
      "default": "modal"
    },
    { "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": "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": "header", "content": "Colors" },
    { "type": "color", "id": "color_bg", "label": "Background", "default": "#FFFDF9" },
    { "type": "color_background", "id": "color_bg_gradient", "label": "Background gradient", "info": "Optional. Covers the background color." },
    { "type": "color", "id": "color_text", "label": "Text", "default": "#2A2420" },
    { "type": "color", "id": "color_accent", "label": "Accent (button, focus ring)", "default": "#2A2420" },
    { "type": "color", "id": "color_accent_text", "label": "Text on accent", "default": "#FFFDF9" },
    { "type": "range", "id": "backdrop_opacity", "label": "Page dimming behind the dialog", "min": 0, "max": 80, "step": 5, "unit": "%", "default": 45 },

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

## 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.
