# Video: ücretsiz Shopify bölümü

> Shopify, YouTube ya da Vimeo videosu: sayfa içi oynatıcı, sessiz arka plan döngüsü ya da lightbox açan kapak görseli. Oynatılana kadar hiçbir şey yüklenmez.

Video, Shopify’da barındırılan bir videoyu ya da bir YouTube veya Vimeo bağlantısını sayfa içi oynatıcı, duraklatma düğmeli sessiz bir arka plan döngüsü ya da videoyu bir lightbox’ta açan kapak görseli olarak gösteren ücretsiz bir Shopify bölümüdür. Oynatıcı yalnızca oynatıldığında ya da arka plan videosu yakına kaydırıldığında oluşturulur ve YouTube youtube-nocookie.com’dan oynatılır. Online Store 2.0 temalarında uygulama olmadan çalışır.

- **Kategori:** Görsel ve video
- **Kullanıldığı yerler:** Ana sayfa, Ürün, Hakkımızda, Açılış sayfaları
- **Okuduğu veri:** Shopify videosu ya da YouTube / Vimeo bağlantısı
- **Boyut:** 3 dosyada 40 KB, küçük bir betikle
- **Lisans:** MIT, © 2026 Lintel
- **Sayfa:** https://lintelagency.com/tr/kaynaklar/shopify-bolumleri/video
- **Canlı demo:** https://lintelagency.com/resources/shopify-sections/demo/lib-video.html
- **Eklenme tarihi:** 2026-10-02

## Ne işe yarar

- Ana sayfada ya da Hakkımızda sayfasında, kapak görselinden lightbox’ta açılan marka ya da yapım filmi
- Hero olarak bir başlık ve düğmenin arkasında sessiz, atmosferik bir döngü
- Ürün ya da açılış sayfasında sayfa içinde oynatılan ürün demosu veya nasıl yapılır klibi

## Tema düzenleyici ayarları

Bölüm tema düzenleyicide “Lib · Video” 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 |
| --- | --- | --- | --- | --- |
| `eyebrow` | `text` | Eyebrow | From the studio |  |
| `heading` | `inline_richtext` | Heading | `Made slowly, <em>by hand</em>` |  |
| `heading_tag` | `select` | Heading tag | h2 | `h1`, `h2`, `h3`, `p` |
| `heading_size` | `select` | Heading size | large | `small`, `medium`, `large`, `xlarge` |
| `text` | `richtext` | Text | `<p>Every bowl starts as a lump of local clay. Two minutes in the studio, from the wheel to the kiln.</p>` |  |
| `text_position` | `select` | Text position | above | `above`, `overlay` |

### Video

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `mode` | `select` | Display | lightbox | `inline`, `background`, `lightbox` |
| `video` | `video` | Shopify-hosted video | – |  |
| `video_url` | `video_url` | Or YouTube / Vimeo link | – |  |
| `video_title` | `text` | Video title | A bowl thrown on the wheel |  |
| `poster` | `image_picker` | Poster image on desktop | – |  |
| `poster_mobile` | `image_picker` | Poster image on mobile | – |  |
| `play_label` | `text` | Play button text | Watch the film |  |
| `autoplay` | `checkbox` | Autoplay (muted) | `false` |  |
| `muted` | `checkbox` | Start muted | `false` |  |
| `controls` | `checkbox` | Show player controls | `true` |  |
| `loop` | `checkbox` | Loop | `false` |  |

### Button

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

### Layout

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `ratio` | `select` | Shape on desktop | 16-9 | `16-9`, `21-9`, `4-3`, `1-1`, `9-16` |
| `ratio_mobile` | `select` | Shape on mobile | 4-3 | `16-9`, `21-9`, `4-3`, `1-1`, `9-16` |
| `section_width` | `select` | Width | page | `page`, `full` |
| `corner_radius` | `range` | Corner radius | `16` | 0–40 px, step 2 |
| `text_alignment` | `text_alignment` | Text alignment on desktop | center |  |
| `text_alignment_mobile` | `select` | Text alignment on mobile | left | `left`, `center`, `right` |
| `overlay_color` | `color` | Overlay color | #1C1917 |  |
| `overlay_opacity` | `range` | Overlay opacity | `15` | 0–90 %, step 5 |

### Colors

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `color_bg` | `color_background` | Background | #F5F1EA |  |
| `color_text` | `color` | Text | #1C1917 |  |
| `color_on_video` | `color` | Text over video | #FFFFFF |  |
| `color_accent` | `color` | Accent (play button, solid button) | #1C1917 |  |
| `color_accent_text` | `color` | Text on accent | #F5F1EA |  |

### Spacing and visibility

