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.
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.
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.The following are the fields that content editors will use in the page editor interface.
text_color
)Text and Number Color
color
counter_boxes
)Counter Boxes
group
(with repeater enabled)title
)Title
text
number
)Number
number
suffix
)Suffix (e.g., %)
text
<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>
.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;
}
}
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);
});
});
We can customize this sample to match your specific business requirements.
Book Free ConsultationThe 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.
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.
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.
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.
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!