ShopifyのDebutテーマのカスタマイズメモ

窓辺

Shopifyの公式ヘルプの中で紹介されているテーマの中の一つで、ベーシックで基本となると思われるテーマ「Debut」に関するメモです。
なお、バージョンアップにより後々違いがでてくるはずなので、該当の項目や設定が違う場合は適宜読み替えなりが必要になると思われます。

更新履歴

簡易ではありますが、上記の更新情報ページで多少の履歴が確認できます。

v17.4.1に関する注記

2020.10のアップデートで確認しましたが、v17.4.1で基本部分がかなり変わった模様です。大筋はv17.4.0からだとは思いますが、v17.4.1で確認したのでv17.4.0との差がわからないためあくまで以降はv17.4.1時点の内容です。

DebutといいますかShopifyのテーマはWPとは違い詳細な更新履歴がなく(どこかに記載されていそうなものですが見つけられず)、ドキュメントも弱いため、こういった更新時の変化がわかりにくいという点は問題に感じました。

jQueryの削除(要確認)

jQueryが削除されている模様です(不確定)。以前はあったvendor.js(jquery.min.jsがここに書かれていました)自体が削除されています。
jQueryの有無に関しては、目視と開発者ツールのコンソールに以下を記述して確認しました。


//falseがかえってくる。以前のバージョンではtrueがかえってきた。
typeof jQuery === "function" 

フォーラムでこの件が書かれていますが、いまいち要領を得ない内容でjQueryがあるともないとも判断がつきかねますが。

Slickの非jQuery化(要確認)

SlickがあるのでどこかにjQueryの読み込み記述があるかと思ったのですが、コードを見るにバニラで書かれているように見えました。Slick本家からバニラ版は出ていないと思いますので、Debutの開発元が作成したのかもしれません。

この変更ため以前のバージョンでSlickを用いたコードをカスタムしていた場合、そのコードは動かなくなる可能性が高いです。

Resource Hintsの追加

以下のようにResource Hintsが追加されています。


<link rel="preconnect" href="https://cdn.shopify.com" crossorigin>
<link rel="preconnect" href="https://fonts.shopify.com" crossorigin>
<link rel="preconnect" href="https://monorail-edge.shopifysvc.com">

CSSに関する変更

外から見る分にはわかりませんが、以下のように内部の仕組み的な部分が変わっています。

  • スタイルシートからファーストビューに関連する指定をコピーしてインライン化(スタイルシートとインラインの2箇所に同じ記述が存在する)
  • インラインとスタイルシートのそれぞれ別々に記述されている(v17.9.2で確認)
  • CSSカスタムプロパティ(CSS variables)の利用
  • CSSの非同期読み込み

非同期に関しては以下別ページで検討しています。

今回の変更に伴い、カスタムする場合は以下のように作る必要があるかもしれません。

  • ファーストビュー用の記述をコピーしてインラインで記述
  • 外部スタイルシートを増やすのではなく、theme.cssに追加する

元のCSSの書き換えるかそのまま残すかで取れる方法がかわりますが、元のCSSを残すならインラインとスタイルシートの2箇所への記載とする方がよいように思います

[追記:2021.2.15]
デフォルトのインライン部分に該当箇所があるならそちらに記載し、ない場合はスタイルシートに記載する形になるようです。

SCSSではなくCSSの使用

今まではtheme.scss.liquidだったのですが、theme.css.liquidに変更されています。おそらく後述するCSS Variablesを使用することによりSCSSを使うメリットがなくったためではと思われますが明確にはわかりません。

なお、ShopifyのSCSSからCSSへのコンパイル機能がなくなったわけではないので、引き続きSCSSはLiquidで使用できます。

[追記:2020.11.4]
以下のブログでSCSSをやめた理由が書かれています。

インライン化と分割と非同期読み込み

