【Horizon】ドロワーメニューにバナー画像を表示するカスタマイズ

【Horizon】ドロワーメニューにバナー画像を表示するカスタマイズ

「ドロワーメニューにキャンペーンバナーを表示したい」

モバイルのハンバーガーメニューを開いたとき、ナビゲーションの下にバナー画像を表示してクーポン配布やセール告知の導線にしたい、というのはよくある要望です。Dawn/Riseテーマには設定例がありますが、Horizonではどうでしょうか。

結論から言うと、Horizonでも実装可能ですが、ファイル構造の違いによりベンダーファイルの編集が必要になります。本記事では、ファイル追跡の過程から、テーマエディタで画像とリンクを設定できるようにするまでの具体的な手順を紹介します。

なお、本記事ではHorizonのカスタマイズ方針で推奨されている custom.* ファイルの新設は敢えて行わず、ベンダーファイルを直接編集する方法を掲載しています。その理由についても詳細に述べていますので、併せてご確認ください。

完成イメージ

ドロワーメニューのナビゲーション下部にバナー画像が表示され、テーマエディタのメニューブロック設定から画像とリンク先を自由に変更できるようになります。画像を設定しなければ何も表示されないため、元の見た目は一切変わりません。

Horizonテーマ ドロワーメニューのバナー表示例

Dawnでの対応

Dawn/Riseテーマの場合、ヘッダーセクションとドロワーメニューは同じファイルに収まっています。

// Dawn/Riseの場合

sections/header.liquid    ← schema定義もドロワーの描画コードも全てここ

header.liquid のschemaに image_picker(画像選択)と url(リンク先)の設定を追加し、同じファイル内のドロワー描画部分にバナー表示コードを追加するだけで完了です。変更は1ファイルで完結します。

Horizonでのドロワーメニュー:ファイルの分離

Horizonでは、ドロワーメニューの機能が3つのファイルに分かれています。

// Horizonの場合

sections/header.liquid                 ← セクション本体
  └─ blocks/_header-menu.liquid          ← メニューブロック(schema定義はここ)
       └─ snippets/header-drawer.liquid   ← ドロワーのHTML構造(描画はここ)

Dawnで1ファイルに収まっていた「schema定義」と「ドロワーの描画コード」が、Horizonでは別々のファイルに分離しています。

なぜこうなっているのか

Horizonは「テーマブロックアーキテクチャ」を採用しており、メニューの設定(リンクリスト、フォント、アコーディオン表示など)はブロックのschemaで管理され、HTMLの描画はスニペットに委任されています。この分離により、同じ _header-menu ブロックをデスクトップメニュー・モバイルドロワー・ナビゲーションバーの3つの形態で使い回すことが可能になっています。

ファイル追跡の実際

「ドロワーメニューにバナーを追加する」ために、実際にどのような手順でファイルを追跡することになるかを見ていきましょう。

Step 1:header.liquid → ブロックへの委任を確認

ヘッダーセクション sections/header.liquid を開くと、ドロワーメニューのレンダリングは _header-menu ブロックに委任されています。

// header.liquid 内の _header-menu 呼び出し(3箇所)

{% content_for 'block', type: '_header-menu', id: 'header-menu' %}                                        ← デスクトップメニュー
{% content_for 'block', type: '_header-menu', id: 'header-menu', variant: 'mobile' %}             ← モバイルドロワー
{% content_for 'block', type: '_header-menu', id: 'header-menu', variant: 'navigation_bar' %}    ← ナビゲーションバー

ここで重要なのは、content_for 'block'type にブロックファイル名がハードコードされている点です。これが後の「custom.*方式が使えない理由」に繋がります。

また、header.liquid のschemaを確認すると、blocks 定義がありません。headerセクションは静的なブロック呼び出しのみで、カスタムブロックを受け付ける仕組みがないことがわかります。

Step 2:_header-menu.liquid → ドロワースニペットへの委任

blocks/_header-menu.liquid を開くと、variant の値に応じて異なるレンダリングを行っています。variant: 'mobile' のとき、ドロワーの描画は header-drawer スニペットに委任されます。

// _header-menu.liquid(冒頭部分)

{% case variant %}
  {% when 'mobile' %}
    <div class="header__drawer">
      {% render 'header-drawer',
        linklist: block_settings.menu,
        data_header_drawer_type: 'mobile-drawer' %}
    </div>

  {% when 'navigation_bar' %}
    ...
{% endcase %}

このファイルの末尾にはschemaがあり、メニューに関するさまざまな設定項目が定義されています。テーマエディタの「メニュー」ブロック設定パネルに表示される項目は、すべてここで管理されています。バナーの設定を追加するのもこのschemaです。

ただし、バナーの表示(HTML描画)はこのファイルではなく、呼び出し先のスニペットで行う必要があります。

Step 3:header-drawer.liquid → バナーの挿入位置を特定

snippets/header-drawer.liquid を開くと、ドロワー内部のHTML構造が見えてきます。

