Logo
Home
Resources

Resources

Blog
Youtube
Template

How to Build a Reusable "Animated Counter" Module in HubSpot to Showcase Key Metrics

Learn how to implement a custom "Animated Counter" module in HubSpot to visually highlight your company's achievements and KPIs. This guide provides the complete HTML, CSS, JavaScript, and field configuration, allowing you to create a module that is intuitive even for non-developers.

Demo Video

Over view
Code

Detail

Counter Module

This module visually highlights key company achievements, metrics, or important statistics (e.g., customer satisfaction rates, projects completed) with animated numbers, effectively capturing visitor attention.

What You Can Do

  • Editors can freely add, delete, and reorder as many counter boxes as needed. Each box consists of a "Title," a "Number," and a "Suffix" (e.g., "%" or "Cases").
  • When a user scrolls down the page and the module becomes visible, the numbers animate, counting up from 0 to the specified target value.
  • The color of all text and numbers in the module can be changed at once using a single color picker.

Implementation Points & Notes

  • Field Configuration (Repeater):This module utilizes a Group field with the Repeater option enabled. This setup allows content editors to dynamically add as many counters as they require.
  • Animation Trigger:The animation is powered by JavaScript using the Intersection Observer API. This ensures the counting animation only begins when the module enters the browser's viewport, which is an effective performance optimization technique.
  • Responsive Layout:The layout is built with CSS Flexbox (display: flex) to align the counter boxes horizontally. The flex-wrap: wrap property is included to ensure the boxes stack vertically on smaller screens, maintaining a responsive design.

Detailed Input Fields

The following are the fields that content editors will use in the page editor interface.

‍

  1. Text and Number Color (text_color)
    • Label: Text and Number Color
    • Type: color
    • Description: Allows you to globally set the color for all text elements within this module (title, number, and suffix) using a color picker.
  2. Counter Boxes (counter_boxes)
    • Label: Counter Boxes
    • Type: group (with repeater enabled)
    • Description: This is the main repeatable field that allows you to create multiple counter boxes. By clicking the "+Add" button, you can generate as many boxes as needed. Each box contains the following three settings.
    • Child Fields:
      • Title (title)
        • Label: Title
        • Type: text
        • Description: The heading that describes what the number represents (e.g., "Closing Rate," "Customer Satisfaction").
      • Number (number)
        • Label: Number
        • Type: number
        • Description: The final target number you want the counter to animate to.
      • Suffix (suffix)
        • Label: Suffix (e.g., %)
        • Type: text
        • Description: The unit or symbol that appears after the number (e.g., "%," "Million," "People").

‍

Source Code

HTML
<div class="counter-wrapper">
  {% for box in module.counter_boxes %}
    <div class="counter-box">
      <h2 class="counter-title" style="color: {{ module.text_color.color }};">
        {{ box.title }}
      </h2>
      <div class="counter-number-container" style="color: {{ module.text_color.color }};">
        <span class="counter-number" data-target="{{ box.number }}">0</span>
        <span class="counter-suffix">{{ box.suffix }}</span>
      </div>
    </div>
  {% endfor %}
