Video carousel with product link

Video carousel with product link

{% doc %}
@prompt
I want a video carousel block with the followings:
- unlimited videos, preferably adding thru the section
- product thumbnail with product link, when click it goes to product page
- desktop column: 3,4,5
- mobile column: 1,2
- carousel, video control at the left and right of the video section
- with text and description below or on the video, I want a carousel that can support at least 10 videos, Looks good except the video size is not auto size.

{% enddoc %}
{% assign ai_gen_id = block.id | replace: '_', '' | downcase %}

{% style %}
.ai-video-carousel-{{ ai_gen_id }} {
position: relative;
padding: 40px 0;
}

.ai-video-carousel-container-{{ ai_gen_id }} {
position: relative;
overflow: hidden;
}

.ai-video-carousel-track-{{ ai_gen_id }} {
display: flex;
transition: transform 0.3s ease;
gap: 20px;
}

.ai-video-carousel-slide-{{ ai_gen_id }} {
flex: 0 0 auto;
width: calc((100% - {{ block.settings.desktop_columns | minus: 1 | times: 20 }}px) / {{ block.settings.desktop_columns }});
position: relative;
}

.ai-video-carousel-video-wrapper-{{ ai_gen_id }} {
position: relative;
width: 100%;
background-color: #000;
border-radius: 8px;
overflow: hidden;
}

.ai-video-carousel-video-{{ ai_gen_id }} {
width: 100%;
height: auto;
display: block;
}

.ai-video-carousel-overlay-{{ ai_gen_id }} {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.7) 100%);
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px;
color: white;
opacity: 0;
transition: opacity 0.3s ease;}

.ai-video-carousel-slide-{{ ai_gen_id }}:hover .ai-video-carousel-overlay-{{ ai_gen_id }} {
opacity: 1;
}

.ai-video-carousel-text-{{ ai_gen_id }} {
font-size: 18px;
font-weight: 600;
margin-bottom: 8px;
}

.ai-video-carousel-description-{{ ai_gen_id }} {
font-size: 14px;
line-height: 1.4;
opacity: 0.9;
}

.ai-video-carousel-content-below-{{ ai_gen_id }} {
padding: 16px 0;
}

.ai-video-carousel-content-below-{{ ai_gen_id }} .ai-video-carousel-text-{{ ai_gen_id }} {
color: {{ block.settings.text_color }};
font-size: 18px;
font-weight: 600;
margin-bottom: 8px;
}

.ai-video-carousel-content-below-{{ ai_gen_id }} .ai-video-carousel-description-{{ ai_gen_id }} {
color: {{ block.settings.text_color }};
font-size: 14px;
line-height: 1.4;
opacity: 0.8;
}

.ai-video-carousel-product-{{ ai_gen_id }} {
margin-top: 12px;
}

.ai-video-carousel-product-link-{{ ai_gen_id }} {
display: flex;
align-items: center;
gap: 12px;
text-decoration: none;
color: inherit;
padding: 12px;
border-radius: 8px;
background-color: {{ block.settings.product_bg_color }};
transition: background-color 0.3s ease;
}

.ai-video-carousel-product-link-{{ ai_gen_id }}:hover {
background-color: {{ block.settings.product_hover_bg_color }};
}

.ai-video-carousel-product-image-{{ ai_gen_id }} {
width: 60px;
height: 60px;
border-radius: 6px;
overflow: hidden;
flex-shrink: 0;
}

.ai-video-carousel-product-image-{{ ai_gen_id }} img {
width: 100%;
height: 100%;
object-fit: cover;
}

.ai-video-carousel-product-info-{{ ai_gen_id }} {
flex-grow: 1;
}

.ai-video-carousel-product-title-{{ ai_gen_id }} {
font-size: 14px;
font-weight: 600;
margin-bottom: 4px;
color: {{ block.settings.product_text_color }};
}

.ai-video-carousel-product-price-{{ ai_gen_id }} {
font-size: 14px;
color: {{ block.settings.product_text_color }};
opacity: 0.8;
}