ファーストビューに影響する部分のCSSを切り出してインラインで記載し、残りは以下の記述により非同期で外部スタイルシートを読み込んでいます。パフォーマンス測定も追加されていますが、意図は不明です。開発中ならいざ知らず、配布先でもデフォルトで常に計測する必要性があるのかは少々疑問です(Shopifyの速度計測用?)。


<script>
    window.performance.mark('debut:theme_stylesheet_loaded.start');

    function onLoadStylesheet() {
      performance.mark('debut:theme_stylesheet_loaded.end');
      performance.measure('debut:theme_stylesheet_loaded', 'debut:theme_stylesheet_loaded.start', 'debut:theme_stylesheet_loaded.end');

      var url = "{{ 'theme.css' | asset_url }}";
      var link = document.querySelector('link[href="' + url + '"]');
      link.loaded = true;
      link.dispatchEvent(new Event('load'));
    }
  </script>

  <link rel="stylesheet" href="{{ 'theme.css' | asset_url }}" type="text/css" media="print" onload="this.media='all';onLoadStylesheet()">

上記のmedia="print" onload="this.media='all';"が非同期読み込みの仕組みで詳細は以下。

CSS Variablesの使用

CSS Variables(カスタムプロパティ/変数)の中身を定義するsnipet/css-variables.liquidが追加され、以下のコードで読み込まれてインラインで出力されます。当初迷いましたがassets内のvariables.liquidではなく、snipet内のcss-variables.liquidです。


{% include 'css-variables' %}

なお、CSS Variablesは主に色と文字サイズに用いられており、以前はあったスペーサー系は存在しません。そのため以前ならpadding-top: $section-spacing;と書いていたところがpadding-top: 55px;のように単なる数値指定に置き換わっています。

恐らくですが、「CSS Variablesは管理画面から操作可能な指定に絞る」というルールが策定されたのではと考えています。
とはいえスペーサー系の数値が直書きに戻るのは運用的にデメリットが大きいように見え、今後、下手すれば直近のアップデートで見直されそうで不安です。

IE11用のpolyfill

これに伴いIE11用にpolyfillを読み込ませています。


<script type="text/javascript">
if (window.MSInputMethodContext && document.documentMode) {
  var scripts = document.getElementsByTagName('script')[0];
  var polyfill = document.createElement("script");
  polyfill.defer = true;
  polyfill.src = "{{ 'ie11CustomProperties.min.js' | asset_url }}";

  scripts.parentNode.insertBefore(polyfill, scripts);
}
</script>

この点からDebutはIE10以下には対応していないということが明確になっていますが、そもそもIE自体がサポート対象外です。

商売上の理由からIE10以下にどうしても対応したいという場合は、読みこませるポリフィルの変更など改造が必要ですが、Shopifyの対応ブラウザのことを考えれば以下が結論と言えるでしょう。

  • IE対応が必須ならShopifyを選ぶべきではない

ここまでの雑感

メジャーアップデートでもないのにこの変化かと驚いています。しかも変更点がどこかに記されている訳でもないようでコードを読むしかないかもしれません。

個人的にDebutをカスタマイズした場合のアップデートに関して色々と考えていたのですが、このレベルですと結構な不安感があります。基本的にJSはバニラで書いているのでカスタマイズ部分が全く動作しないことにはならないはずですが、問題はアプリです。

まだ確認できていませんがテーマ側にjQueryがない場合動かないアプリが出てくるのではと…。アプリ側でjQueryの読み込み判定をつけて対応できているとは思いますが、場合によっては以下のような対策が必要になると思われます。

  • 必要に応じてストア側でjQueryの読み込み記述を追加する

なお当記事の以降の内容はv17.4.1より前の時点の内容なので、読まれる方はその点留意ください。

概要

