HubSpotでカスタムモジュールを作成する方法 - 完全ガイド2026

ラピッドチーム

HubSpot CMSを使用している場合、カスタムモジュールは習得すべき最も重要なスキルです。クリーンで高速、かつ保守しやすいコードを保ちながら、コンテンツ編集者にページコンテンツの完全な制御を提供します。

しかし、ここに問題があります — ほとんどのHubSpot開発者はモジュールを間違った方法で構築しています。肥大化したCSSを書き、フィールドのグループ化をスキップし、見出しタグをハードコードし、パフォーマンスを完全に無視しています。その結果はどうなるでしょうか?ページの読み込みが遅くなり、編集者は不満を抱き、誰も保守したくないような乱雑なコードになってしまいます。

このガイドでは、HubSpotで本番環境で使えるカスタムモジュールを正しい方法で構築するために必要なすべてを網羅しています。実際のコード例、適切なフィールド構造、HubLのベストプラクティス、そして実際に役立つパフォーマンス向上のヒントを紹介します。

HubSpotのカスタムモジュールを理解する

カスタムモジュールは、デザインマネージャー内の4つのファイルで構成される再利用可能なコンテンツブロックです:

  • module.html — 動的コンテンツをレンダリングするHubLタグを含むHTMLテンプレート
  • fields.json — コンテンツ編集者が確認し、操作するフィールドを定義します
  • module.css — このモジュールにスコープされたオプションのスタイル
  • meta.json — ラベル、アイコン、このモジュールを使用できるテンプレートタイプなどの構成

レゴブロックを組み立てるようなものだと考えてください。すべての適切な接続ポイントを備えたものを一度設計すれば、コンテンツ編集者はコードに一切触れることなく、任意のページの任意のセクションでそれを使用できます。

meta.jsonを正しい方法で設定する

何かを構築する前に、モジュールを適切に設定してください。以下は、ヒーローセクションモジュール用のクリーンな meta.json の例です:

{% module_block module "widget_d4a81dde-d631-4355-8e28-1c3a3700539f" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{
  \"label\": \"ヒーローセクション\",
  \"css_assets\": [],
  \"external_js\": [],
  \"global\": false,
  \"help_text\": \"見出し、説明、画像、およびコールトゥアクション(CTA)ボタンを備えた柔軟なヒーローセクション。\",
  \"host_template_types\": [\"PAGE\", \"BLOG_LISTING\", \"BLOG_POST\"],
  \"icon\": \"\",
  \"is_available_for_new_content\": true,
  \"js_assets\": [],
  \"other_assets\": [],
  \"smart_type\": \"NOT_SMART\",
  \"categories\": [\"DESIGN\"]
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

ここで注目すべき点が2つあります。第一に、host_template_types はこのモジュールを使用できる場所を制御します。ページ専用モジュールの場合は PAGE に設定し、ブログテンプレートでも機能させる必要がある場合は BLOG_POST を含めます。モジュールがCSSまたはJSを使用している場合は、電子メールテンプレート用に許可したままにしないでください(電子メールはこれらをサポートしていません)。第二に、常に意味のある help_text を追加してください。これはデザインマネージャーに表示され、チームがコードを読まなくてもモジュールの機能や目的を理解するのに役立ちます。

適切な fields.json 構造の構築

fields.json ファイルは、ほとんどの開発者が失敗する場所です。15以上のフィールドが平坦にリストされていると、編集作業が苦痛になります。関連するフィールドをグループ化することで、クリーンで直感的なインターフェースが作成されます。

セクション設定 — すべてのモジュールにこれが必要

コンテンツフィールドの前に、すべてのモジュールにはセクション設定グループが必要です。これにより、コンテンツ編集者はセクションのID、カスタムクラス、および表示/非表示を制御できます:

{% module_block module "widget_9cda33c3-8e07-4674-96d3-f9af6ca875a0" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"

{
  \"id\": \"section_settings\",
  \"name\": \"section_settings\",
  \"label\": \"セクション設定\",
  \"type\": \"group\",
  \"children\": [
    {
      \"id\": \"section_id\",
      \"name\": \"section_id\",
      \"label\": \"セクションID\",
      \"type\": \"text\",
      \"help_text\": \"アンカーリンク用の一意のIDを追加します。例:hero-section\",
      \"default\": \"\"
    },
    {
      \"id\": \"section_class\",
      \"name\": \"section_class\",
      \"label\": \"カスタムCSSクラス\",
      \"type\": \"text\",
      \"help_text\": \"セクションのラッパーにカスタムTailwindまたはCSSクラスを追加します。\",
      \"default\": \"\"
    },
    {
      \"id\": \"section_visible\",
      \"name\": \"section_visible\",
      \"label\": \"セクションを表示\",
      \"type\": \"boolean\",
      \"help_text\": \"コンテンツを削除せずにこのセクションを非表示にする場合はオフにします。\",
      \"default\": true
    }
  ]
}

"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

これがなぜ重要なのでしょうか?コンテンツ編集者は多くの場合、セクションを一時的に非表示にしたり、ナビゲーション用のアンカーリンクを追加したり、特別なスタイリング用のカスタムクラスを適用したりする必要があります。これらのフィールドがないと、小さな変更のたびに開発者に依頼しなければなりません。これらがあれば、彼らは自立して作業できます。

コンテンツグループ — SEO制御を備えた見出し

見出しタグをハードコードしないでください。編集者がSEOの階層を制御できるように、常に選択フィールドを提供してください:

{% module_block module "widget_7b29c692-a85c-4348-bbd6-6b0d7fe13db4" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{
  \"id\": \"content\",
  \"name\": \"content\",
  \"label\": \"コンテンツ\",
  \"type\": \"group\",
  \"children\": [
    {
      \"id\": \"heading\",
      \"name\": \"heading\",
      \"label\": \"見出し\",
      \"type\": \"text\",
      \"help_text\": \"メインの見出し。SEOの最良の結果を得るために、60文字以内に抑えてください。\",
      \"default\": \"素晴らしいものを構築する\"
    },
    {
      \"id\": \"heading_tag\",
      \"name\": \"heading_tag\",
      \"label\": \"見出しタグ\",
      \"type\": \"choice\",
      \"help_text\": \"H1はページにつき1回だけ使用してください。その他のすべてのセクション見出しにはH2を使用します。\",
      \"choices\": [
        [\"h1\", \"H1 — 主要なページ見出し\"],
        [\"h2\", \"H2 — セクション見出し\"],
        [\"h3\", \"H3 — サブセクション見出し\"]
      ],
      \"default\": \"h2\"
    },
    {
      \"id\": \"description\",
      \"name\": \"description\",
      \"label\": \"説明\",
      \"type\": \"textarea\",
      \"help_text\": \"見出しの下の補足テキスト。1〜2文を推奨します。\",
      \"default\": \"\"
    }
  ]
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

デフォルトの見出しタグが h1 ではなく h2 に設定されていることに注目してください。これは意図的なものです。ほとんどのページには H1 が1つしかなく、それは通常、最初のモジュールによって処理されます。それ以降のすべてのモジュールはデフォルトで H2 になるべきです。編集者がこのモジュールをページの最初のセクションとして配置した場合、彼ら自身で H1 に切り替えることができます。

ボタングループ — CTAではなくリンクフィールドを使用する

よくある間違いは、すべてのボタンにHubSpotのCTAモジュールを使用することです。CTAは強力ですが、トラッキングのオーバーヘッドが追加され、使いすぎるとページの読み込みが遅くなります。ほとんどのボタンにおいて、シンプルなテキストフィールド付きのリンクフィールドがあれば十分です:

{% module_block module "widget_114667bd-6e91-4175-bf55-54dc51a1abe0" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{
  \"id\": \"primary_button\",
  \"name\": \"primary_button\",
  \"label\": \"プライマリボタン\",
  \"type\": \"group\",
  \"children\": [
    {
      \"id\": \"button_text\",
      \"name\": \"button_text\",
      \"label\": \"ボタンテキスト\",
      \"type\": \"text\",
      \"default\": \"はじめる\"
    },
    {
      \"id\": \"button_link\",
      \"name\": \"button_link\",
      \"label\": \"ボタンリンク\",
      \"type\": \"link\",
      \"default\": {
        \"url\": { \"href\": \"#\", \"type\": \"EXTERNAL\" },
        \"open_in_new_tab\": false,
        \"no_follow\": false
      }
    }
  ]
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

リンクフィールドタイプは、URL、新しいタブで開く、nofollow のオプションを編集者に自動的に提供します。これらすべてを、開発者の追加作業なしで実現できます。

フィールドの命名規則

フィールドの命名方法は、あなたが考えている以上に重要です。コンテンツ編集者は、ページを編集するたびにこれらのラベルを目にします。具体的に記述してください:

  • ❌ "画像" — どの画像ですか?背景?プロフィール?ヒーロー?
  • ✅ "ヒーロー背景画像" — すぐに明確になります
  • ❌ "トグル" — 何を切り替えるのですか?
  • ✅ "セクションを表示" または "背景オーバーレイを有効にする" — 何を制御するかを正確に説明しています
  • ❌ "テキスト" — すべてのフィールドがテキストです
  • ✅ "ボタンテキスト" または "小見出し" — 具体的で明白です

また、すべてのフィールドに help_text を追加してください。今書く10単語の説明が、後々の10分間のSlackでの会話を省いてくれます。

モジュール HTML + HubL の記述

それでは、実際の module.html を構築しましょう。ここで、すべてのフィールドが1つのレンダリングされたコンポーネントとして統合されます。

ヒーローセクションの完全な module.html

{% module_block module "widget_a0d634f5-fd08-4e28-ac9d-2d3a76126018" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{%- if module.section_settings.section_visible -%}
<section
  {%- if module.section_settings.section_id %} id=\"{{ module.section_settings.section_id }}\"{% endif -%}
  class=\"relative overflow-hidden {{ module.section_settings.section_class }}\"
>
  <div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20 lg:py-24\">
    <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-12 items-center\">

      <div class=\"flex flex-col gap-6\">

        {%- if module.content.heading -%}
        <{{ module.content.heading_tag }} class=\"text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight text-slate-900\">
          {{- module.content.heading -}}
        </{{ module.content.heading_tag }}>
        {%- endif -%}

        {%- if module.content.description -%}
        <p class=\"text-lg text-slate-600 leading-relaxed max-w-xl\">
          {{- module.content.description -}}
        </p>
        {%- endif -%}

        {%- if module.primary_button.button_text -%}
        <div class=\"flex flex-wrap gap-4 mt-2\">
          <a
            href=\"{{ module.primary_button.button_link.url.href }}\"
            {%- if module.primary_button.button_link.open_in_new_tab %} target=\"_blank\" rel=\"noopener noreferrer\"{% endif -%}
            class=\"inline-flex items-center px-8 py-4 text-base font-semibold text-white bg-blue-600 rounded-xl hover:bg-blue-700 transition-colors\"
          >
            {{- module.primary_button.button_text -}}
          </a>
        </div>
        {%- endif -%}

      </div>

      {%- if module.hero_image.image.src -%}
      <div class=\"relative\">
        <img
          src=\"{{ module.hero_image.image.src }}\"
          alt=\"{{ module.hero_image.image.alt }}\"
          width=\"{{ module.hero_image.image.width }}\"
          height=\"{{ module.hero_image.image.height }}\"
          loading=\"lazy\"
          class=\"w-full h-auto rounded-2xl\"
        >
      </div>
      {%- endif -%}

    </div>
  </div>
</section>
{%- endif -%}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

このコードで使用されている重要なパターンを分解してみましょう。

{%- -%} を使用した空白の制御

上記のコードのすべての HubL タグは、{% %} の代わりにダッシュ構文 {%- -%} を使用しています。この1つの変更が、レンダリングされるHTMLに大きな違いをもたらします。

ダッシュがないと、HubLはタグを処理する場所に空白行を挿入します。ページのソースコードは不要な空白でいっぱいになり、すべての要素の間に空行ができます。これにより、HTMLが重くなり、デバッグが難しくなり、解析もわずかに遅くなります。

ダッシュを使用すると、HubLはその空白をきれいに取り除きます。レンダリングされたHTMLは無駄がなく、最小限で、ブラウザが必要とする通りのものになります。20以上のモジュールがあるページでは、HTMLのファイルサイズを著しく削減できます。

変数の出力にも同じことが言えます。動的な値の周囲に空白ができないように、{{ }} の代わりに {{- -}} を使用してください。

動的な見出しタグ

見出しは、フィールドの値を直接HTMLタグとして使用します:

{% module_block module "widget_8e0e0819-0d9f-420c-b36f-12c46a3708d2" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"<{{ module.content.heading_tag }}>
  {{- module.content.heading -}}
</{{ module.content.heading_tag }}>"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

編集者がドロップダウンから「H2」を選択すると、これは適切な <h2> タグとしてレンダリングされます。「H1」を選択すると、<h1> としてレンダリングされます。編集者は開発者を必要とせずに、完全なSEO制御を持っています。

条件付きレンダリング

コンテンツの各セクションは if チェックでラップされています。見出しフィールドが空の場合、<h2> タグはレンダリングされません。ボタンテキストが空の場合、ボタンは表示されません。画像にソースがない場合、画像の列は完全に消えます。

これにより、空のHTML要素がページに表示されるのを防ぎます。レイアウトの崩れや、スペースを占有する空のdiv、空の見出しタグによるアクセシビリティの問題が発生しません。

HubSpot 画像のベストプラクティス

HubSpotの画像フィールドタイプを使用する場合は、常にフィールドの値を直接出力してください(src、alt、width、height)。HubSpotの画像フィールドはすでにSEOに最適化されています。レスポンシブ画像属性と適切な代替テキスト(alt)の処理が標準で提供されます。

これらの値をハードコードされた属性で上書きしないでください。スクロールせずに見える範囲(アバブ・ザ・フォールド)より下にある画像には loading="lazy" を追加するだけで、ページのパフォーマンスが向上します。

再利用可能なセクション設定のためのマクロの使用

すべてのモジュールでセクション設定(ID、クラス、表示/非表示)が必要な場合、そのコードをすべての module.html にコピーするべきではありません。代わりに、マクロファイルを1度だけ作成し、それをどこにでもインポートします。

テーマ内に macros/section-macros.html を作成する

{% module_block module "widget_af9dd67b-9662-411b-9f10-8a9a2e058939" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{%- macro section_attrs(settings) -%}
  {%- if settings.section_id %} id=\"{{ settings.section_id }}\"{% endif -%}
  {%- if settings.section_class %} class=\"{{ settings.section_class }}\"{% endif -%}
{%- endmacro -%}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

任意のモジュールにインポートして使用する

{% module_block module "widget_cb4c41b6-50fd-4d27-bc62-d8dfc76886cc" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"{%- import '../macros/section-macros.html' as section_macros -%}

{%- if module.section_settings.section_visible -%}
<section {{ section_macros.section_attrs(module.section_settings) }}>
  {# ここにモジュールのコンテンツ #}
</section>
{%- endif -%}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

一度書けば、50個のモジュールにインポートできます。セクションIDの仕組みを変更する必要がある場合は、1つのファイルを更新するだけで、すべてのモジュールに更新が反映されます。これがプロフェッショナルなHubSpotテーマの構築方法です。

CSS戦略 — モジュールのCSSを書くのをやめる

これは、このガイド全体の中で最も重要なセクションかもしれません。ほとんどのHubSpot開発者は、各モジュールの module.css ファイル内にCSSを書きます。これは混乱を招きます。モジュール間でのスタイルの重複、一貫性のない間隔、ファイルサイズの増大、そして新しいモジュールが追加されるたびにページの読み込みが遅くなります。

代わりに Tailwind CSS ユーティリティクラスを使用する

上記の module.html を見てください。カスタムCSSはゼロです。すべてのスタイルは、HTML内のTailwindユーティリティクラスを通じて直接適用されています:

  • max-w-7xl mx-auto px-4 — 最大幅、中央揃え、水平方向のパディングを持つコンテナ
  • grid grid-cols-1 lg:grid-cols-2 gap-12 — レスポンシブな2カラムグリッド
  • text-4xl sm:text-5xl lg:text-6xl font-bold — レスポンシブな見出しサイズ
  • hover:bg-blue-700 transition-colors — スムーズなトランジションを伴うホバー効果

このアプローチの利点:

  • テーマ全体で1つのCSSファイル — 50個の個別の module.css ファイルではありません
  • 縮小とパージ(不要コードの削除) — 実際に使用するクラスのみが含まれます
  • 一貫したデザインシステム — すべてのモジュールが同じ間隔のスケール、カラーパレット、ブレークポイントを使用します
  • より優れたカラーコントラスト — Tailwindのデフォルトパレットは、WCAG AA のアクセシビリティ基準を念頭に置いて設計されています
  • どの開発者でも読める — クラスの機能を理解するためにスタイルシートを探し回る必要はありません

実際に module.css が必要な場合 

module.css を書く唯一のタイミングは、Tailwindではどうしても処理できない場合です。通常は、カスタムアニメーションや複雑な疑似要素のパターンです:

{% module_block module "widget_eeb558a0-95d6-400c-8c54-e00bda85a85b" %}{% module_attribute "child_css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "code_block" is_json="true" %}"@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}

.hero-animate {
  animation: fadeInUp 0.6s ease-out;
}"{% end_module_attribute %}{% module_attribute "css" is_json="true" %}{}{% end_module_attribute %}{% module_attribute "definition_id" is_json="true" %}null{% end_module_attribute %}{% module_attribute "field_types" is_json="true" %}{"code_block":"richtext"}{% end_module_attribute %}{% module_attribute "label" is_json="true" %}null{% end_module_attribute %}{% module_attribute "module_id" is_json="true" %}321957934793{% end_module_attribute %}{% module_attribute "path" is_json="true" %}"/tryrapid/modules/Code"{% end_module_attribute %}{% module_attribute "schema_version" is_json="true" %}2{% end_module_attribute %}{% module_attribute "smart_objects" is_json="true" %}null{% end_module_attribute %}{% module_attribute "smart_type" is_json="true" %}"NOT_SMART"{% end_module_attribute %}{% module_attribute "tag" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "type" is_json="true" %}"module"{% end_module_attribute %}{% module_attribute "wrap_field_tag" is_json="true" %}"div"{% end_module_attribute %}{% end_module_block %}

それ以外のすべては、グローバルなテーマCSSに含めるか、Tailwindのユーティリティで処理されるべきです。例外はありません。

JavaScript — 必要な場合のみ

同じルールが module.js にも適用されます。ほとんどのモジュールはJavaScriptを全く必要としません。

JSが不要なモジュール:ヒーローセクション、テキストと画像ブロック、機能グリッド、お客様の声(テスティモニアル)カード、価格表、フッターセクション。これらは純粋に視覚的なものであり、HTMLとCSSがすべてを処理します。

JSが必要なモジュール:アコーディオン、カルーセル/スライダー、タブコンポーネント、モーダルポップアップ、フォーム検証、スクロールでトリガーされるアニメーション。

JavaScriptを書く場合は、バニラ(依存関係なし)にしてください。2026年にjQueryは不要です。HubSpotは、1つのページにモジュールが複数回表示されても、モジュールタイプごとに1回だけ module.js を読み込みます。しかし、それでもページの重みには追加されます。含めるすべてのスクリプトについて意図を持ってください。

カラーコントラストとアクセシビリティ

Googleは検索ランキングにおいてアクセシビリティのシグナルを考慮しています。アクセシブルなモジュールを構築することは、単なる良い習慣ではなく、SEOに直接影響を与えます。

Tailwind CSSを使用している場合、すでに有利です。Tailwindのデフォルトのカラーパレットは、適切なコントラスト比で設計されています。これらの安全な組み合わせに固執してください:

  • bg-white 上の text-slate-900 — コントラスト比 15.4:1(AAAをクリア)
  • bg-slate-900 上の text-white — コントラスト比 15.4:1(AAAをクリア)
  • bg-slate-50 上の text-slate-700 — コントラスト比 8.1:1(AAAをクリア)

明るい背景に明るいテキストは避けてください。4.5:1 未満のコントラスト比は WCAG AA に不合格となり、検索ランキングに悪影響を与える可能性があります。

モジュールのアクセシビリティチェックリスト

  • すべての画像に、HubSpotの画像フィールドからの意味のある alt 属性がある
  • 見出しの階層が論理的である — H1の後にH2が続き、H4に飛ぶことはない
  • リンクに説明的なテキストがある — 「ここをクリック」ではなく「完全なガイドを読む」
  • インタラクティブな要素に適切なHTMLタグが使用されている — リンクには <a>、アクションには <button>
  • 色だけで意味を伝えていない — 常に色をテキストやアイコンと組み合わせる

モジュールのテスト

モジュールを公開する前に、以下のチェックリストを実行してください:

  • HubSpotのデザインマネージャーのプレビューでモジュールをプレビューする
  • ダミーテキストだけでなく、実際のコンテンツを使用して実際のページでテストする
  • 4つのブレークポイントすべてを確認する:モバイル(320px)、タブレット(768px)、ラップトップ(1024px)、デスクトップ(1440px)
  • このモジュールが存在する場合、ページ上にH1が1つだけ存在することを確認する
  • 空のフィールドでテストする — レイアウトはまだきれいに見えるか?
  • 非常に長いコンテンツでテストする — 見出しは適切に折り返されるか?
  • パフォーマンス、アクセシビリティ、SEOのスコアについて Lighthouse 監査を実行する
  • ページのソースコードを表示する — 不要な空白行や空の要素はないか?

モジュールをダメにするよくある間違い

すべてのモジュールにCSSを書くこと。 これにより、スタイルの重複とページ読み込みの重大化が生じます。Tailwindユーティリティクラスを使用するか、テーマのスタイルシートにグローバルCSSを記述してください。モジュールのCSSは最終手段であるべきです。

不必要にJavaScriptを追加すること。 ヒーローセクション、機能グリッド、お客様の声などの静的コンテンツモジュールにはJavaScriptは必要ありません。すべてのスクリプトがページの重さを増大させます。

見出しタグをハードコードすること。 HTMLに直接 <h2> と書くと、コンテンツ編集者はSEOの階層を制御できません。常に選択フィールドを使用し、タグを動的にレンダリングしてください。

HubLの空白を無視すること。 {% %} の代わりに {%- -%} を使用すると、レンダリングされたHTMLが空白行でいっぱいになります。ダッシュ構文は空白を削除し、よりクリーンで軽量な出力を生成します。

グループ化されていない平坦なフィールドリスト。 ページエディターで20個のグループ化されていないフィールドがあると、コンテンツ編集者にとって悪夢です。コンテンツ、画像、ボタン、セクション設定など、関連するフィールドをグループ化して、インターフェースをクリーンでナビゲートしやすくしてください。

セクション設定をスキップすること。 IDフィールドがないと、編集者はアンカーリンクを作成できません。表示/非表示の切り替えがないと、セクションを一時的に隠すことができません。カスタムクラスフィールドがないと、スタイルの微調整のたびに開発者が必要になります。これら3つのフィールドを標準的な実践としてすべてのモジュールに追加してください。

HubSpotの画像スニペットを上書きすること。 HubSpotの画像フィールドタイプは、適切な src、alt、width、height、およびレスポンシブ属性を備えたSEO最適化済みの出力をすでに生成します。これをカスタムコードで置き換えないでください。フィールドの値を直接出力し、適切な場合は loading="lazy" を追加するだけにしてください。

すべてのボタンにCTAを使用すること。 HubSpotのCTAにはトラッキングのオーバーヘッドがあります。ページにリンクするシンプルなボタンの場合は、代わりにテキストフィールドとリンクフィールドを備えたアンカータグを使用してください。CTAは、トラッキングと分析が本当に必要なボタンのために予約しておいてください。

まとめ

HubSpotでカスタムモジュールを構築することは、単に見栄えを良くすることだけではありません。高速で、アクセシブルで、SEOに優れ、コンテンツ編集者が簡単に使用できるコンポーネントを構築することです。

このガイドで取り上げたテクニック — グループ化されたフィールド、動的な見出しタグ、HubLの空白制御、Tailwindユーティリティクラス、再利用可能なマクロ、および適切な画像処理 — こそが、プロフェッショナルなHubSpot開発と素人の仕事を分けるものです。

次のモジュールからこれらの実践を適用し始めてください。コンテンツ編集者はあなたに感謝し、ページの読み込みは速くなり、SEOスコアは向上するでしょう。


HubSpotモジュールをゼロから構築するには時間がかかります。Rapid を使用すると、任意のUIスクリーンショットをアップロードして、fields.json、HubLバインディング、および適切な構造を備えた完全に本番環境ですぐに使えるHubSpotモジュールを60秒未満で生成できます。無料で試す →

Share this article

まだ手動でモジュールを作成していますか?

UIスクリーンショットを、AIでそのまま使えるHubSpotモジュールに変換。