// header-drawer.liquid のドロワー内部構造

<div class="menu-drawer">
  <button class="menu-drawer__close-button">...</button>

  <nav class="menu-drawer__navigation">
    <ul>
      ...メニュー項目...
    </ul>
  </nav>
                                                  ← ★ バナーの挿入位置
  <div class="menu-drawer__utility-links">
    ...言語・通貨セレクタ...
  </div>

  <div class="menu-drawer__featured-content">
    ...コレクション/商品画像...
  </div>
</div>

ナビゲーション(</nav>)の直後、ユーティリティリンクの直前がバナーの自然な挿入位置です。メニュー項目をスクロールした先にバナーが現れる形になります。

このスニペットはschemaを持てませんが、呼び出し元の _header-menu ブロックの設定値に block_settings(= block.settings)でアクセスできます。これは、Shopifyのスニペットが呼び出し元のスコープを引き継ぐ仕組みによるものです。

スニペットのスコープ

header-drawer.liquid の冒頭を見ると、assign block_settings = block.settings で親ブロックの設定を変数に格納しています。Step 2で _header-menu.liquid のschemaに追加した設定は、このスニペット内で block_settings.drawer_banner_image のようにアクセスできます。

実際の変更内容

変更方針は前回の記事(バッジカスタマイズ)と同じです。元のコードを削除するのではなく、schemaに設定項目を追加してテーマエディタから操作できるようにすることで、デフォルト値で元の動作を維持します。

変更1:_header-menu.liquid のschemaにバナー設定を追加

schemaのsettings配列の末尾(drawer_dividers の後)に、バナー用のheader・image_picker・urlの3項目を追加します。

// _header-menu.liquid の schema → settings 配列末尾に追加

    {
      "type": "header",
      "content": "t:content.drawer_banner"
    },
    {
      "type": "image_picker",
      "id": "drawer_banner_image",
      "label": "t:settings.drawer_banner_image"
    },
    {
      "type": "url",
      "id": "drawer_banner_link",
      "label": "t:settings.drawer_banner_link"
    }

image_picker はShopifyの管理画面から画像をアップロード・選択できる設定タイプです。url はストア内のページ・コレクション・商品・外部URLなどを選択できるリンクピッカーです。どちらも default を設定していないので、初期状態では空(=バナー非表示)になります。

変更2:header-drawer.liquid にバナー表示コードを追加

ドロワーのHTML構造で、ナビゲーション(</nav>)の直後にバナー表示コードを挿入します。

// header-drawer.liquid の </nav> 直後に追加

      {%- if block_settings.drawer_banner_image -%}
        <div class="menu-drawer__banner menu-drawer__animated-element"
          style="--menu-drawer-animation-index: {{ linklist.links.size }};">
          {%- if block_settings.drawer_banner_link != blank -%}
            <a href="{{ block_settings.drawer_banner_link }}">
              {{ block_settings.drawer_banner_image
                | image_url: width: 800
                | image_tag:
                    class: 'menu-drawer__banner-image',
                    loading: 'lazy',
                    widths: '400, 600, 800' }}
            </a>
          {%- else -%}
            {{ block_settings.drawer_banner_image
              | image_url: width: 800
              | image_tag:
                  class: 'menu-drawer__banner-image',
                  loading: 'lazy',
                  widths: '400, 600, 800' }}
          {%- endif -%}
        </div>
      {%- endif -%}

menu-drawer__animated-element クラスを付与することで、ドロワーが開くときのスライドインアニメーションにバナーも自然に組み込まれます。--menu-drawer-animation-index はアニメーションの遅延順序を制御するCSS変数で、メニュー項目数に合わせた値を設定しています。

image_urlimage_tag フィルタを使うことで、Shopify CDNによるリサイズと srcset 属性の自動生成が行われ、端末に応じた最適なサイズの画像が配信されます。

変更3:localeファイルにラベルを追加

テーマエディタ上で日本語ラベルを表示するために、localeファイルに翻訳キーを追加します。

// en.default.schema.json

// "content" オブジェクト内に追加:
"drawer_banner": "Drawer banner"

// "settings" オブジェクト内に追加:
"drawer_banner_image": "Banner image",
"drawer_banner_link": "Banner link"
// ja.schema.json

// "content" オブジェクト内に追加:
"drawer_banner": "ドロワーバナー"

// "settings" オブジェクト内に追加:
"drawer_banner_image": "バナー画像",
"drawer_banner_link": "バナーのリンク先"

変更4:CSSの追加

バナーの見た目を整えるCSSを追加します。こちらは assets/custom.css に追加する形で、ベンダーファイルへの変更は不要です。

.menu-drawer__banner {
  padding: 16px 20px 0;
}

.menu-drawer__banner-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--style-border-radius-card, 8px);
}

Horizonのカラースキーム変数 --style-border-radius-card を使うことで、テーマ設定の角丸に自動で追従します。

