HubSpot CMSの現代標準であるテーマ開発を完全解説。theme.json によるデザイントークン設定・ドラッグ&ドロップエディタの構造設計・クライアントが安全に編集できる構造の作り方まで体系的に整理します。
HubSpotのテーマ(Theme)は、サイトを構成するテンプレート・モジュール・CSS・JS・画像を ひとまとまりのパッケージとして管理する仕組みです。 2021年頃から強く推奨されるようになり、現在はテーマ構成が開発のデファクトスタンダードになっています。
非テーマ構成の既存案件をリニューアルする場合、テーマ構成への移行を検討してください。 ただし、非テーマ → テーマへの移行は単純ではなく、テンプレートのパス参照・モジュールの配置場所が変わるため、 フルリニューアルのタイミングでの全面移行か、 既存構成を維持したまま部分的に改善するかを工数と見合わせて判断します。
テーマは1つのルートディレクトリ以下にすべてのファイルをまとめます。
CLIの hs create theme テーマ名 コマンドで基本構成を自動生成できます。
hs create theme my-theme を実行すると、HubSpotが提供するデフォルトの雛形テーマが生成されます。
これをそのまま使うのではなく、不要なファイルを削除し、プロジェクトの要件に合わせた最小構成に整理してから使い始めることを推奨します。
デフォルト雛形はHubSpotが提供するデモ用コンテンツが含まれており、実案件ではノイズになります。
theme.json はテーマの核心ファイルです。
ここで定義したデザイントークン(カラー・フォント・スペーシング・角丸等)が、
HubSpotの管理画面に「テーマ設定」として表示され、
マーケターがコードなしでサイト全体のデザインを変更できます。
{
"label" : "My Company Theme",
"preview_path": "./templates/home.html",
// テーマ設定プレビューに使うテンプレート
"settings": {
// ===== カラーパレット =====
"color": {
"label" : "カラーパレット",
"fields": [
{
"name" : "primary_color",
"label" : "プライマリカラー",
"type" : "color",
"default": { "color": "#0052CC", "opacity": 100 }
},
{
"name" : "secondary_color",
"label" : "セカンダリカラー",
"type" : "color",
"default": { "color": "#FF5630", "opacity": 100 }
},
{
"name" : "accent_color",
"label" : "アクセントカラー",
"type" : "color",
"default": { "color": "#36B37E", "opacity": 100 }
},
{
"name" : "bg_color",
"label" : "背景色(サイト全体)",
"type" : "color",
"default": { "color": "#FFFFFF", "opacity": 100 }
},
{
"name" : "text_color",
"label" : "テキストカラー(本文)",
"type" : "color",
"default": { "color": "#2D3748", "opacity": 100 }
}
]
},
// ===== タイポグラフィ =====
"typography": {
"label" : "フォント設定",
"fields": [
{
"name" : "heading_font",
"label" : "見出しフォント",
"type" : "font",
"default": {
"font" : "Noto Sans JP",
"font_set" : "GOOGLE",
"size" : "40px",
"size_unit" : "px",
"bold" : true,
"italic" : false,
"underline" : false,
"color" : "#1A202C",
"line_height" : "1.3",
"letter_spacing": "0"
}
},
{
"name" : "body_font",
"label" : "本文フォント",
"type" : "font",
"default": {
"font" : "Noto Sans JP",
"font_set" : "GOOGLE",
"size" : "16px",
"size_unit" : "px",
"bold" : false,
"color" : "#4A5568",
"line_height" : "1.85"
}
}
]
},
// ===== スペーシング =====
"spacing": {
"label" : "スペーシング",
"fields": [
{
"name" : "section_vertical_spacing",
"label" : "セクション上下余白",
"type" : "spacing",
"default": {
"top" : "80px",
"bottom": "80px"
}
},
{
"name" : "container_max_width",
"label" : "コンテナ最大幅",
"type" : "number",
"default": 1200,
"suffix" : "px"
}
]
},
// ===== ボーダー・シャドウ =====
"border": {
"label" : "ボーダー・角丸",
"fields": [
{
"name" : "border_radius",
"label" : "ボーダーラジウス(角丸)",
"type" : "number",
"default": 6,
"suffix" : "px"
},
{
"name" : "button_border_radius",
"label" : "ボタンの角丸",
"type" : "number",
"default": 4,
"suffix" : "px"
}
]
}
}
}
theme.json で定義した値は、テンプレートやモジュールから
theme.settings.カテゴリ名.フィールド名 の形式で参照できます。
{# テーマのカラーをインラインスタイルで使う #} <section style="background-color: {{ theme.settings.color.bg_color.color }};"> {# フォント設定を <head> のCSS変数として出力する(推奨パターン)#} <style> :root { --color-primary : {{ theme.settings.color.primary_color.color }}; --color-secondary : {{ theme.settings.color.secondary_color.color }}; --color-accent : {{ theme.settings.color.accent_color.color }}; --color-text : {{ theme.settings.color.text_color.color }}; --color-bg : {{ theme.settings.color.bg_color.color }}; --font-heading : '{{ theme.settings.typography.heading_font.font }}', sans-serif; --font-body : '{{ theme.settings.typography.body_font.font }}', sans-serif; --font-size-body : {{ theme.settings.typography.body_font.size }}; --line-height-body: {{ theme.settings.typography.body_font.line_height }}; --spacing-section : {{ theme.settings.spacing.section_vertical_spacing.top }}; --container-width : {{ theme.settings.spacing.container_max_width }}px; --border-radius : {{ theme.settings.border.border_radius }}px; --border-radius-btn: {{ theme.settings.border.button_border_radius }}px; } </style>
theme.json の設定値をCSS変数として出力し、CSSファイルではCSS変数を参照する設計にすることで、
マーケターが管理画面のテーマ設定を変更するだけでサイト全体のデザインが更新されます。
直接インラインスタイルに設定値を書くより保守性が大幅に上がります。
variables.css を用意してグローバルCSSでCSS変数を読み込む構成が定番です。
{# theme.json で font_set: "GOOGLE" に設定したフォントを自動読み込み #} {# HubSpotがstandard_header_includesで自動読み込みする場合もある #} {# 手動で制御したい場合は以下のように記述 #} {% set heading_font = theme.settings.typography.heading_font %} {% set body_font = theme.settings.typography.body_font %} {% if heading_font.font_set == "GOOGLE" %} <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family= {{- heading_font.font|replace(" ", "+") -}} :wght@400;700;900&display=swap"> {% endif %}
HubSpotのドラッグ&ドロップエディタ(DnDエディタ)は、 マーケターがコードなしでページの構成を変更できる仕組みです。 開発者は「どの範囲を編集可能にするか」を設計し、 マーケターはその範囲内でモジュールを配置・並び替えします。
| レイヤー | HubLタグ | 役割 | 編集者の操作 |
|---|---|---|---|
| dnd_area | {% dnd_area %} | 編集可能な最上位コンテナ。テンプレートに複数配置可能。 | 操作なし(開発者がテンプレートで定義) |
| dnd_section | {% dnd_section %} | 横幅いっぱいのセクション。背景色・パディングを設定できる。 | 追加・削除・並び替え・背景色設定 |
| dnd_column | {% dnd_column %} | セクション内の列。幅の比率をパーセントで指定。 | 幅の調整・列の追加・削除 |
| dnd_row | {% dnd_row %} | 列内の行。モジュールを縦方向に並べる単位。 | 行の追加・削除 |
| dnd_module | {% dnd_module %} | 実際のコンテンツパーツ(カスタムまたは標準モジュール)。 | 追加・削除・並び替え・フィールド編集 |
{% block main_content %} {# ====== シンプルなDnDエリア ====== #} {% dnd_area "main_area" label="メインコンテンツエリア" %} {# デフォルトのセクション・モジュール構成をここに定義 #} {% dnd_section vertical_alignment="TOP" padding='{"top": "80px", "bottom": "80px"}' %} {% dnd_column width=12 %} {# 12/12 = 100% #} {% dnd_row %} {% dnd_module "hero" path="../modules/hero-banner.module" label="ヒーローバナー" %} {% end_dnd_module %} {% end_dnd_row %} {% end_dnd_column %} {% end_dnd_section %} {% end_dnd_area %} {% endblock %}
{% dnd_area "content_area" label="コンテンツエリア" %} {% dnd_section %} {# メインコンテンツ:8/12 = 66.6% 幅 #} {% dnd_column width=8 %} {% dnd_row %} {% dnd_module "main_content" path="@hubspot/rich_text" label="本文" %} {% end_dnd_module %} {% end_dnd_row %} {% end_dnd_column %} {# サイドバー:4/12 = 33.3% 幅 #} {% dnd_column width=4 %} {% dnd_row %} {% dnd_module "sidebar_recent" path="../modules/recent-posts.module" label="最新記事" count=5 %} {% end_dnd_module %} {% end_dnd_row %} {% dnd_row %} {% dnd_module "sidebar_cta" path="../modules/cta-banner.module" label="サイドバーCTA" %} {% end_dnd_module %} {% end_dnd_row %} {% end_dnd_column %} {% end_dnd_section %} {% end_dnd_area %}
{% dnd_section {# 縦方向の配置 #} vertical_alignment="MIDDLE" {# TOP / MIDDLE / BOTTOM #} {# パディング(JSON文字列で指定) #} padding='{"top":"80px","bottom":"80px","left":"0px","right":"0px"}' {# 背景色(theme.jsonの色を参照可) #} background_color='{"color":"#f7fafc","opacity":100}' {# 背景画像 #} background_image='{"src":"https://example.com/bg.jpg","size_type":"cover","position":"center center"}' {# 最大幅(コンテナを持つかどうか) #} max_width="1200px" {# カラム間のガター幅 #} margin='{"top":"0px","bottom":"0px"}' %}
HubSpotのDnDエディタは12カラムグリッドを採用しています。
width=12 で100%、width=6 で50%、width=4 で33.3%、width=3 で25%になります。
デフォルトの構成コードでこの値を設定しておくと、マーケターがページを作る際の初期レイアウトとして表示されます。
HubSpot開発で最も重要な設計判断のひとつが、「どこをクライアントに触らせるか」です。 自由度が高すぎるとデザインが崩れ、低すぎると使い勝手が悪くなります。 適切な制御設計が、長期的な保守品質を左右します。
テンプレートに直接記述。DnDエリアなし。開発者のみ変更可能。ヘッダー・フッター・テンプレートの骨格。
モジュールとして配置し位置は固定。テキスト・画像・リンクの内容は変更可。構成は変えられない。
DnDエリア内に配置。モジュールの追加・削除・並び替え・スタイル変更が可能。最大の自由度。
{% extends "./layouts/base.html" %} {% block main_content %} {# ① 完全固定:ヒーローバナーは位置もデザインも固定 #} {# (位置はテンプレートで固定、内容はフィールド編集のみ可) #} {% module "hero" path="../modules/hero-banner.module" label="トップヒーロー" %} {# ② 固定配置 + フィールド編集:最新記事は常にここにある #} {% module "recent_posts" path="../modules/recent-posts.module" label="最新記事(件数・タグは変更可)" %} {# ③ DnDエリア:この範囲内はマーケターが自由に組み替えられる #} {% dnd_area "flexible_area" label="自由編集エリア(モジュールの追加・削除・並び替え可)" %} {% dnd_section %} {% dnd_column width=12 %} {% dnd_row %} {% dnd_module "default_cta" path="../modules/cta-banner.module" %} {% end_dnd_module %} {% end_dnd_row %} {% end_dnd_column %} {% end_dnd_section %} {% end_dnd_area %} {# ④ 完全固定:フォームセクションは常に最下部に固定 #} {% module "contact_form" path="../modules/form-section.module" label="お問い合わせフォーム" %} {% endblock %}
デフォルトではDnDエリアにHubSpot標準モジュールを含むすべてのモジュールを配置できますが、
allowed_modules を設定することで特定のモジュールのみに制限できます。
クライアントが意図しないモジュールを配置してデザインが崩れるのを防ぐ重要な設定です。
{% dnd_area "restricted_area" label="コンテンツエリア(使用可能モジュールを限定)" %} {# ※ HubSpot CLIバージョンによってはtheme.jsonで設定する場合もある #} {# 使用可能モジュールの制限はtheme.jsonのmodules_allowed設定で行う #} {% end_dnd_area %}
{
"label": "My Theme",
"settings": { /* ... */ },
// DnDエディタで許可するモジュールを制限
"modules_allowed": [
"../modules/hero-banner.module",
"../modules/card-grid.module",
"../modules/cta-banner.module",
"../modules/recent-posts.module",
"../modules/faq.module",
"../modules/testimonials.module",
"@hubspot/rich_text", // HubSpot標準:リッチテキスト
"@hubspot/linked_image", // HubSpot標準:リンク付き画像
"@hubspot/video" // HubSpot標準:動画
// ← ここに列挙したモジュールのみDnDエリアで使用可能になる
]
}
モジュールの制限はクライアントの誤操作を防ぐ有効な手段ですが、 制限しすぎるとマーケターが必要なコンテンツを作れなくなります。 納品前にクライアントと「何を編集したいか」をすり合わせた上で適切な制限範囲を決めることが重要です。
モジュールのフィールドには通常の「コンテンツフィールド」に加え、 スタイルセクション(Style Fields)を定義できます。 これにより、モジュールの見た目をGUIで変更できる「デザイン設定パネル」を作れます。
[ // === コンテンツフィールド(通常) === { "name" : "title", "label" : "見出し", "type" : "text", "default": "セクションタイトル" }, // === スタイルフィールド("tab": "STYLE" を指定)=== { "name" : "bg_color", "label" : "背景色", "type" : "color", "tab" : "STYLE", // ← これがスタイルタブに表示されるキー "default" : { "color": "#ffffff", "opacity": 100 } }, { "name" : "text_color", "label" : "テキストカラー", "type" : "color", "tab" : "STYLE", "default" : { "color": "#2d3748", "opacity": 100 } }, { "name" : "padding", "label" : "上下パディング", "type" : "spacing", "tab" : "STYLE", "default" : { "top": "64px", "bottom": "64px" } }, { "name" : "layout_style", "label" : "レイアウト", "type" : "choice", "tab" : "STYLE", "choices" : [["default","標準"],["wide","ワイド"],["narrow","ナロー"]], "default" : "default" } ]
<section class="section-block section-block--{{ module.layout_style }}" style=" background-color: {{ module.bg_color.color }}; color: {{ module.text_color.color }}; padding-top: {{ module.padding.top }}; padding-bottom: {{ module.padding.bottom }}; "> <div class="container"> <h2>{{ module.title }}</h2> </div> </section>
コンテンツフィールド:テキスト・画像・リンクなど「何を表示するか」に関するもの
スタイルフィールド:背景色・パディング・レイアウトなど「どう見せるか」に関するもの
この分離によって管理画面の編集パネルが「コンテンツ」と「スタイル」の2タブに整理され、
編集者が直感的に操作できるようになります。
ただし、スタイルフィールドをつけすぎると管理が煩雑になるため、
変更頻度が高い設定のみスタイルフィールド化することを推奨します。
| ステップ | 作業内容 | ポイント |
|---|---|---|
| ① 雛形生成 | hs create theme テーマ名 で雛形を生成し、不要ファイルを整理する |
デモ用コンテンツを削除して最小構成からスタート |
| ② theme.json 設計 | デザインシステムのカラー・フォント・スペーシングをtheme.jsonに定義する | デザインカンプのトークンをそのまま転記する意識で |
| ③ CSS変数連携 | base.html で theme.json → CSS変数を出力するコードを実装する | 以降のCSSはすべてCSS変数を参照して書く |
| ④ base.html 実装 | ベーステンプレートにhead・ヘッダー・フッター・共通ブロックを実装する | standard_header/footer_includes を忘れない |
| ⑤ グローバルパーシャル作成 | header.html・footer.html のグローバルパーシャルを実装する | モジュールで編集可能にする部分を明確に設計する |
| ⑥ テンプレート実装 | 各テンプレートを base.html を継承して実装する(page / blog-* / landing-page) | DnDエリアの制御設計をここで固める |
| ⑦ カスタムモジュール開発 | デザインカンプの各セクションをモジュールとして実装する | 第4章のパターン集を活用して効率化する |
| ⑧ クライアント確認 | テストアカウントでDnDエディタを実際に操作して使用感を確認する | マーケターの視点で編集のしやすさを検証する |
| ⑨ 本番デプロイ | hs upload src/ @my-theme --portal prod で本番に反映する |
事前に hs lint でエラーがないことを確認する |
theme.json:カラー・フォント・スペーシングの設定値が確認され、管理画面のテーマ設定パネルで正しく表示されるか
CSS変数:theme.jsonの変更がサイト全体に反映されるか(カラーを変えてみて確認)
全テンプレート:standard_header/footer_includes が抜けていないか
DnDエリア:マーケターが実際に操作して意図したように動くか
モジュール制限:allowed_modules の設定が要件と合っているか
グローバルパーシャル:1か所の変更が全ページに反映されるか確認したか
新規案件はすべてテーマ構成で開発する。非テーマ構成はレガシー。テーマにより theme.json・DnDエディタ・マーケットプレイス配布が使えるようになる。
theme.json の設定値を base.html でCSS変数として出力し、CSS全体はCSS変数で参照する設計が定番。マーケターがテーマ設定を変えるだけで全体に反映される。
dnd_area → dnd_section → dnd_column(12グリッド)→ dnd_row → dnd_module の階層を理解して設計する。セクションの背景・パディングはJSON文字列で指定。
完全固定(テンプレート直書き)/ フィールド編集のみ(module配置)/ 自由編集(dnd_area)の3レベルをクライアントの要件に合わせて使い分ける。
theme.json の modules_allowed で使用可能モジュールを制限する。制限しすぎず、クライアントとすり合わせた上で適切な範囲を設定する。
フィールドに "tab": "STYLE" を付けると編集パネルの「スタイル」タブに表示される。コンテンツとスタイルを分離することで編集UIが整理される。