Logo
Home
Resources

Product

Custom Workflow

Resources

Blog
Youtube
Template
Home
>
Product
>
Template Top
>
detail

HubSpot CMS: Implementing the Dynamic "Success Story Card Module"

"Success Stories" (or Case Studies) are essential content for building customer trust on your website. By using the Success Story Card Module, you can maintain design consistency while beautifully displaying multiple success stories in an interactive slider format.

‍

Demo Video

Over view
Code

Detail

"Success Stories" (or Case Studies) are essential content for building customer trust on your website. By using the Success Story Card Module, you can maintain design consistency while beautifully displaying multiple success stories in an interactive slider format.

1. Objectives

The primary objective of this module is to enable the intuitive creation and updating of visually appealing success story sections without requiring technical expertise.

  • Visualize Credibility: Organize company logos and key achievements into a card format to provide peace of mind to potential customers.
  • Smooth Browsing Experience: Equipped with a carousel (slider) function, allowing users to browse many cases stress-free, even in limited space.
  • Instant Design Customization: Freely change heading sizes, colors, and card borders within the editor without touching a single line of code.

2. Use Cases

  • Homepage Trust Section: Showcase major client logos and short testimonials on your main page to bolster brand authority.
  • Service Page Success Stories: Pick specific cases related to a particular service to provide that final "nudge" to users considering a purchase.
  • Landing Page Social Proof: Use "Customer Voices" on campaign pages to build rapid credibility within a short timeframe.

3. Implementation Steps

This section covers the entire process from page creation to code application and final configuration.

Step 1: Page Creation and Module Placement

Create a new page from the HubSpot admin dashboard. Search for the module name in the left sidebar of the editor, then drag and drop it into the desired editing area.

Step 2: Applying Code in the Design Manager

To enable advanced dynamic functionality, perform the following in the Design Manager:

  • File Preparation: Create a new module within the Design Manager.
  • Paste the Code: Simply copy and paste the provided source code into the respective HTML/HubL, JS, and CSS sections, then save and publish.

Step 3: Editor Configuration and Customization

Once the code is applied, you can fine-tune the details via the no-code side panel in the page editor:

  • Add Content: Under the "cards" group, add as many items as needed, including images, titles, and descriptions for each case.
  • General Settings: Enter the section title and toggle the "autoplay" feature on or off.
  • Style Adjustments: Use the color picker to adjust the colors of titles, text, and card borders to match your brand identity.

Module Source Code

Create a "New Module" in the HubSpot Design Manager and paste the following code into each respective section.

1. fields.json

This is the field definition. It consists of a repeater field (Items) and style setting fields (Styles).

[
 {
  "allow_new_line": false,
  "display_width": null,
  "id": "f828e942-d88b-6872-99aa-7e6839a2ba4a",
  "label": "section_title",
  "locked": false,
  "name": "section_title",
  "required": false,
  "type": "text"
 },
 {
  "default": {
   "color": null,
   "opacity": null
  },
  "display_width": null,
  "id": "23d16879-569c-2624-b906-6ae2371a442f",
  "label": "title_color",
  "locked": false,
  "name": "title_color",
  "required": false,
  "type": "color"
 },
 {
  "display": "text",
  "display_width": null,
  "id": "bf18e844-9553-d75c-fe55-7e0c1a65fe9d",
  "label": "title_font_size",
  "locked": false,
  "name": "title_font_size",
  "required": false,
  "step": 1,
  "type": "number"
 },
 {
  "children": [
   {
    "default": {
     "size_type": "auto",
     "src": "",
     "alt": null,
     "loading": "lazy"
    },
    "display_width": null,
    "id": "0c514272-f096-02d4-33c1-791a40fe2d9f",
    "label": "Image",
    "locked": false,
    "name": "image",
    "required": false,
    "resizable": true,
    "responsive": true,
    "show_loading": false,
    "type": "image"
   },
   {
    "allow_new_line": false,
    "display_width": null,
    "id": "1f453c47-b6a2-a51a-f8d7-dc5248114ba0",
    "label": "title",
    "locked": false,
    "name": "title",
    "required": false,
    "type": "text"
   },
   {
    "display_width": null,
    "id": "456812f3-1652-0fff-f563-f8787f2efbcc",
    "label": "description",
    "locked": false,
    "name": "description",
    "required": false,
    "type": "richtext"
   }
  ],
  "default": [],
  "display_width": null,
  "expanded": false,
  "group_occurrence_meta": null,
  "id": "004e3419-0e40-def3-93c0-bf008f14bb26",
  "label": "cards",
  "locked": false,
  "name": "cards",
  "occurrence": {},
  "required": false,
  "tab": "CONTENT",
  "type": "group"
 },
 {
  "default": {
   "color": null,
   "opacity": null
  },
  "display_width": null,
  "id": "f577bff8-1c57-1000-f2b9-0297e618fb2a",
  "label": "card_title_color",
  "locked": false,
  "name": "card_title_color",
  "required": false,
  "type": "color"
 },
 {
  "default": {
   "color": null,
   "opacity": null
  },
  "display_width": null,
  "id": "33968af4-9a30-8270-1df2-5ff36ebcca38",
  "label": "card_descriotion_color",
  "locked": false,
  "name": "card_descriotion_color",
  "required": false,
  "type": "color"
 },
 {
  "display": "text",
  "display_width": null,
  "id": "9c9f49bb-5a85-c6b6-395b-c8577fffac7b",
  "label": "card_description_font_size",
  "locked": false,
  "name": "card_description_font_size",
  "required": false,
  "step": 1,
  "type": "number"
 },
 {
  "default": {
   "color": null,
   "opacity": null
  },
  "display_width": null,
  "id": "7152aefd-c5cc-1ad9-9025-c6f55715d19d",
  "label": "border_color",
  "locked": false,
  "name": "border_color",
  "required": false,
  "type": "color"
 },
 {
  "display": "text",
  "display_width": null,
  "id": "1c170b7c-6ba3-2f81-7881-884f1b780fc9",
  "label": "border_width",
  "locked": false,
  "name": "border_width",
  "required": false,
  "step": 1,
  "type": "number"
 },
 {
  "default": false,
  "display": "checkbox",
  "display_width": null,
  "id": "624857cf-7d81-5530-10aa-72b51715b231",
  "label": "autoplay",
  "locked": false,
  "name": "autoplay",
  "required": false,
  "type": "boolean"
 }
]

