Skip to content

Catalog and previews#

Two generated artifacts describe every module: a catalog to read and a preview site to look at. Regenerate both after any module change.

In Claude Code

The hubspot-audit skill of the bb-hubspot plugin covers the catalog. build-catalog.py is a plugin script; the preview generator is a site tool.

The catalog#

python3 $BBH/scripts/build-catalog.py      # → MODULES.md + module-catalog.json

The script walks every .module folder in the theme. For each module it records:

  • the path, the label, and the module_id from meta.json (the same on every portal)
  • the full field tree: types, choices, defaults, repeaters
  • the root CSS classes the template emits (the same hooks the crawler matches on)
  • its CSS and JS footprint
  • its live usage count, from reports/site-crawl.json

Hand-written notes live in catalog-annotations.json (its path is catalog.annotations in hubspot-site.json), keyed by module path: purpose, good for, avoid, notes, bugs. Edit the annotations there, never the generated files.

The summary table at the top of MODULES.md is sorted by live usage, and it tells you which modules matter. On the first site, three modules carried most of the pages and a dozen had no uses at all. Write annotations for the used ones first.

The preview site#

python3 $BBH/scripts/serve-drafts.py 8815    # → http://127.0.0.1:8815/drafts/previews/
python3 tools/build-previews.py                # the site's own generator; full rebuild with screenshots
python3 tools/build-previews.py --only cards   # one page while iterating on a module

build-previews.py is a site tool: the HubL shims carry over from an existing site, and the examples are written for this one. It writes one style-reference page per module into drafts/previews/, plus an index with a screenshot of each module.

  • Real markup. Every example is rendered from the module's own module.html through a small HubL-to-Jinja2 shim. Field values are the fields.json defaults plus that example's overrides, so the markup and CSS are exactly the module's own.
  • Settings chips. Under each example, chips list the Content and Style tab settings that produce it, with defaults marked. A reviewer can copy them straight into the editor.
  • Self-contained. The script copies the theme CSS, JS and fonts into drafts/_kit/vendor/, so the folder can be zipped or hosted on its own.
  • Examples are code. Each example is a module_page(...) call in the generator, with sample copy and placeholder images. Adding a variant means adding a call.

Shims you will need on any site#

The shim is a set of regex rewrites in hubl_to_jinja(), plus a few filters Jinja2 doesn't have (truncatehtml, regex_replace, cut, HubL's round).

HubL Preview stand-in
{% form %} A static mock form
{% video_player %} A mock 16:9 player panel
{% menu %} The live nav markup, captured as fixtures in tools/fixtures/
{% cta %} A plain link with the theme's CTA class
{% related_blog_posts %} and blog modules Sample post data
require_css / require_js / require_head blocks Removed; the vendored CSS and JS are linked instead
inline_rich_text The value, printed
get_asset_url A path into drafts/_kit/vendor/

Showcase pages: previews in the real editor#

Once a module is on the dev portal, tools/build-showcase.py <module> turns its preview variants into a draft page with one drag-and-drop section per variant, using real File Manager and CDN assets. Publish it with the normal publisher, and reviewers can open each variant in the actual HubSpot editor. Each variant's caption goes in HubSpot's default rich-text module, outside the module being tested.

python3 tools/build-showcase.py <client>-hero      # → drafts/<client>-hero-showcase.html (+ pages.json entry)
python3 $BBH/scripts/publish-page.py <client>-hero-showcase