作業の準備や前提

  • ShopifyでTheme Kitを使い既存テーマをカスタマイズする:メモ – WEBUTUBUTU
  • Theme KitのインストールとAPIキーの取得が必要
  • ターミナル(Macの場合)を使う
  • 今回はDebutなので最初から入ってるはずだが、ない場合はShopify管理画面で事前にテーマをインストールして、可能ならテーマのIDを確認しておく(ターミナルを使う際に確認することも可能なので必須ではない)
  • バージョンによる違いがでるかもしれないが、lazysizes.jsのv4.1.8をassets内に配置して読み込んでいるため、Lazyload(画像の遅延表示)は実装されている

補足

  • Theme kitのtheme watchにより、APIを利用してローカルのデータをShopifyのデータと同期させる
  • APIの設定さえできていればShopify管理画面にはいる必要はない
  • 公開設定していないテーマでもIDを指定すれば作業可能なので、管理画面内の「カスマイズ」を利用すれば本番のデータを使った開発環境として利用可能(カートなどシステム連動のページは未確認)
  • theme watchで非公開テーマをブラウザ確認する場合は、theme open -b [ブラウザ指定]か、管理画面のプレビューで表示すればよい

カスタマイズ

ブログに関して

  • ブログ全体の一覧ページはない模様?各カテゴリーの一覧は存在する(newsカテゴリーならexample.com/blogs/newsのような感じ)
  • 管理画面のメニューでナビの飛び先をしているためにはURLを入力するしかなく、ブログへのナビを作りたいなら手作業で調べて入力する必要がある?
  • ブログ一覧の大枠はtemplate/blog.liquidで、一覧自体はsections/blog-template.liquidが使われる
  • ブログ記事単体の画面ではsections/article-template.liquidが使われる

グローバルナビのメガメニューをカーソルのホバーで開くようにする

上記に情報があるが、古いのと少し気になる部分があったので以下のようにして実装。CSSはtheme.scss.liquidでも新規で追加したCSSでもどちらでもいい。


//CSSに以下を新規で追加
.site-nav--has-dropdown:hover .site-nav__dropdown,
.site-nav--has-dropdown:focus .site-nav__dropdown {
  display: block;
}
.site-nav__dropdown{
  top:calc(1em * 1.5 + 3px);
}

元のクリックで開閉する機能をなくすには、最低限theme.jsの2376行目から2370行目までを以下のようにコメントアウトすれば実現はできる。


//2376行目から2370行目が該当するが、Debutテーマのアップデートによって行数やコード自体が変わるので、必要に応じて都度調査
cache.$parents.on('click.siteNav', function() {
  var $el = $(this);
  $el.hasClass(config.activeClass) ? hideDropdown($el) : showDropdown($el);
});
//上記を以下のようにコメントアウト
//cache.$parents.on('click.siteNav', function() {
//  var $el = $(this);
//  $el.hasClass(config.activeClass) ? hideDropdown($el) : showDropdown($el);
//});

TOPページのカルーセルのそれぞれにリンクをつける

以下のコードで、管理画面内の Slideshow の設定欄にURLのリンク先の入力欄ができ、ボタンではなくスライド丸ごと一枚ごとにリンクを設定できるようになる。


{% if block_image == blank %}
  <div class="slideshow__image js">
    {% comment %} 省略 {% endcomment %} 
  </div>
{% endif %}

{% comment %} 上記を探し、以下のように書き換える {% endcomment %} 

{%- unless block.settings.image_link == blank -%}
  <a href="{{ block.settings.image_link }}">
{%- endunless -%}

  {% if block_image == blank %}
    <div class="slideshow__image js">
    {% comment %} 省略 {% endcomment %} 
    </div>
  {% endif %}
{%- unless block.settings.image_link == blank -%}
  </a>
{%- endunless -%}

{
  "type": "url",
  "id": "button_link",
  "label": {
    "da": "Knaplink"
  }
},

{% comment %} 上記を探し、以下の記述を追加する {% endcomment %} 

{
  "type": "url",
  "id": "image_link",
  "label": "Image link"
}
{% comment %} これで Button link 欄の下に Image link という入力欄ができる {% endcomment %} 