</div>
{# Load the CSS and JavaScript files #}
<link rel="stylesheet" href="{{ module.path }}/module.css">
<script src="{{ module.path }}/module.js" defer></script>
CSS
.logo-slider-wrapper {
  width: 100%;
  overflow: hidden;
  background: #ffffff;
  padding: 40px 0;
  position: relative;
}
.counter-wrapper {
  display: flex;
  justify-content: space-around;
  align-items: center;
  flex-wrap: wrap; /* Wrap items if the screen is narrow */
  text-align: center;
  padding: 20px;
  font-family: sans-serif;
}
.counter-box {
  padding: 20px;
  min-width: 200px;
}
.counter-title {
  font-size: 1.5em;
  margin-bottom: 10px;
}
.counter-number-container {
  font-size: 4em;
  font-weight: bold;
}
.counter-suffix {
  font-size: 0.8em;
  margin-left: 5px;
}
.logo-slider-container {
  position: relative;
  width: 100%;
  max-width: 100%;
}
.logo-slider-track {
  display: flex;
  align-items: center;
  white-space: nowrap;
  animation: logoSlide 25s linear infinite;
  will-change: transform;
}
.logo-slider-item {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 200px;
  height: 100px;
  margin-right: 60px;
  padding: 15px;
}
.logo-slider-item img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  filter: grayscale(100%);
  opacity: 0.7;
  transition: all 0.3s ease;
}
.logo-slider-item:hover img {
  filter: grayscale(0%);
  opacity: 1;
  transform: scale(1.05);
}
/* HubSpot compatibility: Removed 0% and specified only 100% */
@keyframes logoSlide {
  100% {
    transform: translateX(-50%);
  }
}
/* Pause on hover */
.logo-slider-wrapper:hover .logo-slider-track {
  animation-play-state: paused;
}
/* Responsive */
@media (max-width: 768px) {
  .logo-slider-track {
    animation-duration: 30s;
  }

  .logo-slider-item {
    width: 150px;
    height: 75px;
    margin-right: 40px;
    padding: 10px;
  }

  .logo-slider-wrapper {
    padding: 30px 0;
  }
}
@media (max-width: 480px) {
  .logo-slider-track {
    animation-duration: 35s;
  }

  .logo-slider-item {
    width: 120px;
    height: 60px;
    margin-right: 30px;
    padding: 8px;
  }

  .logo-slider-wrapper {
    padding: 20px 0;
  }
}
/* Accessibility support */
@media (prefers-reduced-motion: reduce) {
  .logo-slider-track {
    animation: none;
  }
}
Javascript
document.addEventListener('DOMContentLoaded', () => {
  const counters = document.querySelectorAll('.counter-number');
  const animationDuration = 2000; // Animation duration (in milliseconds)

  const startCounter = (counter) => {
    const target = +counter.getAttribute('data-target');
    let current = 0;

    // Calculate how much to increase the number in each step of the animation
    // The larger the target value, the larger the increment per step
    const increment = target / (animationDuration / 16); // Assuming 60fps

    const updateCounter = () => {
      current += increment;
      if (current < target) {
        counter.innerText = Math.ceil(current).toLocaleString();
        requestAnimationFrame(updateCounter); // Update again on the next frame
      } else {
        counter.innerText = target.toLocaleString(); // Finally, set the target value
      }
    };
    updateCounter();
  };

  const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      // Start the animation when the element enters the viewport
      if (entry.isIntersecting) {
        startCounter(entry.target);
        // Stop observing once it has been executed
        observer.unobserve(entry.target);
      }
    });
  }, {
    threshold: 0.5 // Trigger when 50% of the element is visible
  });

  // Add each counter element to the observer
  counters.forEach(counter => {
    observer.observe(counter);
  });
});

Related Template

Need Customization?

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

Book Free Consultation

[GAS Library] Department Classification Library

The Department Classification Library is an AI-powered automatic classification tool that runs in the Google Apps Script environment. It uses ChatGPT or Gemini to automatically classify company department names into predefined business segments.

Hubspot modules Feature Section Module

This module is designed to visually showcase the features of a product or service by arranging images and text in an alternating layout. It uses scroll-triggered animations to engage users.

‍

How to Build a Reusable "Animated Counter" Module in HubSpot to Showcase Key Metrics

Learn how to implement a custom "Animated Counter" module in HubSpot to visually highlight your company's achievements and KPIs. This guide provides the complete HTML, CSS, JavaScript, and field configuration, allowing you to create a module that is intuitive even for non-developers.

[GAS Library] Automatically Format Phone Numbers in Google Sheets by Country

This guide explains how to use "PhoneFormatter," a convenient Google Apps Script (GAS) library that allows you to batch-format international phone numbers in a Google Sheet. Easily convert numbers into their proper, hyphenated format simply by providing a country name in English, Japanese, or even abbreviated form.

[GAS Library] Easy Copy-Paste! A Library to Automatically Parse Addresses in Google Sheets into Country, State, and City

Do you ever face the tedious task of managing address lists from various countries like Japan, the USA, and the UK in Google Sheets, wishing you could easily separate them by country, state, or city?

This article introduces "AddressParserLibrary," a Google Apps Script library that automates this data cleaning and parsing process with a single click.

Once you add the library, anyone can easily implement address parsing by making minor adjustments to the provided sample code. We will walk through everything from setup to execution in a step-by-step guide that is clear even for those unfamiliar with programming. Start boosting your daily productivity today!

‍

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

Our ServiceHubspot Solutions
Terms & ConditionsPrivacy Policy

Copyright ©SugerTechEnterprise .Inc

Back To Top Image