BFJ Media · PROJECT LOBSTER FOR hUBSPOT

HubSpot Figma-to-Code Pipeline

Standard flow for building HubSpot websites from Figma designs — from file prep to published page, with minimal manual work.

Audience Any HubSpot developer at BFJ
Goal Build a theme, modules, and pages from Figma, automated
Time split ~80% automated / ~20% polish

Overview

This document is the standard flow for building a new HubSpot CMS website from a Figma design using the BFJ automated pipeline. It defines the seven phases every project goes through, what happens in each, who owns it, and the rules that keep the process consistent across projects.

Follow the phases in order. Don't skip. The automation is deterministic only if every step is done correctly.

1 Figma Prep
2 Bootstrap
3 Image Pipeline
4 Generate Modules
5 Sync Assets
6 Draft Page
7 QA & Publish

Before You Start

These are the tools and access you'll need for every new project. Set them up once and you're ready to go.

Tool / Access Purpose
Figma file (with edit access) Source of truth for the design
Figma Personal Access Token file_content:read scope — for the Figma REST API
HubSpot portal + hs CLI Where the theme lives and is synced
HubSpot Private App Token With content and files scopes
Node.js 18+ For the image sync script (built-in fetch)
Claude Code CLI Runs the automation, generates modules, creates pages
GitHub repo access Canonical reference documents and the image sync script — clone or reference via raw URLs

The Pipeline — 7 Phases

01

Prepare the Figma File

Owner: Designer & Project Lead
Set up the design file so the automation can read it deterministically. This is the most important phase — garbage in, garbage out.
  • Build a dedicated Style Guide page — define colour tokens, spacing scale, typography, radius, and shadows as Figma local variables
  • Name every top-level section frame using kebab-case module names (e.g. hero-banner, service-cards, image-text-list)
  • Annotate layers with type hints in square brackets: [text], [richtext], [image], [link], [boolean], [number], [choice]
  • Mark repeater groups with an asterisk: cards* [group]
  • Use Figma auto-layout consistently — it maps directly to CSS flexbox
  • Group reusable elements (buttons, cards, badges) as Figma components
  • Each website page lives on its own Figma page, with sections stacked vertically in a single parent frame
!
Non-negotiable If the Figma file doesn't follow the naming convention, push it back to the designer before building. Don't try to fix a broken file from the dev side.
Reference: figma-naming-convention-spec.md
02

Bootstrap the HubSpot Project

Owner: Developer
Spin up the empty theme in HubSpot Design Manager and start the file sync that stays running for the rest of the project.
  1. Clone the HubSpot CMS boilerplate:
    git clone https://github.com/HubSpot/cms-theme-boilerplate.git ProjectName
    cd ProjectName
    git init && git remote set-url origin <client-repo-url>
  2. Authenticate the HubSpot CLI against the client's portal:
    hs account auth --account=<portal-id>
  3. Start hs cms watch in a separate terminal window and keep it running for the entire project:
    hs cms watch src ProjectName --account=<portal-id>
    Run this alongside Claude Code — split your terminal so you can see both. Every file change Claude makes syncs to HubSpot automatically. You'll never run hs cms upload manually.
  4. Verify the ProjectName/ folder is live in HubSpot → Marketing → Files and Templates → Design Tools
  5. Confirm the HubSpot Private App token has content and files scopes
Never run hs cms upload It's unnecessary when watch is running and can cause sync conflicts. Watch handles everything from this point forward.
03

Set Up the Image Asset Pipeline

Owner: Developer
Wire up the standardized image sync script so image field defaults populate automatically from Figma — no manual uploads, ever.
  1. Copy figma-sync-images.js from the BFJ pipeline repo into the project's scripts/ folder
  2. Create figma-sync.config.json at the project root with figmaFileKey and defaultFolder
  3. Commit an empty .figma-hubspot-manifest.json (or let the script create it on first run)
  4. Set environment variables:
    export FIGMA_TOKEN="figd_..."
    export HUBSPOT_TOKEN="pat-ap1-..."
  5. Smoke test: node scripts/figma-sync-images.js help
  6. Commit all three files to Git — yes, including the manifest
Windows Git Bash gotcha Prefix every script invocation with MSYS_NO_PATHCONV=1 or the --folder=/... flag gets mangled into a Windows path. Native PowerShell and macOS/Linux shells are unaffected.
Reference: figma-to-hubspot-automation.md → Image Asset Pipeline
04

Generate Modules from Figma

Owner: Developer + Claude
Claude reads the Figma file and generates each top-level section as a HubSpot module. Because hs cms watch is running, every file Claude writes syncs to HubSpot instantly.
!
Always consult the HubSpot Fields Reference first Before generating any module, Claude must read hubspot-fields-reference.md and familiarise itself with the valid field types, their exact property names, and default value shapes. HubSpot is strict — field types like video, videoplayer, form, file, embed, menu, simplemenu, link, cta, backgroundimage, icon and others each have their own required default structure, and misspelling a type or property key will cause silent upload failures or broken editor UI. Never guess field types — look them up every time.

For every top-level Figma frame, four files are produced:

  • fields.json — editable fields with defaults pulled from the Figma content
  • module.html — HubL template rendering the section
  • module.css — scoped styles
  • meta.json — with content_types array so the module is available on pages

Local Module Standard — Two-Tab Structure

Every Local Module must follow this structure. Global modules (header, footer, nav) are exempt — they have their own field shapes.

Content Tab:

  • Layout dropdown as the first field — only if the design has layout variations
  • Section-specific fields extracted from the Figma frame
  • section_id text field as the last field — drives anchor / jump links

