Logo
Home
Resources

Resources

Blog
Youtube
Template

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.

‍

Demo Video

Over view
Code

Detail

Purpose

  • To compellingly communicate product features and benefits in a storytelling format.
  • To present case studies or customer testimonials persuasively with accompanying photos.
  • To explain a service delivery process or workflow step-by-step.

Features

  • Ability to add, delete, and reorder an unlimited number of "image" and "heading/description" sets.
  • Option to select the image position (left or right) for each individual set.
  • Full control over the text content, color, and font size for the section number, subtitle, title, and description.
  • A fade-in-up animation is applied to elements as they enter the viewport upon scrolling.

Implementation Points & Notes

  • Repeater: The ability to add multiple content boxes is implemented using "type": "group" with the "occurrence" property in the fields.json.
  • Layout Control: Based on the value of the Boolean field named "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.
  • Animation: A dedicated JavaScript file using the 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.
  • Styling Options: Global style settings (like text colors and font sizes) are handled by color picker and number fields located in the "STYLE" tab, which apply their values to inline styles in the HTML.

Field Definitions (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"
  }
]

‍

Source Code

HTML
{# ======== 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>
CSS
/* ======== 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 */
  }
}

‍

‍

‍

Javascript
// ======== 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);
  });
});

‍

‍

‍

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