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.
"type": "group"
with the "occurrence"
property in the fields.json
."image_on_left"
within the group, a HubL if
statement outputs a specific CSS class (e.g., layout--image-left
) to the HTML, which controls the layout.IntersectionObserver
API is required. It detects when an element enters the viewport and adds a CSS class (e.g., .is-visible
) to trigger the CSS transition.fields.json
)This module is configured by the following field definitions.
[
{
"name": "boxes",
"label": "Content Boxes",
"type": "group",
"occurrence": {
"min": 1,
"max": null,
"default": 2
},
"children": [
{
"type": "image",
"name": "image",
"label": "Image",
"responsive": true,
"show_loading": false
},
{
"type": "text",
"name": "section_number",
"label": "Section Number"
},
{
"type": "text",
"name": "subtitle",
"label": "Subtitle"
},
{
"type": "text",
"name": "title",
"label": "Title"
},
{
"type": "richtext",
"name": "description",
"label": "Description"
},
{
"type": "boolean",
"name": "image_on_left",
"label": "Place image on the left",
"display": "checkbox",
"default": false
}
]
},
{
"type": "color",
"name": "title_color",
"label": "Title Color",
"tab": "STYLE"
},
{
"type": "color",
"name": "subtitle_color",
"label": "Subtitle Color",
"tab": "STYLE"
},
{
"type": "color",
"name": "description_color",
"label": "Description Color",
"tab": "STYLE"
},
{
"type": "number",
"name": "title_font_size",
"label": "Title Font Size",
"suffix": "px",
"tab": "STYLE"
},
{
"type": "number",
"name": "subtitle_font_size",
"label": "Subtitle Font Size",
"suffix": "px",
"tab": "STYLE"
},
{
"type": "number",
"name": "description_font_size",
"label": "Description Font Size",
"suffix": "px",
"tab": "STYLE"
},
{
"type": "number",
"name": "section_number_font_size",
"label": "Section Number Font Size",
"suffix": "px",
"tab": "STYLE"
}
]
{# ======== module.html (with font size adjustment feature) ======== #}
<div class="feature-section-module">
{% for box in module.boxes %}
{% set layout_class = box.image_on_left ? 'layout--image-left' : 'layout--image-right' %}
<div class="feature-box animated-box {{ layout_class }}">
<div class="feature-text-content">
<div class="section-header">
{# ★ Change: Added font-size #}
<span class="section-number" style="color: {{ module.title_color.color }}; {% if module.section_number_font_size %}font-size: {{ module.section_number_font_size }}px;{% endif %}">
{{ box.section_number }}
</span>
{# ★ Change: Added font-size #}
<span class="section-subtitle" style="color: {{ module.subtitle_color.color }}; {% if module.subtitle_font_size %}font-size: {{ module.subtitle_font_size }}px;{% endif %}">
{{ box.subtitle }}
</span>
</div>
{# ★ Change: Added font-size #}
<h2 class="section-title" style="color: {{ module.title_color.color }}; {% if module.title_font_size %}font-size: {{ module.title_font_size }}px;{% endif %}">
{{ box.title }}
</h2>
{# ★ Change: Added font-size #}
<div class="section-description" style="color: {{ module.description_color.color }}; {% if module.description_font_size %}font-size: {{ module.description_font_size }}px;{% endif %}">
{{ box.description }}
</div>
</div>
<div class="feature-image-content">
{% if box.image.src %}
<img src="{{ box.image.src }}" alt="{{ box.image.alt }}" loading="lazy">
{% endif %}
</div>
</div>
{% endfor %}
</div>
/* ======== module.css (Font-size adjusted version) ======== */
/* Basic styles for each section (box) */
.feature-box {
display: flex;
align-items: center;
gap: 60px;
margin-bottom: 80px;
opacity: 0;
transform: translateY(40px);
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
}
.feature-box.is-visible {
opacity: 1;
transform: translateY(0);
}
.feature-text-content,
.feature-image-content {
flex: 1;
min-width: 0;
}
.feature-box.layout--image-left {
flex-direction: row-reverse;
}
.feature-image-content img {
width: 100%;
height: auto;
border-radius: 8px;
}
/* Text-related styles */
.section-header {
display: flex;
align-items: baseline;
margin-bottom: 16px;
}
.section-number {
font-size: 60px; /* ★ Change: Slightly smaller from 72px */
font-weight: 700;
line-height: 1;
margin-right: 16px;
}
.section-subtitle {
font-size: 16px; /* ★ Change: Slightly smaller from 18px */
font-weight: 600;
}
.section-title {
font-size: 32px; /* ★ Change: Slightly smaller from 36px */
font-weight: 700;
line-height: 1.4;
margin-bottom: 20px;
}
.section-description {
font-size: 16px; /* No change */
line-height: 1.8;
}
/* Responsive settings for smartphones */
@media (max-width: 768px) {
.feature-box {
flex-direction: column !important;
gap: 30px;
margin-bottom: 50px;
}
.section-title {
font-size: 26px; /* ★ Change: Adjusted size for smartphones */
}
.section-number {
font-size: 50px; /* ★ Change: Adjusted size for smartphones */
}
}
// ======== module.js ======== //
document.addEventListener('DOMContentLoaded', () => {
// Get all elements to be animated
const animatedBoxes = document.querySelectorAll('.animated-box');
// Create an observer to watch if elements become visible
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
// If entry.isIntersecting is true, it means the element has entered the viewport
if (entry.isIntersecting) {
// Use setTimeout to create a staggered animation effect
setTimeout(() => {
entry.target.classList.add('is-visible');
}, index * 200); // Delay each item by 0.2 seconds
// Stop observing the element once it has been made visible
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.1 // Trigger when 10% of the element is visible
});
// Start observing each box
animatedBoxes.forEach(box => {
observer.observe(box);
});
});
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!