.ai-video-carousel-nav-{{ ai_gen_id }} {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: {{ block.settings.nav_bg_color }};
border: none;
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 2;
transition: background-color 0.3s ease, opacity 0.3s ease;
color: {{ block.settings.nav_icon_color }};
}

.ai-video-carousel-nav-{{ ai_gen_id }}:hover {
background-color: {{ block.settings.nav_hover_bg_color }};
}

.ai-video-carousel-nav-{{ ai_gen_id }}:disabled {
opacity: 0.3;
cursor: not-allowed;
}

.ai-video-carousel-nav-prev-{{ ai_gen_id }} {
left: -25px;
}

.ai-video-carousel-nav-next-{{ ai_gen_id }} {
right: -25px;
}

.ai-video-carousel-placeholder-{{ ai_gen_id }} {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 300px;
background-color: #f4f4f4;
border-radius: 8px;
text-align: center;
padding: 40px;
}

.ai-video-carousel-placeholder-{{ ai_gen_id }} svg {
width: 80px;
height: 80px;
margin-bottom: 16px;
opacity: 0.5;
}

.ai-video-carousel-empty-text-{{ ai_gen_id }} {
font-size: 16px;
color: #666;
margin-bottom: 8px;
}

.ai-video-carousel-empty-subtext-{{ ai_gen_id }} {
font-size: 14px;
color: #999;
}

@media screen and (max-width: 749px) {
.ai-video-carousel-slide-{{ ai_gen_id }} {
width: calc((100% - {{ block.settings.mobile_columns | minus: 1 | times: 20 }}px) / {{ block.settings.mobile_columns }});
}

.ai-video-carousel-nav-{{ ai_gen_id }} {
width: 40px;
height: 40px;
}

.ai-video-carousel-nav-prev-{{ ai_gen_id }} {
left: -20px;
}

.ai-video-carousel-nav-next-{{ ai_gen_id }} {
right: -20px;
}

.ai-video-carousel-overlay-{{ ai_gen_id }} {
padding: 16px;
}

.ai-video-carousel-text-{{ ai_gen_id }} {
font-size: 16px;
}

.ai-video-carousel-description-{{ ai_gen_id }} {
font-size: 13px;
}
}
{% endstyle %}<video-carousel-{{ ai_gen_id }}
class="ai-video-carousel-{{ ai_gen_id }}"
{{ block.shopify_attributes }}
>
{% assign has_videos = false %}
{% for i in (1..15) %}
{% assign video_key = 'video_' | append: i %}
{% assign text_key = 'video_text_' | append: i %}
{% assign description_key = 'video_description_' | append: i %}
{% assign product_key = 'video_product_' | append: i %}
{% if block.settings[video_key] != blank %}
{% assign has_videos = true %}
{% break %}
{% endif %}
{% endfor %}

{% if has_videos %}
<div class="ai-video-carousel-container-{{ ai_gen_id }}">
<div class="ai-video-carousel-track-{{ ai_gen_id }}" data-track>
{% for i in (1..15) %}
{% assign video_key = 'video_' | append: i %}
{% assign text_key = 'video_text_' | append: i %}
{% assign description_key = 'video_description_' | append: i %}
{% assign product_key = 'video_product_' | append: i %}
{% assign video = block.settings[video_key] %}
{% assign video_text = block.settings[text_key] %}
{% assign video_description = block.settings[description_key] %}
{% assign video_product = block.settings[product_key] %}

{% if video != blank %}
<div class="ai-video-carousel-slide-{{ ai_gen_id }}">
<div class="ai-video-carousel-video-wrapper-{{ ai_gen_id }}">
{% if video.sources.size > 0 %}
<video
class="ai-video-carousel-video-{{ ai_gen_id }}"
muted
loop
preload="metadata"
{% if block.settings.autoplay %}autoplay{% endif %}
>
{% for source in video.sources %}<source src="{{ source.url }}" type="{{ source.mime_type }}">
{% endfor %}
</video>
{% endif %}