| Kimlik | Tür | Etiket | Varsayılan | Seçenekler ya da aralık |
| --- | --- | --- | --- | --- |
| `padding_top` | `range` | Top padding on desktop | `80` | 0–200 px, step 4 |
| `padding_bottom` | `range` | Bottom padding on desktop | `80` | 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 | `48` | 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-video.liquid` adında yeni bir dosya ekleyin, aşağıdaki kaynak koddan `sections/lib-video.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 · Video** 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-video.liquid https://lintelagency.com/resources/shopify-sections/files/lib-video.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-video.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-video.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-video.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

### Video sayfayı yavaşlatır mı?

Sayfayla birlikte hiçbir oynatıcı yüklenmez. Yalnızca kapak görseli yüklenir; video ya da iframe, ziyaretçi oynata bastığında, lightbox’ı açtığında ya da arka plan ve otomatik oynatılan videolarda bölüm görünüm alanına 200px yaklaştığında etkisiz bir şablondan oluşturulur.

### YouTube ve Vimeo için gizlilik dostu mu?

YouTube gömmeleri youtube-nocookie.com kullanır, Vimeo gömmeleri dnt=1 (do not track) ekler. Sayfa içi (otomatik oynatmasız) ve lightbox oynatıcılar yalnızca oynatıldığında gömülür; böylece oynata hiç basmayan ziyaretçiler YouTube’a ya da Vimeo’ya hiç bağlanmaz. Arka plan ve otomatik oynatılan videolar bölüm yakına kaydırıldığında yüklenir.

### Azaltılmış hareket tercih eden ziyaretçilerde ne olur?

Hiçbir şey otomatik oynatılmaz. Arka plan döngüleri ve sayfa içi otomatik oynatma kapak görselinde kalır; arka plan duraklat/oynat düğmesi videoyu ancak basıldığında başlatır.

### Klavye ve ekran okuyucu için erişilebilir mi?

Oynat düğmesi, arka plan duraklat/oynat düğmesi ve lightbox kapat düğmesi, video başlığınızı içeren etiketlere sahip gerçek düğmelerdir. Lightbox yerel bir dialog’dur: odak içine taşınır; Esc, kapat düğmesi ya da dışarıya tıklamak onu kapatır, oynatıcı kaldırılır ve odak oynat düğmesine döner.

### Mobilde farklı bir şekil kullanabilir miyim?

Evet. Masaüstü ve mobil ayrı ayrı 16:9, 21:9, 4:3, 1:1 ya da 9:16 alır ve ayrı bir mobil kapak görseli belirleyebilirsiniz. Arka plan videoları şekli dolduracak şekilde kırpılır.

### Metin videonun üzerinde durabilir mi?

Evet, arka plan ve lightbox videolarında. Sayfa içi oynatıcılar kontrolleri asla örtmemesi için metni her zaman üstte gösterir. Metin video şeklinden uzunsa video alanı metni sığdıracak kadar büyür.

## Kaynak kod

### sections/lib-video.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{%- comment -%}
  Video: a Shopify-hosted video or a YouTube / Vimeo link, shown three ways:
  - inline: poster with a play button, the player loads in place on click (or autoplays muted)
  - background: muted ambient loop with a pause / play button, no controls
  - lightbox: poster with a play button that opens the player in a native <dialog>
  The player (video or iframe) is only created on play, open or when a background video scrolls near,
  so nothing third-party loads with the page. YouTube uses youtube-nocookie.com, Vimeo dnt=1.
  Under prefers-reduced-motion nothing autoplays.
  Depends on: snippets/lib-image.liquid, snippets/lib-button.liquid
{%- endcomment -%}