参考URLのページやり方はhero.liquidのもので、かつ画像カルーセル全体で一つのリンクとなるため、対象ファイルと記載箇所を変更。

加えて、管理画面内の入力欄にURLをいれていない場合はaタグ自体を出力しなように変更。

商品説明文を下部に下げる(画像の横に並べない)

通常は商品の「メディア」のサイズを全幅にしないと画像の右横に商品説明文が並んでしまうが、表示位置を変更すればどの画像サイズでも説明文を下に表示できる。


        <div class="product-single__description rte">
          {{ product.description }}
        </div>

        {% if section.settings.show_share_buttons %}
          {% include 'social-sharing', share_title: product.title, share_permalink: product.url, share_image: product.featured_media %}
        {% endif %}
    </div>
  </div>
</div>

上記を以下のように変更する。


        {% if section.settings.show_share_buttons %}
          {% include 'social-sharing', share_title: product.title, share_permalink: product.url, share_image: product.featured_media %}
        {% endif %}
    </div>
  </div>

  <div class="product-single__description rte">
    {{ product.description }}
  </div>
  
</div>

商品詳細ページにテーマエディタで管理可能なコレクションのセクションを追加する

Debutに限らずだが上記の記事とテーマ内のファイルを参照すれば概ねなんとかなるはず。

新たに商品一覧を追加する(コレクションを使う場合)

  • 既存の Featured collection から価格も消して画像だけにしたい
  • CSSで消すこともできるが、今回は liquid で対応したい
  1. 必要なコレクションを作成(コレクションを利用する前提のため)
  2. sections/collection.liquidsnippet/product-card-grid.liquidを複製して利用
  3. 複製したファイルの一部を書き換える

//sections/collection.liquidを複製してリネームしたファイル
//idを変更しないと複製元のcollection.liquidと重複してしまうため、複製して利用する場合は変更が必要

{%- assign collection = collections[section.settings.collection] -%}
//↓ 以下のように変更
{%- assign collection = collections[section.settings.collection-test] -%}

//↓ 以下を消す
{% include 'product-price' %}