Source Code

HTML
{% if module.section_title %}
<div class="hs-carousel-section">
  <h2 class="hs-carousel-title" style="color: {{ module.title_color.color }}; font-size: {{ module.title_font_size }}px;">
    {{ module.section_title }}
  </h2>
  
  <div class="hs-carousel-container">
    <button class="hs-carousel-nav hs-carousel-nav-prev" aria-label="前へ">
      <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
        <path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
    </button>
    
    <div class="hs-carousel-wrapper">
      <div class="hs-carousel-track">
        {% for card in module.cards %}
        <div class="hs-carousel-card" style="border-color: {{ module.border_color.color }}; border-width: {{ module.border_width }}px;">
          {% if card.image.src %}
          <div class="hs-card-image-wrapper">
            <img src="{{ card.image.src }}" alt="{{ card.image.alt }}" class="hs-card-image">
          </div>
          {% endif %}
          
          <div class="hs-card-content">
            {% if card.title %}
            <h3 class="hs-card-title" style="color: {{ module.card_title_color.color }}; font-size: {{ module.card_title_font_size }}px;">
              {{ card.title }}
            </h3>
            {% endif %}
            
            {% if card.description %}
            <p class="hs-card-description" style="color: {{ module.card_description_color.color }}; font-size: {{ module.card_description_font_size }}px;">
              {{ card.description }}
            </p>
            {% endif %}
          </div>
        </div>
        {% endfor %}
      </div>
    </div>
    
    <button class="hs-carousel-nav hs-carousel-nav-next" aria-label="次へ">
      <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
        <path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
    </button>
  </div>
  
  <div class="hs-carousel-dots"></div>
</div>
{% endif %}