{% if block.settings.text_position == 'overlay' %}
<div class="ai-video-carousel-overlay-{{ ai_gen_id }}">
{% if video_text != blank %}
<div class="ai-video-carousel-text-{{ ai_gen_id }}">{{ video_text }}</div>
{% endif %}
{% if video_description != blank %}
<div class="ai-video-carousel-description-{{ ai_gen_id }}">{{ video_description }}</div>
{% endif %}
</div>
{% endif %}
</div>

{% if block.settings.text_position == 'below' %}
<div class="ai-video-carousel-content-below-{{ ai_gen_id }}">
{% if video_text != blank %}
<div class="ai-video-carousel-text-{{ ai_gen_id }}">{{ video_text }}</div>
{% endif %}
{% if video_description != blank %}
<div class="ai-video-carousel-description-{{ ai_gen_id }}">{{ video_description }}</div>
{% endif %}
</div>
{% endif %}

{% if video_product != blank %}
<div class="ai-video-carousel-product-{{ ai_gen_id }}">
<a href="{{ video_product.url }}" class="ai-video-carousel-product-link-{{ ai_gen_id }}">
<div class="ai-video-carousel-product-image-{{ ai_gen_id }}">
{% if video_product.featured_image %}
<img
src="{{ video_product.featured_image | image_url: width: 120}}"
alt="{{ video_product.featured_image.alt | escape }}"
loading="lazy"
width="120"
height="120"
>
{% else %}
<div style="background-color: #f4f4f4; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
{{'product-1' | placeholder_svg_tag }}
</div>
{% endif %}
</div>
<div class="ai-video-carousel-product-info-{{ ai_gen_id }}">
<div class="ai-video-carousel-product-title-{{ ai_gen_id }}">{{ video_product.title }}</div>
<div class="ai-video-carousel-product-price-{{ ai_gen_id }}">{{ video_product.price | money }}</div>
</div>
</a>
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>

<button class="ai-video-carousel-nav-{{ ai_gen_id }} ai-video-carousel-nav-prev-{{ ai_gen_id }}" data-prev aria-label="Previous video">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="15,18 9,12 15,6"></polyline>
</svg>
</button>

<button class="ai-video-carousel-nav-{{ ai_gen_id }} ai-video-carousel-nav-next-{{ ai_gen_id }}" data-next aria-label="Next video">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="9,18 15,12 9,6"></polyline>
</svg>
</button>
</div>
{% else %}
<div class="ai-video-carousel-placeholder-{{ ai_gen_id }}">
{{ 'image' | placeholder_svg_tag }}<div class="ai-video-carousel-empty-text-{{ ai_gen_id }}">Add videos to get started</div>
<div class="ai-video-carousel-empty-subtext-{{ ai_gen_id }}">Upload videos in the block settings to create your carousel</div>
</div>
{% endif %}
</video-carousel-{{ ai_gen_id }}>