{% schema %}
{
  "settings": [
    {
      "id": "collection",
      "type": "collection",
//↓ 以下のように変更
{% schema %}
{
  "settings": [
    {
      "id": "collection-test",
      "type": "collection",

//snippet/product-card-grid.liquidを複製してリネームしたファイル
//↓ 以下を消す
<div class="h4 grid-view-item__title product-card__title" aria-hidden="true">{{ product.title }}</div>
{% include 'product-price-listing', product: product, show_vendor: show_vendor %}

購入数の入力欄を表示する

  1. 管理画面で「オンラインストア>テーマ>カスタマイズ」と進んでテーマエディタの画面まで移動する
  2. 上部の「ページを選択」プルダウンから「商品ページ」を選ぶ
  3. クション」タブの「商品ページ」を選ぶ
  4. 「数量セレクターを表示する」にチェックを入れる

上記で自動で表示される。

任意の位置に入れたり、最大購入数を設定したい場合には、テンプレート内にある以下のコードに手を加えたり、任意の場所に移動すればよい。


{% if section.settings.show_quantity_selector %}
  <div class="product-form__controls-group">
    <div class="product-form__item">
      <label for="Quantity-{{ section.id }}">{{ 'products.product.quantity' | t }}</label>
      <input type="number" id="Quantity-{{ section.id }}"
        name="quantity" value="1" min="1" max="10" pattern="[0-9]*"
        class="product-form__input product-form__input--quantity" data-quantity-input
      >
    </div>
  </div>
{% endif %}

上記コードのinputタグにつけたmax="10"が最大数の制限となる。20個までにしたいならmax="20"にする。

在庫数を表示する


// product-template.liquid で在庫を表示したい場所に記載
<div>We currently have <B id="variant-inventory">{{ current_variant.inventory_quantity }}</B> in stock.</div>

// ①_onSelectChange を探す
_onSelectChange: function() {
 var variant = this._getVariantFromOptions();

 this.$container.trigger({
  type: 'variantChange',
  variant: variant
 });

 if (!variant) {
  return;
 }

 this._updateMasterSelect(variant);
 this._updateImages(variant);
 this._updatePrice(variant);
 this._updateSKU(variant);
 // ②以下のthis._updateQuantity(variant); を加える
 this._updateQuantity(variant); 
 this.currentVariant = variant;

 if (this.enableHistoryState) {
  this._updateHistoryState(variant);
 }
},

// ③_onSelectChange の下あたりにでも以下追加する
_updateQuantity: function() {
  // window.top.location.href にしているのは、window.location.hrefだとライブビューの際に問題が出ると見た覚えがあるため。問題ないなら window.location.href でよいかも
  setTimeout(function() { $("#variant-inventory").load(window.top.location.href + " #variant-inventory"); }, 100);
},

Debut用のコードなので、他のテーマでは書き換える必要があるかもしれない

検索では2017年ごろの古いサンプルコードが出てくるが、variant.inventory_quantityがなくなったらしく(variantの中身を出力すると確かにない)、バリエーションの変更には対応できなかった。

仕組みとしては単純で、以下のようになるはず。

  1. liquid内のcurrent_variant.inventory_quantityでアクセスした時点で表示されている商品の在庫を取得して表示
  2. _onSelectChangeでバリエーション関連の変更があった場合に _updateQuantityを実行する
  3. jQuery のloadcurrent_variant.inventory_quantityの値を再取得させて在庫が変更される

上記のようにしなければならない背景は以下の通り。

  • JSでは在庫数を取得できなくなったので、liquid側の値を利用するしかない
  • Shopifyのバリエーション変更はURLはかわるもののliquidのコードは動作しない
  • liquidのコードを動作させるために jQuery によるloadなどを行う必要がある

なお、在庫表示用のアプリもあるがその仕組みは確認できてないので、この方法以外の正規の方法があるのではとは思う。

バリエーションをプルダウンからボックスに変更する

上記でできるか不明。このページにいろいろと情報があるのでどれかでできるかも。

上記ページは色選択を意図しているが、一応流用可能。

2020.6現在では、書かれているコードそのままでは動かない。主にJSの書き換え(snippetにいれたファイルではjQueryが使えずエラーが出るためpure JSに書き換えるなど)が必要となる。

ただ、Debutの内部自体がバージョンアップされていて、selectCallback関数が削除されている。上記URLの投稿内に対応策はかかれているが、それも動作しない(なくても動くように見えるが…)。

任意のバリエーションを選択したあとでリロードした場合、画像や価格はリロード後も変わらないものの、バリエーションのボタンは下に戻ってしまう。

スキル的に自力カスタマイズは難しいのであれば、Variant Options Swatch Kingなどのアプリを使った方がよいのではと思われる。

バリエーション変更時の動作

細切れに調査中であまり進んでいないが、途中までは恐らく以下のようになっている。


{% unless product == empty %}
  <script type="application/json" id="ProductJson-{{ section.id }}">
    {{ product | json }}
  </script>
  <script type="application/json" id="ModelJson-{{ section.id }}">
    {{ product.media | where: 'media_type', 'model' | json }}
  </script>
{% endunless %}

product-template.liquid上記のようなコードがあり、{{ product | json }}productオブジェクトをJSON形式で出力している。

このJSONを取得してoptions.productproductSingleObjectとして格納し、slate.Variantsに入れ、以下のようにカスタムイベントを設定し、各アクションに対してバリエーションの情報をJSで更新している。


_initVariants: function() {
  var options = {
    container: this.container,
    enableHistoryState:
      this.container.getAttribute('data-enable-history-state') || false,
    singleOptionSelector: this.selectors.singleOptionSelector,
    originalSelectorId: this.selectors.originalSelectorId,
    product: this.productSingleObject
  };

  this.variants = new slate.Variants(options);
  if (this.storeAvailability && this.variants.currentVariant.available) {
    this.storeAvailability.updateContent(this.variants.currentVariant.id);
  }

  this.eventHandlers.updateAvailability = this._updateAvailability.bind(
    this
  );
  this.eventHandlers.updateMedia = this._updateMedia.bind(this);
  this.eventHandlers.updatePrice = this._updatePrice.bind(this);
  this.eventHandlers.updateSKU = this._updateSKU.bind(this);

  this.container.addEventListener(
    'variantChange',
    this.eventHandlers.updateAvailability
  );
  this.container.addEventListener(
    'variantImageChange',
    this.eventHandlers.updateMedia
  );
  this.container.addEventListener(
    'variantPriceChange',
    this.eventHandlers.updatePrice
  );
  this.container.addEventListener(
    'variantSKUChange',
    this.eventHandlers.updateSKU
  );
},

テーマカスタマイザー

ロゴ画像の最大(最小)サイズを変更する


//header.liquidを開いて以下の部分を探す
{% schema %}
//以上省略
{
      "type": "range",
      "id": "logo_max_width",
      "label": {
        //省略
      },
      "min": 50,   //最小値はここを変更する
      "max": 250,  //最大値はここを変更する
      "step": 5,   //ここも必要に応じて変更する 最小は3
      "unit": "px",
      "default": 100  //minとmaxの間に存在する値でなければならない
    },
//以下省略
{% endschema %}
  • maxminに設定した数値はstepの倍数でなければいけない("step": 5なら5刻みで数値が変化するため、末尾が1から4だと警告が出て設定できない)
  • 刻む段階の上限は100のため、maxminの間の段階数が100を超えないようにstepを設定する
  • stepに設定できる最小値は3
  • defaultminmaxの間にある数値を設定しなければならない

古い情報

v17.4.1以前の古い情報を見る
Debut に使用されている画像スライダー(カルーセル)
  • Slickで実装されている
  • vendor.js内に、slick-slim.min.jsが格納されている
  • theme.js内に、Slickの設定用JSが記載されている(その他の各テンプレートに記載あり)
  • theme.scss.liquid内に、Slick用のCSSがある

テーマ用のJSとして全ページに読み込まれている模様。

Slickを使う場合には、Slick本体のJSは使わずに対応できるということになるが、実際のところ設定が複雑なのであらたにassetsディレクトリにslickのファイルを入れた方がよい場合もありそう。

商品ページのカルーセルのメイン画像に高さ制限を設定する

テーマエディタでFull-widthに指定した際に、横幅いっぱいが基準となり、縦長画像が設定された場合にあまりよろしくない見た目になる。

具体的な方法は以下の通り。

  1. section/product-template.liquidを開く
  2. ファイル上部のheightの値を書き換える

{%- comment -%}
  product-template.liquid の22行目あたりにある以下を見つける。
{%- endcomment -%}
{% case section.settings.media_size %}
  {%- comment -%}省略{%- endcomment -%}
  {% when 'full' %}
    {%- assign product_media_width = '' -%}
    {%- assign product_description_width = '' -%}
    {%- assign height = 1090 -%}
 
{%- comment -%}
 上記を以下のように書き換える。(heightを任意の高さで書き換え)
{%- endcomment -%}
{% case section.settings.media_size %}
  {%- comment -%}省略{%- endcomment -%}
  {% when 'full' %}
    {%- assign product_media_width = '' -%}
    {%- assign product_description_width = '' -%}
    {%- assign height = 400 -%}
SCSSのコンパイルエラー確認方法
  • SCSSに問題がある場合、コンパイルされずに422エラーになる
  • 開発者ツールでエラーを表示させ、エラーと共に記載されているURLをブラウザで開く
  • 問題のあった行数(恐らく最も上部にあるもののみ)が書かれているので問題の箇所が特定できる

結び

必要に応じて追記予定。

4人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。

コメント欄