<script>
(function() {
  const container = document.querySelector('.hs-carousel-section');
  if (!container) return;
  
  const track = container.querySelector('.hs-carousel-track');
  const prevBtn = container.querySelector('.hs-carousel-nav-prev');
  const nextBtn = container.querySelector('.hs-carousel-nav-next');
  const dotsContainer = container.querySelector('.hs-carousel-dots');
  const cards = container.querySelectorAll('.hs-carousel-card');
  
  const autoplay = {{ module.autoplay|lower }};
  const autoplaySpeed = {{ module.autoplay_speed || 5000 }};
  
  let currentIndex = 0;
  let autoplayInterval;
  let cardsPerView = 3;
  let totalSlides = 0;
  
  function getCardsPerView() {
    if (window.innerWidth <= 768) {
      return 1;
    } else if (window.innerWidth <= 1024) {
      return 2;
    }
    return 3;
  }
  
  function getTotalSlides() {
    return Math.ceil(cards.length / cardsPerView);
  }
  
  function createDots() {
    dotsContainer.innerHTML = '';
    totalSlides = getTotalSlides();
    
    for (let i = 0; i < totalSlides; i++) {
      const dot = document.createElement('button');
      dot.classList.add('hs-carousel-dot');
      dot.setAttribute('aria-label', 'スライド ' + (i + 1));
      dot.setAttribute('data-index', i);
      if (i === currentIndex) {
        dot.classList.add('active');
      }
      dot.addEventListener('click', function() {
        const index = parseInt(this.getAttribute('data-index'));
        goToSlide(index);
      });
      dotsContainer.appendChild(dot);
    }
  }
  
  function updateDots() {
    const allDots = dotsContainer.querySelectorAll('.hs-carousel-dot');
    allDots.forEach(function(dot, index) {
      if (index === currentIndex) {
        dot.classList.add('active');
      } else {
        dot.classList.remove('active');
      }
    });
  }
  
  function updateCarousel() {
    cardsPerView = getCardsPerView();
    totalSlides = getTotalSlides();
    
    if (currentIndex >= totalSlides) {
      currentIndex = totalSlides - 1;
    }
    if (currentIndex < 0) {
      currentIndex = 0;
    }
    
    const cardWidth = 100 / cardsPerView;
    const offset = -(currentIndex * cardWidth * cardsPerView);
    track.style.transform = 'translateX(' + offset + '%)';
    
    updateDots();
  }
  
  function goToSlide(index) {
    totalSlides = getTotalSlides();
    if (index >= 0 && index < totalSlides) {
      currentIndex = index;
      updateCarousel();
      resetAutoplay();
    }
  }
  
  function nextSlide() {
    totalSlides = getTotalSlides();
    if (currentIndex >= totalSlides - 1) {
      currentIndex = 0;
    } else {
      currentIndex++;
    }
    updateCarousel();
  }
  
  function prevSlide() {
    totalSlides = getTotalSlides();
    if (currentIndex <= 0) {
      currentIndex = totalSlides - 1;
    } else {
      currentIndex--;
    }
    updateCarousel();
  }
  
  function resetAutoplay() {
    if (autoplay) {
      clearInterval(autoplayInterval);
      autoplayInterval = setInterval(nextSlide, autoplaySpeed);
    }
  }
  
  function init() {
    cardsPerView = getCardsPerView();
    totalSlides = getTotalSlides();
    currentIndex = 0;
    createDots();
    updateCarousel();
  }
  
  init();
  
  prevBtn.addEventListener('click', function() {
    prevSlide();
    resetAutoplay();
  });
  
  nextBtn.addEventListener('click', function() {
    nextSlide();
    resetAutoplay();
  });
  
  if (autoplay) {
    autoplayInterval = setInterval(nextSlide, autoplaySpeed);
  }
  
  container.addEventListener('mouseenter', function() {
    if (autoplay) clearInterval(autoplayInterval);
  });
  
  container.addEventListener('mouseleave', function() {
    resetAutoplay();
  });
  
  let resizeTimer;
  window.addEventListener('resize', function() {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(function() {
      const oldCardsPerView = cardsPerView;
      cardsPerView = getCardsPerView();
      
      if (oldCardsPerView !== cardsPerView) {
        currentIndex = 0;
        createDots();
      }
      updateCarousel();
    }, 250);
  });
})();
</script>
CSS
.hs-carousel-section {
  max-width: 1200px;
  margin: 0 auto;
  padding: 40px 20px;
  box-sizing: border-box;
}
.hs-carousel-title {
  text-align: center;
  margin: 0 0 40px 0;
  font-weight: bold;
  line-height: 1.2;
}
.hs-carousel-container {
  position: relative;
  padding: 0 60px;
  width: 100%;
  box-sizing: border-box;
  margin: 0 auto;
}
.hs-carousel-wrapper {
  overflow: hidden;
  margin-bottom: 30px;
  width: 100%;
  position: relative;
}
.hs-carousel-track {
  display: flex;
  transition: transform 0.5s ease;
  will-change: transform;
}
.hs-carousel-card {
  flex: 0 0 calc(33.333% - 16px);
  background: #fff;
  border-radius: 12px;
  border-style: solid;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  margin-right: 24px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}