<script>
(function() {
class VideoCarousel{{ ai_gen_id }} extends HTMLElement {
constructor() {
super();
this.currentIndex = 0;
this.slidesPerView = {{ block.settings.desktop_columns }};
this.mobileSlides = {{ block.settings.mobile_columns }};
}

connectedCallback() {
this.track = this.querySelector('[data-track]');
this.prevBtn = this.querySelector('[data-prev]');
this.nextBtn = this.querySelector('[data-next]');
this.slides = this.querySelectorAll('.ai-video-carousel-slide-{{ ai_gen_id }}');

if (!this.track || this.slides.length === 0) return;

this.setupEventListeners();
this.updateSlidesPerView();
this.updateNavigation();

window.addEventListener('resize', () => {
this.updateSlidesPerView();
this.updateSlidePosition();
this.updateNavigation();
});
}

setupEventListeners() {
this.prevBtn?.addEventListener('click', () => this.goToPrev());
this.nextBtn?.addEventListener('click', () => this.goToNext());

this.slides.forEach((slide) => {
const video = slide.querySelector('video');
if (video) {
slide.addEventListener('mouseenter', () => {
video.play().catch(() => {});
});
slide.addEventListener('mouseleave', () => {
video.pause();
});
}
});
}

updateSlidesPerView() {
this.slidesPerView = window.innerWidth <= 749 ? this.mobileSlides : {{ block.settings.desktop_columns }};
}

goToPrev() {
if (this.currentIndex > 0) {
this.currentIndex--;
this.updateSlidePosition();
this.updateNavigation();
}
}

goToNext() {
const maxIndex = Math.max(0, this.slides.length - this.slidesPerView);
if (this.currentIndex < maxIndex) {
this.currentIndex++;
this.updateSlidePosition();
this.updateNavigation();
}
}

updateSlidePosition() {
const slideWidth = this.slides[0]?.offsetWidth || 0;
const gap = 20;
const translateX = -(this.currentIndex * (slideWidth + gap));
this.track.style.transform = `translateX(${translateX}px)`;
}

updateNavigation() {
const maxIndex = Math.max(0, this.slides.length - this.slidesPerView);
if (this.prevBtn) {
this.prevBtn.disabled = this.currentIndex === 0;
}
if (this.nextBtn) {
this.nextBtn.disabled = this.currentIndex >= maxIndex;
}
}
}

customElements.define('video-carousel-{{ ai_gen_id }}', VideoCarousel{{ ai_gen_id }});
})();
</script>

