Blog Page to Carousel

To change Blog Page to Carousel format, we will use List Carousel, then sync it with Blog Page, similar WM List Carousel Plugin.

#1. First, find Blog Page URL

In my example, it is: /blog04-1

#2. Add a List Section Carousel

Enable these options.

#3. Next, click Content > Title

Enter this format, replace /blog04-1 with your blog page

#blogurl=/blog04-1#

#4. Use this code to Custom CSS

li.list-item .blog-date-badge {
    position: absolute;
    top: 0px;
    left: 0px;
    background-color: #fff;
    color: #000;
    font-size: 18px;
    width: 60px;
    height: 60px;
}
li.list-item .date-badge-content {
    line-height: 20px;
    display: flex;
    align-items: center;
    flex-direction: column;
    justify-content: center;
    text-align: center;
    height: 100%;
}
section:has(.blog-date-badge) .list-section-title {
  display: none;
}

#5. Use this code to Code Injection > Footer

<script>
class BlogCarouselSync{constructor(options){this.config=options;this.carouselSection=this.config.target;this.carouselSection.dataset.blogSync="loading";this.blogEndpoint=this.extractBlogUrl()||options.blogUrl||"/blog";this.blogData=[];this.carouselItems=this.carouselSection.querySelectorAll("li.user-items-list-carousel__slide");this.carouselContainer=this.carouselSection.querySelector(".user-items-list-carousel__slides");this.initialize()}
extractBlogUrl(){const parentSection=this.carouselSection.closest('.page-section');const titleElement=parentSection?parentSection.querySelector('.list-section-title'):null;if(titleElement){const titleText=titleElement.textContent;const urlMatch=titleText.match(/#blogurl=(.*?)#/);if(urlMatch){console.log("Extracted blog URL:",urlMatch[1]);return urlMatch[1]}}
console.log("No blog URL found in title, using default");return null}
async initialize(){try{this.handleCarouselController();this.blogData=await this.fetchBlogData();console.log("Fetched blog posts:",this.blogData.length);console.log("Blog data:",this.blogData);console.log("Current carousel items:",this.carouselItems.length);while(this.carouselItems.length<this.blogData.length){console.log("Creating new slide...");const newSlide=this.createNewSlide();this.carouselContainer.appendChild(newSlide);this.carouselItems=this.carouselSection.querySelectorAll("li.user-items-list-carousel__slide");console.log("Total slides now:",this.carouselItems.length)}
if(this.carouselItems.length>this.blogData.length){console.warn("Too many carousel items, removing excess");while(this.carouselItems.length>this.blogData.length){this.carouselItems[this.carouselItems.length-1].remove();this.carouselItems=this.carouselSection.querySelectorAll("li.user-items-list-carousel__slide")}}
this.createItemTemplate();this.populateCarouselItems();this.carouselSection.dataset.blogSync="complete";console.log("Sync completed with",this.carouselItems.length,"items")}catch(error){console.error("Blog Carousel Sync failed:",error);this.carouselSection.dataset.blogSync="error"}}
async fetchBlogData(endpoint=this.blogEndpoint,collection=[]){try{const apiUrl=new URL(endpoint,window.location.origin);const params=new URLSearchParams(apiUrl.search);const timestamp=new Date().getTime();params.set("format","json");params.set("_t",timestamp);apiUrl.search=params.toString();const response=await fetch(apiUrl.toString());if(!response.ok){throw new Error(`API request failed: ${response.status}`)}
const data=await response.json();let posts=data.items||[];posts.forEach(post=>collection.push(post));return collection}catch(error){console.error("Error loading blog data:",error);throw error}}
createNewSlide(){const slideTemplate=`
      <li class="user-items-list-carousel__slide list-item" data-is-card-enabled="false">
        <div class="user-items-list-carousel__media-container" style="margin-bottom: 4%; width: 100%;">
          <div class="user-items-list-carousel__media-inner preFade fadeIn" data-media-aspect-ratio="4:3" data-animation-role="image">
            <img class="user-items-list-carousel__media" data-load="false" data-mode="cover" data-use-advanced-positioning="true" style="width: 100%; height: 100%; object-fit: cover;" data-parent-ratio="1.3" loading="lazy" decoding="async" data-loader="sqs">
          </div>
        </div>
        <div class="list-item-content">
          <div class="list-item-content__text-wrapper">
            <h2 class="list-item-content__title preFade" style="max-width: 100%;"></h2>
          </div>
        </div>
      </li>
    `;const tempDiv=document.createElement('div');tempDiv.innerHTML=slideTemplate;return tempDiv.firstElementChild}
createItemTemplate(){if(this.carouselItems.length>0){const templateHTML=this.carouselItems[0].innerHTML;this.carouselItems.forEach(item=>{item.innerHTML=templateHTML})}}
populateCarouselItems(){console.log("Populating",this.carouselItems.length,"items with",this.blogData.length,"blog posts");for(let[index,carouselItem]of this.carouselItems.entries()){if(!this.blogData[index]){console.log("No blog data for index",index);continue}
console.log("Processing blog post",index,":",this.blogData[index].title);const{title:blogTitle,assetUrl:blogImage,publishOn:blogDate,fullUrl:blogLink,excerpt:blogExcerpt}=this.blogData[index];let imageUrl=new URL(blogImage);let imageParams=new URLSearchParams(imageUrl.search);imageParams.set("syncedCarousel","true");imageUrl.search=imageParams.toString();let titleElement=carouselItem.querySelector(".list-item-content__title");let imageElement=carouselItem.querySelector("img");let mediaWrapper=carouselItem.querySelector(".user-items-list-carousel__media-container");if(titleElement){if(this.config.linkTitle){titleElement.innerHTML=`<a href="${blogLink}">${blogTitle}</a>`}else{titleElement.innerHTML=`<span>${blogTitle}</span>`}}
if(imageElement){let newImage=imageElement.cloneNode(!0);newImage.src=imageUrl;newImage.dataset.src=imageUrl;newImage.dataset.image=imageUrl;newImage.srcset="";newImage.alt=blogTitle;imageElement.parentElement.append(newImage);this.addBlogDateOverlay(mediaWrapper,blogDate);imageElement.style.display="none"}}
window.dispatchEvent(new Event("resize"))}
addBlogDateOverlay(container,publishDate){if(!container||!publishDate)return;let existingDateBadge=container.querySelector('.blog-date-badge');if(existingDateBadge){existingDateBadge.remove()}
const dateOverlay=document.createElement("div");const blogDate=new Date(publishDate);dateOverlay.classList.add("blog-date-badge");dateOverlay.innerHTML=`<div class="date-badge-content">
      <div class="date-month">${blogDate.toLocaleDateString("en-US", {month: "short"})}</div>
      <div class="date-day">${blogDate.getDate()}</div>
    </div>`;container.style.position="relative";container.appendChild(dateOverlay)}
setupEventHandlers(){window.addEventListener("DOMContentLoaded",()=>{this.populateCarouselItems()});window.addEventListener("load",()=>{this.populateCarouselItems()})}
handleCarouselController(){const controllerElement=this.carouselSection.querySelector("[data-controller]");const observer=new MutationObserver((mutations)=>{mutations.forEach((mutation)=>{if(mutation.attributeName==="data-controllers-bound"&&controllerElement.dataset.controllersBound==="UserItemsListCarousel"){controllerElement.removeAttribute("data-controller");observer.disconnect()}})});if(controllerElement&&controllerElement.dataset.controllersBound==="UserItemsListCarousel"){controllerElement.removeAttribute("data-controller")}else if(controllerElement){observer.observe(controllerElement,{attributes:!0,attributeFilter:["data-controllers-bound"]})}}}(function(){const initBlogCarouselSync=()=>{const carouselSections=document.querySelectorAll(".user-items-list-carousel");carouselSections.forEach(section=>{if(section.dataset.blogSync)return;const config={target:section.closest(".page-section"),linkTitle:!0,linkImage:!1};if(config.target){config.target.BlogCarouselSync=new BlogCarouselSync(config)}else{section.dataset.blogSync="no-target"}})};window.blogCarouselSync={init:initBlogCarouselSync};initBlogCarouselSync()})()
</script>

#6. Result

#7. Extra

To turn Carousel to 2 items/row on Mobile, you can use this to Custom CSS.

@media screen and (max-width:767px) {
    section:has(.blog-date-badge) ul {
        grid-template-columns: repeat(2,1fr) !important;
    }
}

To hide Carousel on Desktop, use this to Custom CSS

@media screen and (min-width:768px) {
    section:has(.blog-date-badge){
        display: none;
    }
}

 

Buy me a coffee