Skip to main content
FreshJuice DEV follows HubSpot’s CMS architecture with a clean, modular approach that promotes reusability and maintainability.

Architecture Overview

The theme is built on four main building blocks:

Templates

Define page structure and layout using HubL

Modules

Reusable content components with fields

Macros

Utility functions for common operations

Sections

Drag-and-drop layout components

Templates

Templates define the overall page structure and use HubL (HubSpot Markup Language) to create dynamic pages.

Template Types

Base templates that define the common HTML structure:
templates/layouts/base.html
<!doctype html>
<html lang="{{ html_lang }}" x-data="xDOM" hs-cms-theme="FreshJuice">
  <head>
    <meta charset="utf-8">
    <title>{{ page_meta.html_title or pageTitle }}</title>
    {{ require_css(get_asset_url("../../css/main.css")) }}
    {{ standard_header_includes }}
  </head>
  <body>
    <div class="body-wrapper {{ builtin_body_classes }}">
      {% block header %}
        {% global_partial path="../partials/header.html" %}
      {% endblock header %}
      
      <main id="main-content" class="body-container-wrapper">
        {% block body %}
        {% endblock body %}
      </main>
      
      {% block footer %}
        {% global_partial path="../partials/footer.html" %}
      {% endblock footer %}
    </div>
    {{ require_js(get_asset_url("../../js/main.js"), { position: "footer", defer: true }) }}
    {{ standard_footer_includes }}
  </body>
</html>
Templates use HubL blocks ({% block %}) to create inheritance hierarchies and allow child templates to override specific sections.

Modules

Modules are self-contained components that editors can add to pages. Each module consists of multiple files:

Module Structure

Every module lives in its own .module directory:
button.module/
├── fields.json          # Field definitions for the module editor
├── meta.json            # Module metadata and settings
├── module.css           # Module-specific styles (optional)
├── module.html          # Module template markup
└── module.js            # Module-specific JavaScript (optional)

Module Example

<div class="not-prose container flex justify-center items-center">
  {% set href = module.cta_link.url.type == "EMAIL_ADDRESS" ? "mailto:" ~ module.cta_link.url.href : module.cta_link.url.href %}
  <a class="button rounded-md"
     href="{{ module.cta_link.url.type == "CALL_TO_ACTION" ? href : href|escape_url }}"
    {% if module.cta_link.open_in_new_tab %} target="_blank" {% endif %}
    {% if module.cta_link.rel %} rel="{{ module.cta_link.rel|escape_attr }}" {% endif %}>
    {{ module.button_text }}
  </a>
</div>

Available Modules

FreshJuice DEV includes 30+ modules organized by category:
  • card - Content card with image and text
  • flip-cards - Interactive flip card component
  • list - Customizable list component
  • lite-youtube - Optimized YouTube embed
  • lite-vimeo - Optimized Vimeo embed
  • codepen - CodePen embed
  • github-gist - GitHub Gist embed
  • button - Call-to-action button
  • blog-listing - Blog post listing
  • blog-listing-hero - Hero blog listing
  • blog-pagination - Pagination controls
  • related-posts - Related posts widget
  • accordion - Collapsible content sections
  • tabs - Tabbed content interface
  • google-maps - Google Maps embed
  • testimonials-simple-centered - Centered testimonial
  • testimonials-side-by-side - Side-by-side layout
  • testimonials-with-large-avatar - Avatar-focused design
  • testimonials-with-star-rating - Rating display
  • social-icons - Social media icons
  • pricing-card - Individual pricing card
  • pricing-cards - Pricing comparison
  • price-card-with-details - Detailed pricing
  • stats-simple - Simple statistics
  • stats-simple-grid - Grid statistics
  • stats-timeline - Timeline statistics
Modules can be marked as global: true in meta.json to make them available across all page types without manually adding them to each page.

Macros

Macros are reusable HubL functions that perform common operations. They’re similar to functions in traditional programming.

Available Macros

{# Returns the slug version of a string #}
{# Usage: {% from 'macros/slugify.html' import slugify %} #}
{# {{ slugify('Give me slug') }} #}

{%- macro slugify(text) -%}
  {%- set output = text %}
  {%- set output = output | string | lower | safe | striptags -%}
  {%- set output = output | regex_replace("[\t\n\f\r ]", "-") -%}
  {%- set output = output | regex_replace("[^0-9A-Za-z_-]", "") -%}
  {%- set output = output | regex_replace("-+", "-") -%}
  {%- set output = output | regex_replace("^[-]", "") -%}
  {%- set output = output | regex_replace("[-]$", "") -%}
  {{- output -}}
{%- endmacro -%}

Using Macros

Import and use macros in your templates or modules:
{% from '../macros/slugify.html' import slugify %}
{% from '../macros/unique-id.html' import unique_id %}

<div id="{{ unique_id() }}">
  <h2>{{ slugify(content.name) }}</h2>
</div>

Sections

Sections are drag-and-drop layout components that use HubSpot’s DnD (Drag and Drop) areas.

Section Example

<!--
  templateType: section
  label: Hero banner
  isAvailableForNewContent: true
  screenshotPath: ../images/section-previews/hero-banner.png
  description: "Full size background image section"
-->
{% dnd_section
  background_color={{ context.background_color or "#f8fafc" }},
  vertical_alignment="MIDDLE"
%}
  {% dnd_module
    path="@hubspot/linked_image",
    img={
      "alt": context.image_alt or "Hero Image",
      "src": context.image or "default-image.jpg"
    },
    offset=0,
    width=6
  %}
  {% end_dnd_module %}
  
  {% dnd_column offset=6, width=6 %}
    {% dnd_row %}
      {% dnd_module
        path="@hubspot/rich_text"
        html={{ context.content or "<h1>Welcome</h1>" }}
      %}
      {% end_dnd_module %}
    {% end_dnd_row %}
  {% end_dnd_column %}
{% end_dnd_section %}

Available Sections

  • hero-banner.html - Hero section with image and content
  • call-to-action.html - CTA section
  • cards.html - Card grid layout
  • multi-column-content.html - Multi-column layouts
  • row-content-img-left.html - Content with left image
  • row-content-img-right.html - Content with right image
  • pricing.html - Pricing section
Sections provide pre-built layouts that content editors can customize without touching code. They’re perfect for creating flexible page builders.

How They Work Together

1

Templates provide structure

Base templates (layouts/base.html) define the overall HTML structure with header, main content area, and footer.
2

Partials handle common elements

Header and footer partials are included using {% global_partial %} to ensure consistency across pages.
3

Sections create page layouts

Drag-and-drop sections allow editors to build page layouts without coding.
4

Modules add content components

Individual modules can be added to sections or directly to pages for specific functionality.
5

Macros provide utilities

Macros are imported and used throughout templates and modules for common operations.

Best Practices

Keep modules focused on a single purpose. If a module is becoming too complex, consider breaking it into multiple smaller modules.

Module Organization

  • One component per module
  • Clear, descriptive names
  • Consistent field naming
  • Include module icons for better UX

Macro Guidelines

  • Document parameters clearly
  • Provide usage examples
  • Keep macros pure (no side effects)
  • Use descriptive variable names

Template Structure

  • Use template inheritance
  • Keep partials DRY
  • Document block usage
  • Consistent naming conventions

Section Design

  • Mobile-first responsive design
  • Provide context defaults
  • Include preview screenshots
  • Clear descriptions for editors

Project Structure

Understand the directory organization

Build Process

Learn about the compilation pipeline

Build docs developers (and LLMs) love