Shopify Horizon v4.1.5 アップデート解説 ― セーフエリア対応とカート数量入力の不具合修正
Shopify公式テーマ「Horizon」が v4.1.4 から v4.1.5 に進みました。本記事では、コードの差分を読み解きながら、v4.1.4 から v4.1.5 への変更内容と、ストア運営者が押さえておくべきポイントをまとめます。
前回の「v4.1.4 アップデート解説」では複数ファイルで使うCSSの共通化を取り上げましたが、今回は毛色が違います。iPhoneのノッチ・ホームインジケーターに配慮した「セーフエリア」対応が横断的に入ったほか、カート数量入力の実用バグ修正など、体験に直結する改善が中心です。
変更サマリー ― 「54ファイル変更」の中身
差分は 54ファイル変更(新規1・削除1・変更52)。前回・前々回と比べると規模は小さいですが、1つの技術的な仕込み(セーフエリア対応)が十数ファイルに波及した点は同じ構造です。
ストア運営者が気にすべき変更は、大きく次の通りです。
| 変更 | 影響範囲 | 対応の要否 |
|---|---|---|
| ① セーフエリア(ノッチ・ホームインジケーター)対応 | ドロワー・モーダル・カート・商品ギャラリー等のiPhone表示 | 不要(自動で適用) |
| ② カート数量入力の値が消える不具合の修正 | カート内での数量編集の正確性 | 不要(自動で適用) |
| ③ ブログ記事カードの画像読み込み最適化 | ブログ一覧ページの表示速度(LCP) | 不要(自動で適用) |
| ④ Popover APIのネイティブ対応判定 | ポップオーバー系UIの初期表示速度 | 不要(自動で適用) |
| ⑤ ヘッダー検索欄の表示判定バグ修正 | ヘッダーの検索アイコン表示・非表示設定 | 該当設定を使っているストアのみ確認 |
順に解説します。
変更点① ― セーフエリア(ノッチ・ホームインジケーター)対応
今回最大の変更です。snippets/meta-tags.liquid のviewportメタタグに viewport-fit=cover が追加されました。これは「コンテンツをノッチやホームインジケーターの裏側まで含めた画面全体に描画してよい」とブラウザに伝える指定で、これが無いとCSS側でenv(safe-area-inset-*)という値を取得しても常に0になります。
この指定を起点に、snippets/theme-styles-variables.liquid に --safe-area-inset-top/right/bottom/left というCSS変数が新設され、次のような箇所で実際に使われています。
-
ページ全体の左右余白(
--page-margin)が、通常の16px/40pxとセーフエリア幅の大きい方を採用するように変更 - カートドロワー・メニュードロワー・テーマドロワー(右/左それぞれ)の内側余白が、ホームインジケーターや側面ノッチを避けるように調整
- クイック追加モーダル・フルスクリーンダイアログ・ポップアップリンクの余白・閉じるボタン位置がノッチ/ホームインジケーターを避けるように調整
- 商品ページのギャラリーサムネイル・購入ボタンの下部パディングがホームインジケーター分を確保
-
templates/gift_card.liquidなど個別テンプレートのviewportメタタグにも同様にviewport-fit=coverを追加
snippets/popup-link.liquid のコード内コメントには「右側から出るドロワーは物理的なビューポート全体に広がるので(viewport-fit=cover)、コンテンツを右側のノッチとホームインジケーターから離しておく必要がある」と明記されており、Face ID機種などノッチ・アイランドを持つiPhoneでの表示品質を底上げする狙いだと分かります。
変更点② ― カート数量入力の値が消える不具合の修正
Horizonのカートは、内容が変わると裏側で該当箇所だけを再描画する「morph」という仕組みを持っています。今回、この再描画中に数量入力欄を編集していると、タイプ中の値がサーバー側の値で上書きされてしまう不具合が修正されました。
assets/component-quantity-selector.js は、入力欄の編集開始時に data-skip-value-update というマーカー属性を付け、編集終了(blur)時にそれを外すように変更されました。assets/morph.js 側はこのマーカーが付いた入力要素のvalue属性の更新だけを見送るようになっています。
実装には詳細な設計意図がコメントとして残されています。要点は次の3つです。
- マーカーは「入力にフォーカスが当たっている」ことと組み合わせて判定される(
:focus)。マーカーだけでは、blurのタイミング次第で残留し続け、以降ずっと値が更新されなくなる「値の固まり」を防ぐため - サーバー側でその数量がもう編集できない状態(在庫上限超過等で
disabledになった)場合は、フォーカスの有無に関わらずマーカーを無視して値を更新する - 対象は
value属性のみに限定し、上限・下限などその他の属性は通常通りサーバーの最新値で更新される
複数商品をカートに入れた直後や、別タブでカートを操作した直後など、裏で再描画が走りやすいタイミングと数量編集が重なった場合に効いてくる修正です。目立ちにくいバグですが、実害としては「数量を打ち込んだのに元に戻る」という分かりやすい不満につながるものでした。
変更点③ ― ブログ記事カードの画像読み込み最適化
v4.1.4までのblocks/_blog-post-image.liquidは、ブログ一覧に並ぶすべてのカード画像を一律 loading="eager" fetchpriority="high"で読み込んでいました。記事が何十件も並ぶページでは、画面外の画像まで最優先で読み込みを奪い合う状態になります。
v4.1.5ではsections/main-blog.liquid側で画像ごとに読み込み優先度を計算し、_blog-post-card.liquid・_blog-post-image.liquidにパラメータとして渡す方式に変わりました。
-
最初の1枚目だけ
fetchpriority="high"(最初の視界に入る画面=LCP候補として最優先) -
最初の2枚までは
loading="eager"(初期表示のモバイル画面に収まる範囲という想定) - それ以降は
loading="lazy"で、画面に近づいてから読み込む
加えて、画像のsizes属性もカードのレイアウト(コンパクト/標準/ヒーロー)ごとに個別計算されるようになり、ブラウザが不必要に大きい画像を選んでしまうことも防いでいます。ブログ一覧・記事一覧を主要な集客導線にしているストアほど、この変更の恩恵を受けやすいはずです。
変更点④ ― Popover APIのネイティブ対応判定
Horizonのポップオーバー系UI(バリエーション情報・アンカー付きポップオーバー等)は、ブラウザ標準のPopover APIに対応していない環境向けに、ポリフィル(popover-polyfill.js)を用意しています。v4.1.4までは、このポリフィルをブラウザの対応状況に関わらず常に低優先度で読み込んでいました。
v4.1.5では 'popover' in HTMLElement.prototype でネイティブ対応の有無を先に判定し、対応済みのブラウザではポリフィルを読み込まないように変更されました。判定結果はTheme.supportsNativePopover・Theme.popoverPolyfillReadyとしてグローバルに公開され、anchored-popover.js・volume-pricing-info.jsなど、ポップオーバーに依存するカスタムエレメントは、この判定が終わるのを待ってから自身を登録するようになっています。
あわせて、これまで外部ファイルとして読み込んでいたview-transitions.jsも、コード内コメント曰く「レンダリングをブロックするアセットのために余分な通信往復を避ける」目的で、inline_asset_contentを使ってHTML内にインライン化されました。いずれもモダンブラウザ利用者にとっては無駄なJS読み込みが減る方向の最適化です。
変更点⑤ ― ヘッダー検索欄の表示判定バグ修正
sections/header.liquidの検索欄の表示判定が、section.settings.search_style != 'none'からsection.settings.show_searchに修正されました。テーマ設定のスキーマ上、検索欄の表示・非表示はshow_searchという真偽値の設定項目で管理されており、search_styleという設定は存在しません。誤った設定キーを参照していたことになります。
あわせて、2段組みヘッダーのうち1段しか使っていない場合の背景色処理も見直されました。新設されたhas_second_row判定により、data-header-rows="single"という属性が新たに付与され、1段のみのヘッダーで下段用の背景色設定が透明として誤判定されないよう調整されています。
その他の変更
| 変更 | 概要 |
|---|---|
| 国・地域一覧の検索フィルタのバグ修正 |
assets/localization.js/snippets/localization-form.liquid。国・地域を検索する際、「人気の国」リストの表示判定が検索語の有無だけを見ていたのを、実際に一致する国が残っているかで判定するよう修正 |
| 角丸のはみ出し防止 |
snippets/border-override.liquid。角丸指定時のoverflow: hiddenをoverflow: clipに変更。前者は要素をスクロールコンテナ化してしまい、flexboxレイアウトが崩れる場合があった |
| ヘッダー高さ計測の精度向上 |
assets/utilities.js。offsetHeight(整数に丸められる)からgetBoundingClientRect().height(小数を保持)に変更。丸め誤差でヘッダー下に薄い隙間が見えることがあったのを解消 |
| モーダルドロワーを閉じた際のスクロール位置ジャンプ修正 |
assets/theme-drawer.js。モーダル表示のドロワーを閉じる際、スクロールロック解除とフォーカス復帰の過程でページ最上部にジャンプしてしまうことがあったのを、閉じる前のスクロール位置を保持・再適用することで解消 |
| 初回訪問・リロード時の不要な描画ブロック解除 |
assets/view-transitions.js。ページ遷移アニメーションが原理的に発生しえない初回訪問・リロード時も判定に加え、無駄な初期表示ブロックを減らした |
| ヘッダーロゴ切替のクラス名整理 |
blocks/_header-logo.liquid。「通常時/ヘッダー透過時」のクラス名を--original/--inverseから--solid-header/--transparent-headerに改名し、意味が読み取りやすいように整理 |
| スライドショーの矢印アニメーション制御を追加 |
snippets/slideshow.liquid。カルーセル形式では矢印を常時表示に固定し、ホバーで出現するアニメーションを行わないようanimate-arrows属性で制御するように変更 |
アップデート手順
安全カスタマイズ記事で構築したupstream/horizonブランチ運用を使っている場合、テーマの管理画面からエクスポートした最新版のファイル一式をこのブランチにまるごと反映し、upstream/v4.1.5・horizon-v4.1.5のタグを打って管理します。
⚠️ upstreamブランチはあくまで「無加工のHorizon」を保つための専用ブランチです。検証用にテーマエディタで一時的にブロックを追加したりカラーパレットを変更したりすると、その変更が自動コミットとして紛れ込み、次のバージョンアップ時に「本来存在しないファイル」との差分として現れてしまいます。upstreamに接続されたテーマでは実験をしない、が鉄則です。
まとめ
v4.1.4 → v4.1.5 はアップデート推奨です。カラーシステムのような見た目に直結する変更はなく、iPhoneでの表示品質・カート操作の正確性・表示速度を底上げする内容が中心でした。
- セーフエリア対応は、ノッチ・アイランドを持つiPhoneでドロワーやモーダルを使うストアほど恩恵が大きい変更です。カスタムCSSで固定pxの余白をドロワーに直接指定している場合は、更新後に実機(またはSafariの検証ツール)で確認しておくと安心です。
- カート数量入力の修正・ブログ画像の最適化は、いずれも表面化しにくいものの、体感品質に直結する地道な改善です。
- ヘッダーの検索欄設定を非表示にしているストアは、表示判定バグの修正により意図通りの見た目になっているか一度確認しておくとよいでしょう。
ファイル構成がバージョンごとに変わりうるテーマだからこそ、「どこを触ったか」を常に把握できるGit管理(upstream/horizonブランチでのオリジナル追跡)が、こうした地道なアップデートでも効いてきます。