Style Tab (mandatory, identical on every Local Module):

  • heading_tag — H1 / H2 / H3 / H4
  • background_type — Color / Image
  • background_color — dynamic choices from Style Guide colour tokens
  • background_image — visible only when type = Image
  • padding_top / padding_bottom — dynamic choices from Style Guide spacing scale
hubspot-fields-reference.md — all field types & properties
figma-to-hubspot-automation.md → Module Field Standards
05

Sync Images & Assets

Owner: Developer + Claude
Pull every image from Figma into HubSpot File Manager and patch the permanent URLs into module defaults — fully automated.
  1. Claude builds a sync plan listing every image node and its target module folder
  2. Run the batch sync:
    node scripts/figma-sync-images.js batch sync-plan.json
  3. The script exports from Figma → uploads to HubSpot File Manager → returns permanent CDN URLs → writes to the manifest
  4. Permanent HubSpot URLs are patched into each module's fields.json as src defaults

3-Tier Asset Strategy

Tier Destination Used for
Tier 1 src/images/ in the theme Logos, reusable brand icons — 10–20 foundational assets
Tier 2 HubSpot File Manager (via script) Hero images, card photos, design-specific imagery
Tier 3 Inline SVG in module.html Small icons: chevrons, social glyphs, play buttons
Idempotent by design The manifest means re-runs are safe. Running the pipeline twice on the same frame won't duplicate assets or break state.
06

Create the Draft Page

Owner: Developer + Claude
With modules live in HubSpot (synced via the running watch), Claude creates the actual page programmatically.
  1. Claude calls the HubSpot Pages API (POST /cms/v3/pages/site-pages)
  2. Each module is placed in layoutSections.dnd_area.rows with default field values
  3. The page is created as a draft — never auto-published
  4. Claude returns the draft preview URL
i
Everything is draft Pages are always created as drafts. Never let the automation auto-publish. Human review is the safety gate.
07

QA, Review & Publish

Owner: Developer + Project Lead
The ~20% manual step — make sure the draft matches the Figma design exactly before the client sees it. This phase starts with an automated QA pass, then manual review.

Step 1 — Automated QA Parity Check

Run the dedicated QA skill on every draft page. It catches the obvious deviations so human review can focus on judgment calls.

BFJ Claude Code Skill
/qa-figma-parity

Takes the Figma frame URL and the HubSpot draft page URL, then:

  • Screenshots the Figma frame at desktop / tablet / mobile breakpoints
  • Screenshots the HubSpot draft page at the same breakpoints
  • Runs a visual diff comparison between the two
  • Flags spacing, colour, typography, image, and layout deviations
  • Returns a severity-ranked QA report (critical / moderate / minor)

Step 2 — Manual Review

  • Review the QA report and fix any critical deviations
  • Check responsive breakpoints manually — edge cases the diff might miss
  • Verify content defaults look right in the HubSpot page editor
  • Review copy, alt text, SEO meta tags, accessibility (keyboard nav, contrast)
  • Test interactive elements: dropdowns, forms, toggles, play buttons

Step 3 — Client Approval

  • Share the draft URL with the client
  • Collect feedback and apply revisions
  • Re-run the QA skill after any significant change

Step 4 — Publish

  • Move pages from draft to published in HubSpot
  • Monitor the first 24 hours for any issues
  • Short training session with the client on editing modules
i
The QA skill is your first checkpoint, not your only one Automated diff catches most visual issues but can't judge content intent, typography balance, or brand feel. Always follow it with human review.

Important Notes

Rules and gotchas to keep in mind throughout the project. Most of these come from real incidents on previous builds.

01
Naming convention is non-negotiable The automation is deterministic only if the Figma file follows the convention. If there's no naming convention, Claude will just provide all based on the default assumptions.
02
Commit the manifest .figma-hubspot-manifest.json is authoritative state. Without it, re-runs upload duplicates.
03
Never skip Phase 1 Spend real time on the Figma prep. Everything downstream depends on it being right.
04
Tokens need files scope If the sync script errors 401/403, the token is missing the Files scope. Add it and the change applies instantly — no rotation needed.
05
Everything is draft Pages are always created as drafts. Never auto-publish from the automation.
06
Re-running is safe The manifest guarantees idempotency. Running the pipeline twice on the same frame is a no-op for already-synced assets.
07
One frame = one module Strict rule. One top-level Figma frame maps to exactly one HubSpot module. Don't combine.
08
Reserved field names Never use body, label, name, title, type, id, required. Prefix instead: card_body, nav_label.
09
Always read the fields reference Before creating modules, Claude must consult hubspot-fields-reference.md to verify field types and default shapes. Don't guess — video vs videoplayer, link vs url, and image vs backgroundimage all have different structures.
10
Local vs Global modules Header, footer and nav partials are Global — exempt from the Style Tab standard. Everything else is Local and must follow it.
11
Never run hs cms upload Watch handles all sync. Manual upload can cause conflicts. Keep watch running in a separate terminal.
12
Accuracy improves with repetition The first page of a new project needs more manual cleanup than the second and third. Claude learns each project's patterns as it goes.
13
Human review is not optional No matter how well the automation runs, every module and page gets a developer review before the client sees it. QA skill first, then eyes.

Reference Documents

All detailed specs and tooling live in the BFJ pipeline repo: github.com/bfj-jess/hubspot-figma-to-code. Bookmark it — every dev on the team should have easy access.

Document Purpose
figma-to-hubspot-automation.md The full pipeline spec — deep technical reference this document summarises
figma-naming-convention-spec.md Figma layer naming rules. Required reading for designers.
hubspot-fields-reference.md All HubSpot field types with syntax and examples
scripts/figma-sync-images.js Canonical image sync script. Copy into each new project.

Questions or gotchas you can't resolve? Ask in the BFJ dev channel before improvising. Keep the pipeline consistent — that's how it gets faster with every project.