{%- liquid
  assign s = section.settings
  assign heading_tag = s.heading_tag | default: 'h2'
  assign mode = s.mode | default: 'lightbox'
  assign eager = false
  if section.index <= 2
    assign eager = true
  endif

  assign text_position = s.text_position
  if mode == 'inline'
    assign text_position = 'above'
  endif

  assign src_type = ''
  assign src_id = ''
  if s.video != blank
    assign src_type = 'shopify'
  elsif s.video_url != blank
    assign src_type = s.video_url.type
    assign src_id = s.video_url.id
    if src_type == blank
      comment
        Fallback for a plain URL string (theme preview tools); Shopify gives type and id.
      endcomment
      assign raw = s.video_url | append: ''
      if raw contains 'vimeo.com/'
        assign src_type = 'vimeo'
        assign src_id = raw | split: 'vimeo.com/' | last | split: '?' | first | split: '/' | first
      elsif raw contains 'youtu.be/'
        assign src_type = 'youtube'
        assign src_id = raw | split: 'youtu.be/' | last | split: '?' | first
      elsif raw contains 'v='
        assign src_type = 'youtube'
        assign src_id = raw | split: 'v=' | last | split: '&' | first
      elsif raw contains '/shorts/' or raw contains '/embed/'
        assign src_type = 'youtube'
        assign src_id = raw | split: '/' | last | split: '?' | first
      endif
    endif
  endif

  assign autoplay = false
  if mode == 'inline' and s.autoplay
    assign autoplay = true
  endif
  assign muted = false
  if mode == 'background' or autoplay or mode == 'inline' and s.muted
    assign muted = true
  endif
  assign loop = false
  if mode == 'background' or s.loop
    assign loop = true
  endif
  assign controls = true
  if mode == 'background' or mode == 'inline' and s.controls == false
    assign controls = false
  endif

  assign embed = ''
  if src_type == 'youtube'
    assign embed = 'https://www.youtube-nocookie.com/embed/' | append: src_id | append: '?autoplay=1&playsinline=1&rel=0'
    if muted
      assign embed = embed | append: '&mute=1'
    endif
    if loop
      assign embed = embed | append: '&loop=1&playlist=' | append: src_id
    endif
    if controls == false
      assign embed = embed | append: '&controls=0'
    endif
    if mode == 'background'
      assign embed = embed | append: '&enablejsapi=1&disablekb=1&iv_load_policy=3'
    endif
  elsif src_type == 'vimeo'
    assign embed = 'https://player.vimeo.com/video/' | append: src_id | append: '?dnt=1'
    if mode == 'background'
      assign embed = embed | append: '&background=1'
    else
      assign embed = embed | append: '&autoplay=1'
      if muted
        assign embed = embed | append: '&muted=1'
      endif
      if loop
        assign embed = embed | append: '&loop=1'
      endif
      if controls == false
        assign embed = embed | append: '&controls=0'
      endif
    endif
  endif

  assign has_video = false
  if src_type == 'shopify' or embed != blank
    assign has_video = true
  endif

  assign poster = s.poster
  if poster == blank and src_type == 'shopify'
    assign poster = s.video.preview_image
  endif

  assign video_title = s.video_title | default: 'Video' | escape
  assign play_label = s.play_label | strip
  assign play_name = play_label | default: 'Play video' | escape | append: ': ' | append: video_title

  assign dialog_ratio = '16 / 9'
  if src_type == 'shopify' and s.video.aspect_ratio != blank
    assign dialog_ratio = s.video.aspect_ratio
  endif

  assign has_content = false
  if s.eyebrow != blank or s.heading != blank or s.text != blank or s.button_label != blank
    assign has_content = true
  endif
  if text_position == 'overlay' and mode == 'lightbox' and has_video
    assign has_content = true
  endif
-%}

{%- capture play_button -%}
  <button
    type="button"
    class="lib-video__play{% if play_label == blank %} lib-video__play--icon{% endif %}"
    aria-label="{{ play_name }}"
    {% if mode == 'lightbox' %}
      aria-haspopup="dialog"
    {% endif %}
  >
    <span class="lib-video__play-icon" aria-hidden="true">
      <svg viewBox="0 0 24 24" width="22" height="22" focusable="false"><path d="M8 5.5v13l10.5-6.5z" fill="currentColor"/></svg>
    </span>
    {%- if play_label != blank -%}
      <span class="lib-video__play-label">{{ play_label | escape }}</span>
    {%- endif -%}
  </button>
{%- endcapture -%}

<div
  class="lib-video lib-video--{{ s.section_width }}"
  {% if s.anchor_id != blank %}
    id="{{ s.anchor_id | handleize }}"
  {% endif %}
  data-show="{{ s.show_on }}"
  data-text="{{ text_position }}"
  style="
    --lib-bg: {{ s.color_bg }};
    --lib-text: {{ s.color_text }};
    --lib-on-video: {{ s.color_on_video }};
    --lib-accent: {{ s.color_accent }};
    --lib-accent-text: {{ s.color_accent_text }};
    --lib-overlay: {{ s.overlay_color }};
    --lib-overlay-o: {{ s.overlay_opacity | divided_by: 100.0 }};
    --lib-radius-frame: {{ s.corner_radius }}px;
    --lib-ratio: {{ s.ratio | replace: '-', ' / ' }};
    --lib-ratio-m: {{ s.ratio_mobile | replace: '-', ' / ' }};
    --lib-dialog-ratio: {{ dialog_ratio }};
    --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;
  "