.hs-carousel-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.hs-card-image-wrapper {
  width: 100%;
  height: 200px;
  overflow: hidden;
  background: #f5f5f5;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.hs-card-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 20px;
  display: block;
}
.hs-card-content {
  padding: 24px;
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}
.hs-card-title {
  margin: 0 0 12px 0;
  font-weight: 600;
  line-height: 1.4;
}
.hs-card-description {
  margin: 0;
  line-height: 1.6;
}
.hs-carousel-nav {
  position: absolute !important;
  top: 50% !important;
  transform: translateY(-50%) !important;
  background: #fff !important;
  border: 2px solid #ddd !important;
  border-radius: 50% !important;
  width: 48px !important;
  height: 48px !important;
  min-width: 48px !important;
  min-height: 48px !important;
  max-width: 48px !important;
  max-height: 48px !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  cursor: pointer !important;
  transition: all 0.3s ease !important;
  z-index: 100 !important;
  color: #333 !important;
  padding: 0 !important;
  margin: 0 !important;
  font-size: 16px !important;
  line-height: 1 !important;
  text-decoration: none !important;
  outline: none !important;
  box-sizing: border-box !important;
  flex-shrink: 0 !important;
}
.hs-carousel-nav:hover {
  background: #f5f5f5 !important;
  border-color: #999 !important;
}
.hs-carousel-nav:focus {
  outline: 2px solid #fed500 !important;
  outline-offset: 2px !important;
}
.hs-carousel-nav-prev {
  left: 0 !important;
  right: auto !important;
}
.hs-carousel-nav-next {
  right: 0 !important;
  left: auto !important;
}
.hs-carousel-nav svg {
  pointer-events: none;
  display: block;
  width: 24px;
  height: 24px;
}
.hs-carousel-nav svg path {
  stroke: #333 !important;
}
.hs-carousel-dots {
  display: flex;
  justify-content: center;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 20px;
  padding: 0;
}
.hs-carousel-dot {
  width: 12px !important;
  height: 12px !important;
  min-width: 12px !important;
  min-height: 12px !important;
  max-width: 12px !important;
  max-height: 12px !important;
  border-radius: 50% !important;
  background: #ddd !important;
  border: none !important;
  cursor: pointer !important;
  transition: all 0.3s ease !important;
  padding: 0 !important;
  margin: 0 !important;
  flex-shrink: 0 !important;
  display: block !important;
  box-sizing: border-box !important;
}
.hs-carousel-dot.active {
  background: #fed500 !important;
  width: 32px !important;
  min-width: 32px !important;
  max-width: 32px !important;
  height: 12px !important;
  min-height: 12px !important;
  max-height: 12px !important;
  border-radius: 6px !important;
}
.hs-carousel-dot:hover {
  background: #bbb !important;
}
.hs-carousel-dot:hover.active {
  background: #fed500 !important;
}
@media (max-width: 1024px) {
  .hs-carousel-card {
    flex: 0 0 calc(50% - 12px);
  }
  .hs-carousel-container {
    padding: 0 50px;
  }
}
@media (max-width: 768px) {
  .hs-carousel-card {
    flex: 0 0 calc(100% - 24px);
  }
  .hs-carousel-container {
    padding: 0 50px;
  }
  .hs-carousel-nav {
    width: 40px !important;
    height: 40px !important;
    min-width: 40px !important;
    min-height: 40px !important;
    max-width: 40px !important;
    max-height: 40px !important;
  }
  .hs-carousel-nav svg {
    width: 20px;
    height: 20px;
  }
}
Javascript

FAQ

Does it display one card at a time on smartphones?

Yes. It features a built-in responsive function that automatically adjusts the display: typically multiple cards on PC and a single, optimized card on smartphones.

Can I change the speed of the automatic sliding?

Yes. You can adjust the sliding interval by modifying the setting values within the code.

Is it possible to remove the borders from the cards?

Yes. You can change to a flat design by setting the "border_width" to 0 in the editor or by selecting "transparent" in the color picker for the border color.

Search

Search more

Related Template

Need Customization?

We can customize this sample to match your specific business requirements.

Book Free Consultation

Got a quick dev request?

Put it on Trello!Need a fix for HubSpot, CMS, or GAS? Post it on Trello.

Development Requests Here

HubSpot CMS: Implementing the Data Type Conversion Action

HubSpot CMS: Implementing a Professional "Pricing Table Module"

HubSpot CMS: Implementing the "Login Check & Redirect Module" for Secure Page Access

HubSpot CMS: Implementing the "Carousel Card Module" for Smart Content Organization

HubSpot CMS: Implementation Guide for Video Hero Banners (Video Above-the-Fold)

Company Info
Name : SweetsVillage .Inc
CEO :
‍
Tomoo Motoyama

HomeTemplateCustomWorkflow
Terms & ConditionsPrivacy PolicyContact us

Copyright ©SweetsVillage .Inc

Back To Top Image