Untitled
unknown
plain_text
23 days ago
24 kB
8
Indexable
{% comment %}
featured-color-variants.liquid — v4
Updates:
1. Replaced static image with a native-feeling Gallery Slider (swipeable + snap points).
2. Added left/right navigation arrows on hover.
3. Quick Add button updated with Shopping Bag + icon and strict theme font variables.
4. JS updated: Clicking thumbnails now smoothly scrolls the gallery to the correct slide.
{% endcomment %}
<style>
.fcv-section {
width: 100%;
background-color: {{ section.settings.background_color }};
padding-top: {{ section.settings.padding_top }}px;
padding-bottom: {{ section.settings.padding_bottom }}px;
box-sizing: border-box;
}
.fcv-section .fcv-inner--full {
width: 100%;
padding-inline: 1.5rem;
box-sizing: border-box;
}
.fcv-section .fcv-header {
text-align: center;
margin-bottom: 40px;
}
.fcv-section .fcv-heading {
font-family: var(--font-heading-family, inherit);
font-size: clamp(1.5rem, 3vw, 2.25rem);
font-weight: 500;
letter-spacing: 0.04em;
margin: 0 0 12px;
color: inherit;
}
.fcv-section .fcv-subheading {
font-family: var(--font-body-family, inherit);
font-size: 1rem;
font-weight: 400;
color: rgba(var(--color-foreground), 0.7);
margin: 0;
}
.fcv-section .fcv-grid {
display: grid;
grid-template-columns: repeat(var(--fcv-cols, 3), 1fr);
gap: 24px;
}
@media (max-width: 768px) {
.fcv-section .fcv-grid {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
}
@media (max-width: 480px) {
.fcv-section .fcv-grid {
grid-template-columns: 1fr;
gap: 16px;
}
}
.fcv-section .fcv-card {
display: flex;
flex-direction: column;
gap: 12px;
position: relative;
}
/* --- GALLERY SLIDER CSS --- */
.fcv-section .fcv-hero-wrap {
position: relative;
overflow: hidden;
background: #f5f5f5;
}
.fcv-section .fcv-gallery-track {
display: flex;
flex-direction: row;
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: none;
-ms-overflow-style: none;
scroll-behavior: smooth;
width: 100%;
}
.fcv-section .fcv-gallery-track::-webkit-scrollbar {
display: none;
}
.fcv-section .fcv-gallery-slide {
flex: 0 0 100%;
scroll-snap-align: start;
position: relative;
}
.fcv-section .fcv-hero {
width: 100%;
aspect-ratio: 3 / 4;
object-fit: cover;
display: block;
}
/* Slider Navigation Arrows */
.fcv-gallery-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.9);
border: none;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s ease, transform 0.2s ease;
z-index: 5;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
color: #000;
}
.fcv-gallery-arrow:hover {
transform: translateY(-50%) scale(1.05);
}
.fcv-gallery-arrow svg {
width: 16px;
height: 16px;
stroke-width: 2;
}
.fcv-gallery-arrow--prev { left: 8px; }
.fcv-gallery-arrow--next { right: 8px; }
@media (hover: hover) {
.fcv-section .fcv-hero-wrap:hover .fcv-gallery-arrow {
opacity: 1;
}
}
/* --- QUICK ADD FLOATING BUTTON CSS --- */
.fcv-quick-add {
position: absolute;
bottom: 12px;
right: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
background-color: #333333; /* Matched to dark grey theme style */
color: #ffffff;
border: none;
border-radius: 40px;
height: 40px;
min-width: 40px;
padding: 0;
text-decoration: none;
overflow: hidden;
cursor: pointer;
z-index: 10;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
.fcv-quick-add svg {
width: 18px;
height: 18px;
fill: none;
stroke: currentColor;
stroke-width: 1.5;
stroke-linecap: round;
stroke-linejoin: round;
flex-shrink: 0;
margin: 0 11px;
transition: margin 0.3s ease;
}
.fcv-quick-add span {
font-family: var(--font-body-family, sans-serif);
font-size: 10px;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
white-space: nowrap;
opacity: 0;
max-width: 0;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.fcv-section .fcv-hero-wrap:hover .fcv-quick-add,
.fcv-quick-add:hover {
background-color: #000000;
padding: 0 16px 0 0;
}
.fcv-section .fcv-hero-wrap:hover .fcv-quick-add svg,
.fcv-quick-add:hover svg {
margin: 0 8px 0 14px;
}
.fcv-section .fcv-hero-wrap:hover .fcv-quick-add span,
.fcv-quick-add:hover span {
opacity: 1;
max-width: 150px;
}
/* --- THUMBNAILS & DETAILS --- */
.fcv-section .fcv-thumbs {
display: flex;
flex-direction: row;
gap: 8px;
overflow-x: auto;
padding-bottom: 4px;
scrollbar-width: none;
-ms-overflow-style: none;
}
.fcv-section .fcv-thumbs::-webkit-scrollbar {
display: none;
}
.fcv-section .fcv-thumb-wrap {
position: relative;
flex-shrink: 0;
}
.fcv-section .fcv-thumb {
display: block;
border: 2px solid transparent;
cursor: pointer;
padding: 0;
background: none;
border-radius: 2px;
overflow: hidden;
transition: border-color 0.2s ease, transform 0.2s ease;
outline: none;
}
.fcv-section .fcv-thumb:hover {
border-color: #ccc;
transform: scale(1.05);
}
.fcv-section .fcv-thumb.fcv-thumb--active {
border-color: #000;
}
{% assign swatch_px = '56px' %}
{% if section.settings.swatch_size == 'small' %}
{% assign swatch_px = '40px' %}
{% elsif section.settings.swatch_size == 'large' %}
{% assign swatch_px = '72px' %}
{% endif %}
.fcv-section .fcv-thumb img {
width: {{ swatch_px }};
height: {{ swatch_px }};
display: block;
object-fit: cover;
}
.fcv-section .fcv-color-label {
position: absolute;
bottom: calc(100% + 6px);
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.75);
color: #fff;
font-size: 11px;
font-family: var(--font-body-family, inherit);
white-space: nowrap;
padding: 3px 7px;
border-radius: 3px;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
}
{% if section.settings.show_color_label_on_hover %}
.fcv-section .fcv-thumb-wrap:hover .fcv-color-label,
.fcv-section .fcv-thumb:focus-visible ~ .fcv-color-label {
opacity: 1;
}
{% endif %}
.fcv-section .fcv-card-info {
display: flex;
flex-direction: column;
gap: 6px;
text-align: center;
}
.fcv-section .fcv-card-title {
font-family: var(--font-body-family, inherit);
font-size: 0.95rem;
font-weight: 500;
margin: 0;
color: inherit;
letter-spacing: 0.02em;
}
.fcv-section .fcv-card-price {
font-family: var(--font-body-family, inherit);
font-size: 0.9rem;
font-weight: 400;
color: rgba(var(--color-foreground), 0.75);
margin: 0;
}
.fcv-section .fcv-card-btn {
display: inline-block;
margin-top: 4px;
padding: 10px 20px;
background: #000;
color: #fff;
font-family: var(--font-body-family, inherit);
font-size: 0.85rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
text-decoration: none;
border: none;
cursor: pointer;
transition: background 0.2s ease;
align-self: center;
}
.fcv-section .fcv-card-btn:hover {
background: #333;
}
</style>
<section class="fcv-section" id="fcv-{{ section.id }}" style="--fcv-cols: {{ section.settings.columns_desktop }};">
<div class="{% if section.settings.section_width == 'full-width' %}fcv-inner--full{% else %}page-width{% endif %}">
{% if section.settings.heading != blank or section.settings.subheading != blank %}
<div class="fcv-header">
{% if section.settings.heading != blank %}
<h2 class="fcv-heading">{{ section.settings.heading | escape }}</h2>
{% endif %}
{% if section.settings.subheading != blank %}
<p class="fcv-subheading">{{ section.settings.subheading | escape }}</p>
{% endif %}
</div>
{% endif %}
<div class="fcv-grid">
{% for block in section.blocks %}
{% assign blk = block.settings %}
<div class="fcv-card" data-card="{{ section.id }}-{{ block.id }}" {{ block.shopify_attributes }}>
<div class="fcv-hero-wrap">
<button type="button" class="fcv-gallery-arrow fcv-gallery-arrow--prev" aria-label="Previous slide">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M15 18l-6-6 6-6" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<button type="button" class="fcv-gallery-arrow fcv-gallery-arrow--next" aria-label="Next slide">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M9 18l6-6-6-6" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<div class="fcv-gallery-track">
{% comment %}
Render all available images as slides in the gallery track
{% endcomment %}
{% if blk.hero_image != blank %}
<div class="fcv-gallery-slide">
<a href="{{ blk.product.url | default: '#' }}" tabindex="-1" style="display: block;">
<img class="fcv-hero" src="{{ blk.hero_image | image_url: width: 800 }}" alt="{{ blk.hero_image.alt | default: blk.product.title | escape }}" loading="lazy" width="800" height="1067">
</a>
</div>
{% elsif blk.product != blank and blk.product.featured_image != blank %}
<div class="fcv-gallery-slide">
<a href="{{ blk.product.url | default: '#' }}" tabindex="-1" style="display: block;">
<img class="fcv-hero" src="{{ blk.product.featured_image | image_url: width: 800 }}" alt="{{ blk.product.featured_image.alt | default: blk.product.title | escape }}" loading="lazy" width="800" height="1067">
</a>
</div>
{% endif %}
{% for i in (1..6) %}
{% if i == 1 %}{% assign c_img = blk.color_1_image %}{% assign c_name = blk.color_1_name %}
{% elsif i == 2 %}{% assign c_img = blk.color_2_image %}{% assign c_name = blk.color_2_name %}
{% elsif i == 3 %}{% assign c_img = blk.color_3_image %}{% assign c_name = blk.color_3_name %}
{% elsif i == 4 %}{% assign c_img = blk.color_4_image %}{% assign c_name = blk.color_4_name %}
{% elsif i == 5 %}{% assign c_img = blk.color_5_image %}{% assign c_name = blk.color_5_name %}
{% elsif i == 6 %}{% assign c_img = blk.color_6_image %}{% assign c_name = blk.color_6_name %}
{% endif %}
{% if c_img != blank %}
<div class="fcv-gallery-slide" data-color-slide="{{ i }}">
<a href="{{ blk.product.url | default: '#' }}" tabindex="-1" style="display: block;">
<img class="fcv-hero" src="{{ c_img | image_url: width: 800 }}" alt="{{ c_name | default: blk.product.title | escape }}" loading="lazy" width="800" height="1067">
</a>
</div>
{% endif %}
{% endfor %}
</div>
{% comment %}
QUICK ADD BUTTON (Shopping Bag + Plus SVG)
*Ensure you add your theme's modal trigger class here (e.g., js-quick-view, quick-add-btn)*
{% endcomment %}
{% if section.settings.show_quick_add_overlay and blk.product != blank %}
<button class="fcv-quick-add js-quick-add" data-product-url="{{ blk.product.url }}" aria-label="Choose Options">
<svg viewBox="0 0 24 24">
<path d="M8 8V6a4 4 0 018 0v2M5 8h14l1 12H4L5 8z" />
<path d="M12 12v6M9 15h6" />
</svg>
<span>CHOOSE OPTIONS</span>
</button>
{% endif %}
</div>
{% comment %} Render Thumbnails (Clicking them scrolls the gallery) {% endcomment %}
{% assign has_colors = false %}
{% if blk.color_1_image != blank or blk.color_2_image != blank or blk.color_3_image != blank or blk.color_4_image != blank or blk.color_5_image != blank or blk.color_6_image != blank %}
{% assign has_colors = true %}
{% endif %}
{% if has_colors %}
<div class="fcv-thumbs" role="listbox" aria-label="Select colour">
{% assign first_color_rendered = false %}
{% for i in (1..6) %}
{% if i == 1 %}{% assign c_img = blk.color_1_image %}{% assign c_thumb = blk.color_1_thumb %}{% assign c_name = blk.color_1_name %}
{% elsif i == 2 %}{% assign c_img = blk.color_2_image %}{% assign c_thumb = blk.color_2_thumb %}{% assign c_name = blk.color_2_name %}
{% elsif i == 3 %}{% assign c_img = blk.color_3_image %}{% assign c_thumb = blk.color_3_thumb %}{% assign c_name = blk.color_3_name %}
{% elsif i == 4 %}{% assign c_img = blk.color_4_image %}{% assign c_thumb = blk.color_4_thumb %}{% assign c_name = blk.color_4_name %}
{% elsif i == 5 %}{% assign c_img = blk.color_5_image %}{% assign c_thumb = blk.color_5_thumb %}{% assign c_name = blk.color_5_name %}
{% elsif i == 6 %}{% assign c_img = blk.color_6_image %}{% assign c_thumb = blk.color_6_thumb %}{% assign c_name = blk.color_6_name %}
{% endif %}
{% if c_img != blank %}
{% assign thumb_url = c_thumb | default: c_img | image_url: width: 144 %}
{% assign is_first = false %}
{% if first_color_rendered == false %}{% assign is_first = true %}{% assign first_color_rendered = true %}{% endif %}
<div class="fcv-thumb-wrap" role="option">
<button class="fcv-thumb{% if is_first %} fcv-thumb--active{% endif %}" data-target-slide="{{ i }}" aria-selected="{{ is_first }}" aria-label="{{ c_name | default: 'Colour' | escape }}" type="button">
<img src="{{ thumb_url }}" alt="{{ c_name | default: '' | escape }}" loading="lazy" width="144" height="144">
</button>
{% if c_name != blank %}
<span class="fcv-color-label" aria-hidden="true">{{ c_name | escape }}</span>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% comment %} Details {% endcomment %}
{% if blk.show_title or blk.show_price or blk.show_button %}
<div class="fcv-card-info">
{% if blk.show_title and blk.product != blank %}
<p class="fcv-card-title">{{ blk.product.title }}</p>
{% endif %}
{% if blk.show_price and blk.product != blank %}
<p class="fcv-card-price">
{{ blk.product.price_min | money }}
{% if blk.product.price_varies %}–{{ blk.product.price_max | money }}{% endif %}
</p>
{% endif %}
{% if blk.show_button and blk.product != blank %}
{% assign btn_label = blk.button_label | default: 'Shop Now' %}
<a href="{{ blk.product.url }}" class="fcv-card-btn" aria-label="{{ btn_label | escape }}: {{ blk.product.title | escape }}">
{{ btn_label | escape }}
</a>
{% endif %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</section>
<script>
(function () {
'use strict';
function initFCV(sectionEl) {
const cards = sectionEl.querySelectorAll('[data-card]');
cards.forEach(card => {
const track = card.querySelector('.fcv-gallery-track');
const prevBtn = card.querySelector('.fcv-gallery-arrow--prev');
const nextBtn = card.querySelector('.fcv-gallery-arrow--next');
const thumbs = card.querySelectorAll('.fcv-thumb');
const slides = card.querySelectorAll('.fcv-gallery-slide');
if (!track || slides.length === 0) return;
// Gallery Left/Right Navigation
if (prevBtn && nextBtn) {
prevBtn.addEventListener('click', () => {
track.scrollBy({ left: -track.clientWidth, behavior: 'smooth' });
});
nextBtn.addEventListener('click', () => {
track.scrollBy({ left: track.clientWidth, behavior: 'smooth' });
});
}
// Thumbnail Click -> Scroll to specific slide
thumbs.forEach(thumb => {
thumb.addEventListener('click', () => {
const targetIndex = thumb.getAttribute('data-target-slide');
const targetSlide = card.querySelector(`.fcv-gallery-slide[data-color-slide="${targetIndex}"]`);
if (targetSlide) {
// Scroll to the slide
const scrollPos = targetSlide.offsetLeft - track.offsetLeft;
track.scrollTo({ left: scrollPos, behavior: 'smooth' });
// Update active state
thumbs.forEach(t => {
t.classList.remove('fcv-thumb--active');
t.setAttribute('aria-selected', 'false');
});
thumb.classList.add('fcv-thumb--active');
thumb.setAttribute('aria-selected', 'true');
}
});
});
// Update active thumbnail on manual swipe
track.addEventListener('scroll', () => {
const scrollPosition = track.scrollLeft;
const slideWidth = track.clientWidth;
const activeIndex = Math.round(scrollPosition / slideWidth);
if (slides[activeIndex]) {
const activeDataSlide = slides[activeIndex].getAttribute('data-color-slide');
if (activeDataSlide) {
thumbs.forEach(t => t.classList.remove('fcv-thumb--active'));
const activeThumb = card.querySelector(`.fcv-thumb[data-target-slide="${activeDataSlide}"]`);
if (activeThumb) activeThumb.classList.add('fcv-thumb--active');
}
}
}, { passive: true });
});
}
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.fcv-section').forEach(initFCV);
});
document.addEventListener('shopify:section:load', function (e) {
const s = e.target.querySelector('.fcv-section');
if (s) initFCV(s);
});
})();
</script>
{% schema %}
{
"name": "Featured Color Variants",
"tag": "div",
"class": "fcv-section-wrapper",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Heading",
"default": "Featured Collection"
},
{
"type": "text",
"id": "subheading",
"label": "Subheading"
},
{
"type": "select",
"id": "columns_desktop",
"label": "Columns (desktop)",
"options": [
{ "value": "2", "label": "2" },
{ "value": "3", "label": "3" },
{ "value": "4", "label": "4" }
],
"default": "4"
},
{
"type": "select",
"id": "section_width",
"label": "Section width",
"options": [
{ "value": "page-width", "label": "Page width" },
{ "value": "full-width", "label": "Full width" }
],
"default": "page-width"
},
{
"type": "checkbox",
"id": "show_quick_add_overlay",
"label": "Show quick add hovering icon over image",
"default": true
},
{
"type": "select",
"id": "swatch_size",
"label": "Thumbnail size",
"options": [
{ "value": "small", "label": "Small (40px)" },
{ "value": "medium", "label": "Medium (56px)" },
{ "value": "large", "label": "Large (72px)" }
],
"default": "medium"
},
{
"type": "checkbox",
"id": "show_color_label_on_hover",
"label": "Show colour name on hover",
"default": true
},
{
"type": "range",
"id": "padding_top",
"label": "Padding top",
"min": 0,
"max": 100,
"step": 4,
"unit": "px",
"default": 36
},
{
"type": "range",
"id": "padding_bottom",
"label": "Padding bottom",
"min": 0,
"max": 100,
"step": 4,
"unit": "px",
"default": 36
},
{
"type": "color",
"id": "background_color",
"label": "Background colour",
"default": "#ffffff"
}
],
"blocks": [
{
"type": "product_card",
"name": "Product Card",
"settings": [
{
"type": "product",
"id": "product",
"label": "Product"
},
{
"type": "image_picker",
"id": "hero_image",
"label": "Hero image (overrides product image)"
},
{
"type": "header",
"content": "Card Details Options"
},
{
"type": "checkbox",
"id": "show_title",
"label": "Show product title",
"default": false
},
{
"type": "checkbox",
"id": "show_price",
"label": "Show price",
"default": false
},
{
"type": "checkbox",
"id": "show_button",
"label": "Show standard text button",
"default": false
},
{
"type": "text",
"id": "button_label",
"label": "Button label",
"default": "Shop Now"
},
{ "type": "header", "content": "Colour 1" },
{ "type": "text", "id": "color_1_name", "label": "Colour 1 name" },
{ "type": "image_picker", "id": "color_1_image", "label": "Colour 1 full image" },
{ "type": "image_picker", "id": "color_1_thumb", "label": "Colour 1 thumbnail (opt.)" },
{ "type": "header", "content": "Colour 2" },
{ "type": "text", "id": "color_2_name", "label": "Colour 2 name" },
{ "type": "image_picker", "id": "color_2_image", "label": "Colour 2 full image" },
{ "type": "image_picker", "id": "color_2_thumb", "label": "Colour 2 thumbnail (opt.)" },
{ "type": "header", "content": "Colour 3" },
{ "type": "text", "id": "color_3_name", "label": "Colour 3 name" },
{ "type": "image_picker", "id": "color_3_image", "label": "Colour 3 full image" },
{ "type": "image_picker", "id": "color_3_thumb", "label": "Colour 3 thumbnail (opt.)" },
{ "type": "header", "content": "Colour 4" },
{ "type": "text", "id": "color_4_name", "label": "Colour 4 name" },
{ "type": "image_picker", "id": "color_4_image", "label": "Colour 4 full image" },
{ "type": "image_picker", "id": "color_4_thumb", "label": "Colour 4 thumbnail (opt.)" },
{ "type": "header", "content": "Colour 5" },
{ "type": "text", "id": "color_5_name", "label": "Colour 5 name" },
{ "type": "image_picker", "id": "color_5_image", "label": "Colour 5 full image" },
{ "type": "image_picker", "id": "color_5_thumb", "label": "Colour 5 thumbnail (opt.)" },
{ "type": "header", "content": "Colour 6" },
{ "type": "text", "id": "color_6_name", "label": "Colour 6 name" },
{ "type": "image_picker", "id": "color_6_image", "label": "Colour 6 full image" },
{ "type": "image_picker", "id": "color_6_thumb", "label": "Colour 6 thumbnail (opt.)" }
]
}
],
"max_blocks": 6,
"presets": [
{
"name": "Featured Color Variants",
"blocks": [
{ "type": "product_card" },
{ "type": "product_card" },
{ "type": "product_card" }
]
}
]
}
{% endschema %}Editor is loading...
Leave a Comment