>
  <lib-video-player
    class="lib-video__inner"
    data-mode="{{ mode }}"
    data-src="{{ src_type }}"
    data-toggle-label="Pause or play: {{ video_title }}"
    {% if autoplay %}
      data-autoplay
    {% endif %}
  >
    {%- if has_content -%}
      <div class="lib-video__content" data-align="{{ s.text_alignment }}" data-align-m="{{ s.text_alignment_mobile }}">
        {%- if s.eyebrow != blank -%}
          <p class="lib-video__eyebrow">{{ s.eyebrow | escape }}</p>
        {%- endif -%}

        {%- if s.heading != blank -%}
          <{{ heading_tag }} class="lib-video__heading lib-video__heading--{{ s.heading_size }}">
            {{- s.heading -}}
          </{{ heading_tag }}>
        {%- endif -%}

        {%- if s.text != blank -%}
          <div class="lib-video__text">{{ s.text }}</div>
        {%- endif -%}

        {%- if text_position == 'overlay' and mode == 'lightbox' and has_video -%}
          <div class="lib-video__actions">
            {{ play_button }}
            {%- render 'lib-button', label: s.button_label, url: s.button_url, style: s.button_style -%}
          </div>
        {%- elsif s.button_label != blank -%}
          <div class="lib-video__actions">
            {%- render 'lib-button', label: s.button_label, url: s.button_url, style: s.button_style -%}
          </div>
        {%- endif -%}
      </div>
    {%- endif -%}

    <div class="lib-video__stage">
      <div class="lib-video__poster">
        {%- render 'lib-image',
          image: poster,
          image_mobile: s.poster_mobile,
          class: 'lib-video__img',
          sizes: '(min-width: 1200px) 1200px, 100vw',
          eager: eager
        -%}
      </div>
      <div class="lib-video__slot"></div>
      <div class="lib-video__overlay" aria-hidden="true"></div>

      {%- if has_video -%}
        {%- if mode == 'background' -%}
          <button
            type="button"
            class="lib-video__toggle"
            aria-label="Play background video"
            data-label-play="Play background video"
            data-label-pause="Pause background video"
            data-state="paused"
          >
            <svg class="lib-video__toggle-play" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" focusable="false"><path d="M8 5.5v13l10.5-6.5z" fill="currentColor"/></svg>
            <svg class="lib-video__toggle-pause" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" focusable="false"><path d="M7 5h3.5v14H7zM13.5 5H17v14h-3.5z" fill="currentColor"/></svg>
          </button>
        {%- elsif text_position != 'overlay' or mode == 'inline' -%}
          <div class="lib-video__play-wrap">{{ play_button }}</div>
        {%- endif -%}
      {%- endif -%}
    </div>

    {%- if has_video -%}
      <template class="lib-video__tpl">
        {%- if src_type == 'shopify' -%}
          {{
            s.video
            | video_tag:
              class: 'lib-video__media',
              autoplay: true,
              muted: muted,
              loop: loop,
              controls: controls,
              playsinline: true,
              preload: 'metadata'
          }}
        {%- else -%}
          <iframe
            class="lib-video__media"
            src="{{ embed | escape }}"
            title="{{ video_title }}"
            allow="autoplay; encrypted-media; picture-in-picture; fullscreen"
            allowfullscreen
            referrerpolicy="strict-origin-when-cross-origin"
            {% if mode == 'background' %}
              tabindex="-1" aria-hidden="true"
            {% endif %}
          ></iframe>
        {%- endif -%}
      </template>

      {%- if mode == 'lightbox' -%}
        <dialog class="lib-video__dialog" aria-label="{{ video_title }}">
          <button type="button" class="lib-video__close" aria-label="Close video">
            <svg viewBox="0 0 24 24" width="22" height="22" 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>
          <div class="lib-video__dialog-frame"></div>
        </dialog>
      {%- endif -%}
    {%- endif -%}
  </lib-video-player>
</div>