テーマエディタでのドロワーバナー設定画面

変更ファイルの全体像

ファイル 種別 変更内容
blocks/_header-menu.liquid ベンダーファイル変更 schemaにバナー設定(image_picker + url)を追加
snippets/header-drawer.liquid ベンダーファイル変更 バナー表示のHTMLを追加
locales/en.default.schema.json ベンダーファイル変更 英語ラベル追加
locales/ja.schema.json ベンダーファイル変更 日本語ラベル追加
assets/custom.css カスタムファイル バナーのスタイル追加

加えて、これらの変更を行うために読み込んで理解する必要があったファイルは以下の通りです。

ファイル 理解が必要な理由
sections/header.liquid 起点。ブロックへの委任方法とschemaの制約(blocks未定義)を確認
sections/header-group.json ヘッダーグループの構成と _header-menu ブロックの実行時設定を確認

「ベンダーファイルを触らない」方針と、触らざるを得ない現実

安全なカスタマイズガイドでは、custom.* プレフィックス付きのファイルだけで完結させ、ベンダーファイルを変更しないことを推奨しています。

今回のケースでcustom方式を試みると、どうなるでしょうか。

custom方式を試みるとどうなるか

_header-menu.liquid をコピーして custom._header-menu.liquid を作ったとします。しかし、このカスタムブロックを使うには header.liquidcontent_for のtype指定を3箇所すべて書き換える必要があります。

// header.liquid の3箇所を書き換える必要がある

{% content_for 'block', type: '_header-menu', id: 'header-menu' %}
{% content_for 'block', type: 'custom._header-menu', id: 'header-menu' %}
                        ↑ × 3箇所

さらに header-group.json(テーマエディタの保存状態)でもblockのtype指定を書き換える必要があります。

方式 変更するベンダーファイル数
_header-menu.liquid + header-drawer.liquid を直接編集 4ファイル(ブロック + スニペット + locale 2つ)
custom._header-menu.liquid を新設 結局 header.liquid の3箇所 + header-group.json + header-drawer.liquid を書き換え

前回の記事では、custom方式を試みると「参照元の連鎖」によって11ファイルの書き換えが必要になるケースを紹介しました。今回は規模こそ小さいものの、問題の本質は同じです。content_for のtype指定というハードコードされた参照があるため、ブロックのファイル名を変えればその参照元を必ず書き換えることになります。

今回のケースでは、_header-menu.liquidheader-drawer.liquid を直接編集する方式を選択しています。元のコードを削除するのではなく設定項目の追加のみなので、画像を設定しなければ何も変わりません。テーマアップデート時のマージでも、設定の追加は競合が起きにくい変更です。

Git管理の活用

ベンダーファイルを変更するカスタマイズを行う場合、GitHub(またはBitbucket等)によるバージョン管理を活用するのがおすすめです。upstream/horizon ブランチでオリジナルテーマを追跡し、main ブランチでカスタマイズを管理する。こうしておけば、テーマアップデート時に git merge upstream/horizon で差分を確認しながらマージできます。具体的な方法は「Horizonテーマの安全なカスタマイズガイド」で詳しく紹介しています。

DawnとHorizon、同じカスタマイズでも道筋が違う

「ドロワーメニューにバナーを追加する」という同じ要件でも、DawnとHorizonではアプローチが大きく異なることがわかりました。

Dawn / Rise Horizon
schema定義の場所 header.liquid blocks/_header-menu.liquid
描画コードの場所 header.liquid(同じファイル) snippets/header-drawer.liquid(別ファイル)
変更ファイル数 1ファイル 4ファイル(+ CSS 1)
理解が必要なファイル数 1ファイル 4ファイル(header.liquid → _header-menu.liquid → header-drawer.liquid + header-group.json)

Horizonは「schema定義」と「HTML描画」を別ファイルに分離する設計を採用しています。この分離は、同じメニューブロックをデスクトップ・モバイル・ナビゲーションバーの3形態で使い回すためのものです。ノーコードのテーマエディタでは設定1箇所で3形態すべてに反映されるため、Horizonの設計は合理的です。しかしコードでカスタマイズする場合は、設定の追加と描画の追加を別々のファイルで行う必要があり、追跡コストが上がります。

Horizonでカスタマイズを行う際は、以下のことを事前に意識しておくと良いでしょう。

  1. まずテーマエディタで実現できないか確認する。Horizonのブロックシステムは予想以上に柔軟なので、エディタの設定だけで要望を満たせるケースも多いです
  2. CSSだけで対応できるなら custom.css に書く。ベンダーファイルへの変更は最小限に
  3. ベンダーファイルを触る場合は「設定の追加」で対応する。元コードの削除・書き換えではなく、schemaへの項目追加にとどめれば、デフォルト値で元の動作が維持される
  4. ベンダーファイルを触る場合は必ずGit管理する。変更箇所のドキュメント化も忘れずに

関連記事

ブログに戻る