{% schema %}
{
"name": "Video carousel",
"settings": [
{
"type": "header",
"content": "Layout"
},
{
"type": "select",
"id": "desktop_columns",
"label": "Desktop columns",
"options": [
{"value": "3", "label": "3"},
{"value": "4", "label": "4"},
{"value": "5", "label": "5"}
],
"default": "3"
},
{
"type": "select",
"id": "mobile_columns",
"label": "Mobile columns",
"options": [
{"value": "1", "label": "1"},
{"value": "2", "label": "2"}
],
"default": "1"
},
{
"type": "checkbox",
"id": "autoplay",
"label": "Autoplay videos",
"default": false
},
{
"type": "select",
"id": "text_position",
"label": "Text position",
"options": [
{"value": "overlay", "label": "Overlay on video"},
{"value": "below", "label": "Below video"}
],
"default": "below"
},
{
"type": "header",
"content": "Video 1"
},
{
"type": "video",
"id": "video_1",
"label": "Video"
},
{
"type": "text",
"id": "video_text_1",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_1",
"label": "Description"
},
{
"type": "product",
"id": "video_product_1",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 2"
},
{
"type": "video",
"id": "video_2",
"label": "Video"
},
{
"type": "text",
"id": "video_text_2",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_2",
"label": "Description"
},
{
"type": "product",
"id": "video_product_2",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 3"
},
{
"type": "video",
"id": "video_3",
"label": "Video"
},
{
"type": "text",
"id": "video_text_3",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_3",
"label": "Description"
},
{
"type": "product",
"id": "video_product_3",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 4"
},
{
"type": "video",
"id": "video_4",
"label": "Video"
},
{
"type": "text",
"id": "video_text_4",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_4",
"label": "Description"
},
{
"type": "product",
"id": "video_product_4",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 5"
},
{
"type": "video",
"id": "video_5",
"label": "Video"
},
{
"type": "text",
"id": "video_text_5",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_5",
"label": "Description"
},
{
"type": "product",
"id": "video_product_5",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 6"
},
{
"type": "video",
"id": "video_6",
"label": "Video"
},
{
"type": "text",
"id": "video_text_6",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_6",
"label": "Description"
},
{
"type": "product",
"id": "video_product_6",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 7"
},
{
"type": "video",
"id": "video_7",
"label": "Video"
},
{
"type": "text",
"id": "video_text_7",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_7",
"label": "Description"
},
{
"type": "product",
"id": "video_product_7",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 8"
},
{
"type": "video",
"id": "video_8",
"label": "Video"
},
{
"type": "text",
"id": "video_text_8",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_8",
"label": "Description"
},
{
"type": "product",
"id": "video_product_8",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 9"
},
{
"type": "video",
"id": "video_9",
"label": "Video"
},
{
"type": "text",
"id": "video_text_9",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_9",
"label": "Description"
},
{
"type": "product",
"id": "video_product_9",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 10"
},
{
"type": "video",
"id": "video_10",
"label": "Video"
},
{
"type": "text",
"id": "video_text_10",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_10",
"label": "Description"
},
{
"type": "product",
"id": "video_product_10",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 11"
},
{
"type": "video",
"id": "video_11",
"label": "Video"
},
{
"type": "text",
"id": "video_text_11",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_11",
"label": "Description"
},
{
"type": "product",
"id": "video_product_11",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 12"
},
{
"type": "video",
"id": "video_12",
"label": "Video"
},
{
"type": "text",
"id": "video_text_12",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_12",
"label": "Description"
},
{
"type": "product",
"id": "video_product_12",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 13"
},
{
"type": "video",
"id": "video_13",
"label": "Video"
},
{
"type": "text",
"id": "video_text_13",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_13",
"label": "Description"
},
{
"type": "product",
"id": "video_product_13",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 14"
},
{
"type": "video",
"id": "video_14",
"label": "Video"
},
{
"type": "text",
"id": "video_text_14",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_14",
"label": "Description"
},
{
"type": "product",
"id": "video_product_14",
"label": "Featured product"
},
{
"type": "header",
"content": "Video 15"
},
{
"type": "video",
"id": "video_15",
"label": "Video"
},
{
"type": "text",
"id": "video_text_15",
"label": "Title"
},
{
"type": "textarea",
"id": "video_description_15",
"label": "Description"
},
{
"type": "product",
"id": "video_product_15",
"label": "Featured product"
},
{
"type": "header",
"content": "Colors"
},
{
"type": "color",
"id": "text_color",
"label": "Text color",
"default": "#000000"
},
{
"type": "color",
"id": "nav_bg_color",
"label": "Navigation background",
"default": "#ffffff"
},
{
"type": "color",
"id": "nav_hover_bg_color",
"label": "Navigation hover background",
"default": "#f0f0f0"
},
{
"type": "color",
"id": "nav_icon_color",
"label": "Navigation icon color",
"default": "#000000"
},
{
"type": "color",
"id": "product_bg_color",
"label": "Product background",
"default": "#f8f8f8"
},
{
"type": "color",
"id": "product_hover_bg_color",
"label": "Product hover background",
"default": "#f0f0f0"
},
{
"type": "color",
"id": "product_text_color",
"label": "Product text color",
"default": "#000000"
}
],
"presets": [
{
"name": "Video carousel"
}
],
"tag": null
}
{% endschema %}
    • Related Articles

    • Create a Video Background section

      Create the section snippet, create a new snippet named: video-background.liquid and copy the following liquid into it. {%- if section.blocks.size > 0 -%} {%- for block in section.blocks -%} {%- assign img_url = block.settings.image | img_url: '1x1' | ...
    • Create a Video with Text Section

      Create a new section with this code, called "video-with-text.liquid" {{ 'component-video-with-text.css' | asset_url | stylesheet_tag }} <div class="video-with-text {% if section.settings.full_width %}video-with-text--full-width{% else %}page-width{% ...
    • Hide menu link in mega menu when product counts = 0

      In header-mega-menu.liquid, search {%- for childlink in link.links -%} Add the following below: {%- if childlink.object and childlink.object.products_count == 0 -%} {%- continue -%} {%- endif -%} Then, search {%- for grandchildlink in childlink.links ...
    • Make parent link clickable

      Snippets > header-mega-menu.liquid (l.19-25) Parentlink {%- assign parentlink = link.links[0] -%} <a href="{{ parentlink.url }}" title="{{ parentlink.title }}"> <span {%- if link.child_active %} class="header__active-menu-item" {% endif %} > {{- ...
    • Reduce menu link padding in the footer

      In the footer section, add the following in the custom css: .list-menu__item--link { padding: 0px; }