{% stylesheet %}
  .lib-video {
    background: var(--lib-bg);
    color: var(--lib-text);
    padding: var(--lib-pt) 1rem var(--lib-pb);
  }
  .lib-video__inner {
    display: grid;
    gap: clamp(1.5rem, 3vw, 2.5rem);
    max-width: 1200px;
    margin: 0 auto;
  }
  .lib-video--full { padding-inline: 0; }
  .lib-video--full .lib-video__inner { max-width: none; }
  .lib-video--full[data-text='above'] .lib-video__content { padding-inline: 1rem; }
  @media (max-width: 749px) {
    .lib-video { padding-top: var(--lib-pt-m); padding-bottom: var(--lib-pb-m); }
    .lib-video[data-show='desktop'] { display: none; }
  }
  @media (min-width: 750px) {
    .lib-video[data-show='mobile'] { display: none; }
  }

  /* Content: above the video, or layered over it in the same grid cell. */
  .lib-video__content {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    width: 100%;
    max-width: 46rem;
  }
  .lib-video__content[data-align='center'] { align-items: center; text-align: center; margin-inline: auto; }
  .lib-video__content[data-align='right'] { align-items: flex-end; text-align: right; margin-inline-start: auto; }
  @media (max-width: 749px) {
    .lib-video__content[data-align-m] { margin-inline: 0; }
    .lib-video__content[data-align-m='left'] { align-items: flex-start; text-align: left; }
    .lib-video__content[data-align-m='center'] { align-items: center; text-align: center; margin-inline: auto; }
    .lib-video__content[data-align-m='right'] { align-items: flex-end; text-align: right; margin-inline-start: auto; }
  }
  .lib-video[data-text='overlay'] .lib-video__content,
  .lib-video[data-text='overlay'] .lib-video__stage { grid-area: 1 / 1; }
  /* Width is fixed, so a taller text block stretches the stage instead of widening it. */
  .lib-video[data-text='overlay'] .lib-video__stage { align-self: stretch; justify-self: stretch; width: 100%; }
  .lib-video[data-text='overlay'] .lib-video__content {
    position: relative;
    z-index: 2;
    align-self: center;
    justify-self: stretch;
    max-width: none;
    margin: 0;
    padding: clamp(1.5rem, 6vw, 4.5rem);
    color: var(--lib-on-video);
    pointer-events: none;
  }
  .lib-video[data-text='overlay'] .lib-video__content > * { max-width: 40rem; }
  .lib-video[data-text='overlay'] .lib-video__content :is(a, button) { pointer-events: auto; }
  .lib-video[data-text='overlay'] .lib-btn--outline,
  .lib-video[data-text='overlay'] .lib-btn--text { color: var(--lib-on-video); }

  .lib-video__eyebrow {
    margin: 0 0 0.9rem;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    opacity: 0.75;
  }
  .lib-video__heading {
    margin: 0;
    font-size: clamp(2rem, 4.5vw, 3.5rem);
    line-height: 1.06;
    letter-spacing: -0.03em;
    text-wrap: balance;
  }
  .lib-video__heading--small { font-size: clamp(1.5rem, 2.6vw, 2rem); }
  .lib-video__heading--medium { font-size: clamp(1.75rem, 3.4vw, 2.75rem); }
  .lib-video__heading--xlarge { font-size: clamp(2.5rem, 6.5vw, 5rem); }
  .lib-video__text {
    margin-top: 1rem;
    font-size: 1.0625rem;
    line-height: 1.6;
    opacity: 0.8;
  }
  .lib-video__text > :first-child { margin-top: 0; }
  .lib-video__text > :last-child { margin-bottom: 0; }
  .lib-video__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-top: 1.75rem;
  }
  .lib-video__content[data-align='center'] .lib-video__actions { justify-content: center; }
  .lib-video__content[data-align='right'] .lib-video__actions { justify-content: flex-end; }
  @media (max-width: 749px) {
    .lib-video__content[data-align-m='left'] .lib-video__actions { justify-content: flex-start; }
    .lib-video__content[data-align-m='center'] .lib-video__actions { justify-content: center; }
    .lib-video__content[data-align-m='right'] .lib-video__actions { justify-content: flex-end; }
  }

  /* Stage: reserves the chosen ratio per device; poster, player, overlay and controls are layered. */
  .lib-video__stage {
    position: relative;
    aspect-ratio: var(--lib-ratio);
    border-radius: var(--lib-radius-frame);
    overflow: hidden;
    isolation: isolate;
    background: color-mix(in srgb, var(--lib-text) 10%, #000);
    container-type: size;
  }
  @media (max-width: 749px) {
    .lib-video__stage { aspect-ratio: var(--lib-ratio-m); }
  }
  .lib-video--full .lib-video__stage { border-radius: 0; }
  .lib-video__poster,
  .lib-video__slot,
  .lib-video__overlay { position: absolute; inset: 0; }
  .lib-video__img { display: block; width: 100%; height: 100%; object-fit: cover; }
  .lib-video__img:is(svg) { fill: color-mix(in srgb, var(--lib-text) 30%, transparent); }
  .lib-video__slot { z-index: 1; }
  .lib-video__slot:empty { display: none; }
  .lib-video__media {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
    background: #000;
    object-fit: contain;
  }
  .lib-video__overlay {
    z-index: 2;
    background: var(--lib-overlay);
    opacity: var(--lib-overlay-o);
    pointer-events: none;
  }
  .lib-video__inner[data-mode='inline'][data-playing] .lib-video__overlay { display: none; }

  /* Background mode: the player covers the stage like an image; no pointer events. */
  .lib-video__inner[data-mode='background'] .lib-video__slot { pointer-events: none; }
  .lib-video__inner[data-mode='background'] video.lib-video__media { object-fit: cover; background: transparent; }
  .lib-video__inner[data-mode='background'] iframe.lib-video__media {
    position: absolute;
    top: 50%;
    left: 50%;
    width: max(100cqw, 177.78cqh);
    height: max(100cqh, 56.25cqw);
    transform: translate(-50%, -50%);
    background: transparent;
  }

  /* Play button (inline and lightbox) */
  .lib-video__play-wrap {
    position: absolute;
    inset: 0;
    z-index: 3;
    display: grid;
    place-items: center;
    pointer-events: none;
  }
  .lib-video__inner[data-playing] .lib-video__play-wrap { display: none; }
  .lib-video__play {
    display: inline-flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 1.4rem 0.5rem 0.5rem;
    border: 0;
    border-radius: 999px;
    background: var(--lib-accent);
    color: var(--lib-accent-text);
    font: inherit;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    pointer-events: auto;
    box-shadow: 0 10px 30px -12px rgb(0 0 0 / 0.5);
    transition: transform 0.25s ease, box-shadow 0.25s ease;
  }
  .lib-video__play--icon { padding: 0.5rem; }
  .lib-video__play-icon {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    background: color-mix(in srgb, var(--lib-accent-text) 16%, transparent);
  }
  .lib-video__play-icon svg { margin-inline-start: 2px; }
  .lib-video__play--icon .lib-video__play-icon { width: 3.75rem; height: 3.75rem; background: none; }
  .lib-video__play:hover { transform: scale(1.04); box-shadow: 0 14px 34px -12px rgb(0 0 0 / 0.6); }
  .lib-video__play:focus-visible,
  .lib-video__toggle:focus-visible,
  .lib-video__close:focus-visible {
    outline: 2px solid var(--lib-accent-text);
    outline-offset: 3px;
    box-shadow: 0 0 0 5px var(--lib-accent);
  }

  /* Background toggle */
  .lib-video__toggle {
    position: absolute;
    inset-block-end: 1rem;
    inset-inline-end: 1rem;
    z-index: 3;
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgb(0 0 0 / 0.45);
    color: #fff;
    cursor: pointer;
    backdrop-filter: blur(6px);
  }
  .lib-video__toggle:hover { background: rgb(0 0 0 / 0.65); }
  .lib-video__toggle[data-state='playing'] .lib-video__toggle-play,
  .lib-video__toggle[data-state='paused'] .lib-video__toggle-pause { display: none; }
  .lib-video[data-text='overlay'] .lib-video__toggle { z-index: 3; }

  /* Lightbox dialog */
  .lib-video__dialog {
    box-sizing: border-box;
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    padding: clamp(3.5rem, 6vw, 4.5rem) 1rem;
    border: 0;
    background: transparent;
    color: #fff;
  }
  .lib-video__dialog[open] { display: grid; place-items: center; }
  .lib-video__dialog::backdrop { background: rgb(12 10 9 / 0.88); }
  .lib-video__dialog-frame {
    width: min(100%, 1280px, calc((100vh - 9rem) * (var(--lib-dialog-ratio))));
    aspect-ratio: var(--lib-dialog-ratio);
    border-radius: 12px;
    overflow: hidden;
    background: #000;
  }
  .lib-video__close {
    position: fixed;
    inset-block-start: 1rem;
    inset-inline-end: 1rem;
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgb(255 255 255 / 0.14);
    color: #fff;
    cursor: pointer;
  }
  .lib-video__close:hover { background: rgb(255 255 255 / 0.26); }
  .lib-video__close:focus-visible { outline-color: #fff; box-shadow: none; }

  @media (prefers-reduced-motion: reduce) {
    .lib-video__play { transition: none; }
    .lib-video__play:hover { transform: none; }
  }
{% endstylesheet %}

{% javascript %}
  if (!customElements.get('lib-video-player')) {
    customElements.define(
      'lib-video-player',
      class extends HTMLElement {
        connectedCallback() {
          if (this._ready) return;
          this.tpl = this.querySelector('template.lib-video__tpl');
          this.slotEl = this.querySelector('.lib-video__slot');
          if (!this.tpl || !this.slotEl) return;
          this._ready = true;
          this.mode = this.dataset.mode;
          this.playBtn = this.querySelector('.lib-video__play');
          this.toggleBtn = this.querySelector('.lib-video__toggle');
          this.dialog = this.querySelector('dialog.lib-video__dialog');
          this.frame = this.dialog && this.dialog.querySelector('.lib-video__dialog-frame');
          this.closeBtn = this.dialog && this.dialog.querySelector('.lib-video__close');
          const reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

          this.onPlay = () => (this.mode === 'lightbox' ? this.open() : this.startInline());
          this.onToggle = () => this.toggle();
          this.onClose = () => this.closed();
          this.onCloseBtn = () => this.dialog.close();
          this.onBackdrop = (e) => {
            if (e.target === this.dialog) this.dialog.close();
          };
          if (this.playBtn) this.playBtn.addEventListener('click', this.onPlay);
          if (this.toggleBtn) this.toggleBtn.addEventListener('click', this.onToggle);
          if (this.dialog) {
            this.dialog.addEventListener('close', this.onClose);
            this.dialog.addEventListener('click', this.onBackdrop);
            if (this.closeBtn) this.closeBtn.addEventListener('click', this.onCloseBtn);
          }

          const auto = this.mode === 'background' || (this.mode === 'inline' && this.hasAttribute('data-autoplay'));
          if (auto && !reduced) {
            const start = () => (this.mode === 'background' ? this.startBackground() : this.startInline(false));
            if ('IntersectionObserver' in window) {
              this.io = new IntersectionObserver(
                (entries) => {
                  if (!entries.some((en) => en.isIntersecting)) return;
                  this.io.disconnect();
                  this.io = null;
                  start();
                },
                { rootMargin: '200px 0px' }
              );
              this.io.observe(this);
            } else {
              start();
            }
          }
        }

        disconnectedCallback() {
          if (!this._ready) return;
          this._ready = false;
          if (this.io) this.io.disconnect();
          if (this.playBtn) this.playBtn.removeEventListener('click', this.onPlay);
          if (this.toggleBtn) this.toggleBtn.removeEventListener('click', this.onToggle);
          if (this.dialog) {
            this.dialog.removeEventListener('close', this.onClose);
            this.dialog.removeEventListener('click', this.onBackdrop);
            if (this.closeBtn) this.closeBtn.removeEventListener('click', this.onCloseBtn);
            if (this.dialog.open) this.dialog.close();
          }
        }

        // Clones the player from the inert template into target; nothing loads before this.
        mount(target) {
          target.replaceChildren(this.tpl.content.cloneNode(true));
          const media = target.querySelector('video, iframe');
          if (media && media.tagName === 'VIDEO') {
            if (media.hasAttribute('muted')) media.muted = true;
            const p = media.play();
            if (p && p.catch) p.catch(() => {});
          }
          return media;
        }

        startInline(focus = true) {
          if (this.hasAttribute('data-playing')) return;
          const media = this.mount(this.slotEl);
          this.setAttribute('data-playing', '');
          // Without native controls, the video itself pauses and resumes on click, Enter or Space.
          if (media && media.tagName === 'VIDEO' && !media.hasAttribute('controls')) {
            media.setAttribute('tabindex', '0');
            media.setAttribute('aria-label', this.dataset.toggleLabel || 'Pause or play video');
            media.addEventListener('click', () => this.flip(media));
            media.addEventListener('keydown', (e) => {
              if (e.key !== 'Enter' && e.key !== ' ') return;
              e.preventDefault();
              this.flip(media);
            });
          }
          if (focus && media) media.focus({ preventScroll: true });
        }

        flip(video) {
          if (video.paused) {
            const p = video.play();
            if (p && p.catch) p.catch(() => {});
          } else {
            video.pause();
          }
        }

        startBackground() {
          this.media = this.mount(this.slotEl);
          this.setAttribute('data-playing', '');
          this.setState(true);
        }

        toggle() {
          if (!this.media) {
            this.startBackground();
            return;
          }
          const playing = this.toggleBtn.dataset.state === 'playing';
          if (this.media.tagName === 'VIDEO') {
            this.flip(this.media);
          } else {
            this.command(!playing);
          }
          this.setState(!playing);
        }

        // Pause / play an embedded background player through its postMessage API.
        command(play) {
          const win = this.media.contentWindow;
          if (!win) return;
          if (this.dataset.src === 'youtube') {
            win.postMessage(JSON.stringify({ event: 'command', func: play ? 'playVideo' : 'pauseVideo', args: [] }), '*');
          } else if (this.dataset.src === 'vimeo') {
            win.postMessage(JSON.stringify({ method: play ? 'play' : 'pause' }), '*');
          }
        }

        setState(playing) {
          if (!this.toggleBtn) return;
          this.toggleBtn.dataset.state = playing ? 'playing' : 'paused';
          this.toggleBtn.setAttribute('aria-label', this.toggleBtn.dataset[playing ? 'labelPause' : 'labelPlay']);
        }

        open() {
          if (!this.dialog || !this.frame || this.dialog.open) return;
          this.dialog.showModal();
          const media = this.mount(this.frame);
          if (media) {
            if (media.tagName === 'VIDEO') media.setAttribute('tabindex', '-1');
            media.focus({ preventScroll: true });
          }
        }

        // Esc, the close button and a backdrop click all end here: unload the player, restore focus.
        closed() {
          if (this.frame) this.frame.replaceChildren();
          if (this.playBtn) this.playBtn.focus({ preventScroll: true });
        }
      }
    );
  }
{% endjavascript %}

{% schema %}
{
  "name": "Lib · Video",
  "tag": "section",
  "class": "lib-section",
  "settings": [
    { "type": "header", "content": "Content" },
    { "type": "text", "id": "eyebrow", "label": "Eyebrow", "default": "From the studio" },
    { "type": "inline_richtext", "id": "heading", "label": "Heading", "default": "Made slowly, <em>by hand</em>" },
    {
      "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>Every bowl starts as a lump of local clay. Two minutes in the studio, from the wheel to the kiln.</p>" },
    {
      "type": "select",
      "id": "text_position",
      "label": "Text position",
      "info": "Inline players always show the text above, so it never covers the controls.",
      "options": [
        { "value": "above", "label": "Above the video" },
        { "value": "overlay", "label": "Over the video" }
      ],
      "default": "above"
    },

    { "type": "header", "content": "Video" },
    {
      "type": "select",
      "id": "mode",
      "label": "Display",
      "options": [
        { "value": "inline", "label": "Inline player" },
        { "value": "background", "label": "Background loop (muted)" },
        { "value": "lightbox", "label": "Poster that opens a lightbox" }
      ],
      "default": "lightbox"
    },
    { "type": "video", "id": "video", "label": "Shopify-hosted video", "info": "Used first when set." },
    { "type": "video_url", "id": "video_url", "label": "Or YouTube / Vimeo link", "accept": ["youtube", "vimeo"], "info": "YouTube plays from youtube-nocookie.com and Vimeo with do-not-track. Nothing loads until play." },
    { "type": "text", "id": "video_title", "label": "Video title", "info": "Read by screen readers for the play button, the player and the lightbox.", "default": "A bowl thrown on the wheel" },
    { "type": "image_picker", "id": "poster", "label": "Poster image on desktop", "info": "Shown until the video plays. Falls back to the Shopify video's own preview image." },
    { "type": "image_picker", "id": "poster_mobile", "label": "Poster image on mobile", "info": "Optional. Shown below 750px." },
    { "type": "text", "id": "play_label", "label": "Play button text", "info": "Leave empty for an icon-only button.", "default": "Watch the film", "visible_if": "{{ section.settings.mode != 'background' }}" },
    { "type": "checkbox", "id": "autoplay", "label": "Autoplay (muted)", "info": "Starts muted when it scrolls into view. Never autoplays for visitors who prefer reduced motion.", "default": false, "visible_if": "{{ section.settings.mode == 'inline' }}" },
    { "type": "checkbox", "id": "muted", "label": "Start muted", "default": false, "visible_if": "{{ section.settings.mode == 'inline' }}" },
    { "type": "checkbox", "id": "controls", "label": "Show player controls", "default": true, "visible_if": "{{ section.settings.mode == 'inline' }}" },
    { "type": "checkbox", "id": "loop", "label": "Loop", "info": "Background videos always loop.", "default": false, "visible_if": "{{ section.settings.mode != 'background' }}" },

    { "type": "header", "content": "Button" },
    { "type": "text", "id": "button_label", "label": "Button label", "default": "Shop ceramics" },
    { "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": "outline"
    },

    { "type": "header", "content": "Layout" },
    {
      "type": "select",
      "id": "ratio",
      "label": "Shape on desktop",
      "options": [
        { "value": "16-9", "label": "16:9 widescreen" },
        { "value": "21-9", "label": "21:9 cinema" },
        { "value": "4-3", "label": "4:3" },
        { "value": "1-1", "label": "1:1 square" },
        { "value": "9-16", "label": "9:16 vertical" }
      ],
      "default": "16-9"
    },
    {
      "type": "select",
      "id": "ratio_mobile",
      "label": "Shape on mobile",
      "options": [
        { "value": "16-9", "label": "16:9 widescreen" },
        { "value": "21-9", "label": "21:9 cinema" },
        { "value": "4-3", "label": "4:3" },
        { "value": "1-1", "label": "1:1 square" },
        { "value": "9-16", "label": "9:16 vertical" }
      ],
      "default": "4-3"
    },
    {
      "type": "select",
      "id": "section_width",
      "label": "Width",
      "options": [
        { "value": "page", "label": "Page width" },
        { "value": "full", "label": "Full width" }
      ],
      "default": "page"
    },
    { "type": "range", "id": "corner_radius", "label": "Corner radius", "info": "Page width only.", "min": 0, "max": 40, "step": 2, "unit": "px", "default": 16 },
    { "type": "text_alignment", "id": "text_alignment", "label": "Text alignment on desktop", "default": "center" },
    {
      "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": "color", "id": "overlay_color", "label": "Overlay color", "default": "#1C1917" },
    { "type": "range", "id": "overlay_opacity", "label": "Overlay opacity", "info": "Hidden once an inline video plays.", "min": 0, "max": 90, "step": 5, "unit": "%", "default": 15 },

    { "type": "header", "content": "Colors" },
    { "type": "color_background", "id": "color_bg", "label": "Background", "default": "#F5F1EA" },
    { "type": "color", "id": "color_text", "label": "Text", "default": "#1C1917" },
    { "type": "color", "id": "color_on_video", "label": "Text over video", "default": "#FFFFFF" },
    { "type": "color", "id": "color_accent", "label": "Accent (play button, solid button)", "default": "#1C1917" },
    { "type": "color", "id": "color_accent_text", "label": "Text on accent", "default": "#F5F1EA" },

    { "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": 80 },
    { "type": "range", "id": "padding_bottom", "label": "Bottom padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 80 },
    { "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": 48 },
    {
      "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 · Video"
    }
  ]
}
{% 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.
