食品をネットで売るとき、商品ページに載せたい情報は決まっています。熱量・たんぱく質・脂質・炭水化物・食塩相当量の5項目と、アレルゲン。ところが Shopify の標準機能には、この5項目とアレルゲンを商品ごとに持たせる場所がありません。商品説明に手で書き足していくと、味違い・サイズ違いが増えたときに管理しきれなくなります。
「シンプル栄養成分・アレルギー表示|食品表示」は、その部分だけを受け持つアプリです。商品のバリエーションごとに数値とアレルゲンを登録すると、商品ページに栄養成分表とアレルゲンのバッジが出ます。お客様が味を選び直せば、表示も自動で入れ替わります。
この記事では、管理画面の4つの画面を1つずつ、設定項目を全部たどって解説します。テーマエディタの17項目のデザイン設定も、既定値と効果まで書きます。最後に、アプリを使わずコードで自作する場合の実装例も置きました。
食品・飲料・サプリメントを扱う Shopify ストア向けの、栄養成分表示とアレルゲン表示のアプリです。開発は福岡の株式会社UnReact です。
できることは5つです。
特徴は「バリエーション単位」であることです。同じグラノーラバーでも、ハニーアーモンド味には小麦とアーモンド、ダークチョコレート味には乳と小麦、というように中身が変わります。商品単位でしか持てないアプリだと代表的な1つしか出せませんが、このアプリは味ごとの違いをそのまま出せます。
もう1つは、アレルゲンの扱いです。消費者庁の食品表示基準にある特定原材料8品目と、表示が推奨される20品目、合わせて28品目を、義務と推奨に分けて選べます。何をどこまで書くべきかが、選びながら分かる作りになっています。
味・サイズ・容量で成分が変わる商品でも、正確な数値を出せます。商品説明に「※味により異なります」と書いて逃げる必要がありません。
あらかじめ用意されているのは5項目ですが、項目名も単位も書き換えられます。食物繊維・糖質・カルシウム・ビタミンCといった項目を足すこともできます。件数に上限はありません。
食品表示の実務では「1.5未満」「0」のような書き方をします。このアプリは値を数値に限定していないので、そのまま入力してそのまま表示できます。数値しか受け付けないアプリだと、この時点で使えません。
商品ページに出る「栄養成分」「アレルゲン」の見出しと、アレルゲンの品目名は、お客様が見ている言語に自動で合わせて表示されます。日本語で「小麦」にチェックを入れておけば、英語のお客様には「Wheat」と出ます。
テーマエディタから、レイアウト・文字サイズ・色・枠線・余白を調整できます。CSS を書かなくても、ストアの雰囲気に寄せられます。
正直に書きます。導入前に知っておいたほうがよい点が4つあります。
自動で言語が切り替わるのは、見出しとアレルゲンの品目名だけです。項目名と単位は、管理画面で入力した文字がそのまま出ます。「Calories」と入力すれば、日本語のお客様にも「Calories」と表示されます。多言語で運用するストアでは、ここが手当てされていないことを分かったうえで使う必要があります。
管理画面の一覧は「登録した商品の管理台帳」です。ストアの全商品を一覧して登録漏れを探す、という使い方はできません。法令上の網羅チェックはアプリの責務外という設計です。
Shopify 側の検索では「栄養成分を登録済みの商品」だけを直接絞り込めないため、アプリが商品を順番に読みながら拾っています。1回の表示で読む上限は250商品です。カタログが大きく、登録済み商品が後ろのほうに固まっていると、登録してあるのに一覧に出てこないことがあります。その場合は商品名で検索すれば見つかります。
お客様がバリエーションを切り替えたときの表示更新は、テーマが出すイベントを拾って行っています。対応しているのは Dawn 系のテーマ、Horizon、および一般的な切り替えイベントを出すテーマの3通りです。独自テーマではエラーも出ないまま、最初に表示されたバリエーションのまま固定されます。
プランは1つだけです。
無料体験のあいだに、手持ちの商品で商品ページの見え方まで確認できます。プランの選択肢が1つなので、機能制限で迷うことはありません。
Shopify のアプリはすべて Shopify App Store から入れます。
Shopify 管理画面の「設定」から「アプリと販売チャネル」を開き、Shopify App Store へ進みます。検索窓に「栄養成分」「アレルゲン」「食品表示」などを入れると見つかります。

アプリの詳細ページで「インストール」を押します。料金と無料体験の有無も、この画面で確認できます。権限の確認画面が出るので、内容を見て進めるとインストールが完了します。このアプリが扱うのは商品とテーマの情報だけで、顧客情報や注文情報には触れません。

管理画面は4つの画面でできています。サイドバーから直接入れるのは「栄養成分一覧」と「設定」の2つで、残りの2つはそこから移動します。

以下、この4画面を1つずつ見ていきます。
アプリを開くと最初に出る画面です。栄養成分かアレルゲンを1つでも登録済みの商品だけが並びます。まだ何も登録していない商品は出てきません。
一覧に出る列は5つです。

登録状況のバッジは2種類だけです。「未登録」というバッジは出ません。一覧に出ている時点で最低1バリエーションは登録済みだからです。「一部未登録」が出ていたら、まだ数値を入れていないバリエーションが残っているという意味なので、編集画面で埋めます。
「商品名で検索」に文字を入れて「検索」を押すと、商品名で絞り込めます。商品数が多いストアでは、一覧をたどるより検索のほうが早く目的の商品に着きます。

この検索は Shopify 側の商品検索に渡されたうえで、登録済みかどうかの絞り込みが重ねてかかります。前述の「250商品で打ち切る」制約に当たってしまい一覧に出ない商品も、名前が分かっていればここから見つけられます。
一覧は1回に20件ずつ表示され、続きがあるときは下に「もっと見る」ボタンが出ます。増える件数は毎回きっちり20件とは限らず、ページごとにばらつきます。読んだ範囲から登録済みのものだけを拾う仕組みのためです。
画面右上の「商品を追加」を押すと、Shopify の商品ピッカーが開きます。

ピッカーはバリエーション単位で中身が見えるので、どの商品にどんな味があるかを確認しながら選べます。商品を選んで「追加」を押すと、その商品の編集画面へそのまま移動します。
知っておくとよい挙動が2つあります。1つは、この時点では一覧に追加されないことです。編集画面で栄養成分かアレルゲンを保存して初めて一覧に載ります。もう1つは、複数選んでも先頭の商品の編集画面だけが開くことです。残りの商品へ続けて移動する導線は用意されていないので、1商品ずつ登録していく使い方になります。
一覧の「編集」、または商品ピッカーで商品を選んだときに開く画面です。その商品が持つ全バリエーションが、上から順にカードとして並びます。

まだ何も登録していないバリエーションを開くと、消費者庁の食品表示基準の表示順に沿って5項目があらかじめ並んでいます。上から順に、熱量(kcal)、たんぱく質(g)、脂質(g)、炭水化物(g)、食塩相当量(g)です。この順番は食品表示の様式に合わせてあるので、並べ替えずにそのまま使えます。
カードの見出しはバリエーション名です。SKU が設定されている場合は、見出しの下に SKU も出ます。後述の CSV 一括登録は SKU で照合するので、ここに SKU が出ていないバリエーションは CSV では登録できません。
各項目は「項目名」「値」「単位」の3つの入力欄でできています。

値を文字列として保存しているのは、食品表示の実務に合わせるためです。「0」と「0g未満」と「微量」は意味が違いますが、数値型で持つとこの違いが表現できません。
同じカードの下半分がアレルゲンの選択欄です。チェックボックスが2グループに分かれています。

上が 特定原材料(表示義務8品目) です。卵、乳、小麦、そば、落花生(ピーナッツ)、えび、かに、くるみの8つで、表示が義務づけられている品目です。

下が 特定原材料に準ずるもの(表示推奨20品目) です。アーモンド、あわび、いか、いくら、オレンジ、カシューナッツ、キウイフルーツ、牛肉、ごま、さけ、さば、大豆、鶏肉、バナナ、豚肉、まつたけ、もも、やまいも、りんご、ゼラチンの20品目です。
義務と推奨が見出しで分かれているので、最低限どこまで入れるべきかが選びながら分かります。合わせて28品目です。
保存されるのは品目名そのものではなく、品目を表す内部の記号です。そのため商品ページに出る品目名は、お客様が見ている言語に自動で合わせて表示されます。
カードの一番下にある「保存」を押します。保存はバリエーションごとに独立しています。 1つのカードで保存しても、他のカードの入力内容は保存されません。複数のバリエーションを編集したときは、カードごとに「保存」を押す必要があります。

保存に成功すると、そのカードの上に緑色の「保存しました」が出ます。画面の隅に出るトーストではなく、カードの中に出ます。どのバリエーションが保存されたのかが見て分かる作りです。

入力した内容は Shopify のメタフィールドに保存されます。アプリ専用の領域なので、他のアプリの設定と混ざることはありません。外部のデータベースは使っていません。
商品数が多いときは、1商品ずつ入力せず CSV でまとめて登録できます。一覧の右上「CSV一括登録」から開きます。

使える列は8つです。1行目にヘッダー、2行目以降にデータを書きます。文字コードは UTF-8、区切りはカンマです。
必須なのは variant_sku だけです。値を入れなかった列は登録されません。
「CSVファイルを選択」でファイルを選ぶと、すぐにプレビューが出ます。この時点ではまだ何も登録されていません。

プレビューには「行」「SKU」「注意」の3列が出ます。行はファイルの行番号で、ヘッダーを1行目として数えます。最初のデータ行は2になります。

「注意」には、そのまま登録すると一部が無視される場合の警告が出ます。出るのは2種類です。
どちらも登録は止まりません。気づかずに進めると一部だけ抜けた状態になるので、プレビューの「注意」列は必ず目を通してください。
プレビューの下にある「登録」を押すと処理が始まり、終わると結果が出ます。

結果には成功件数と、SKU が見つからなかった件数が出て、見つからなかった SKU は一覧で表示されます。SKU が一致しない行はスキップされ、処理自体は最後まで続きます。 途中で止まらないので、打ち間違えた行だけ直して再実行できます。

実運用で知っておくとよい点が3つあります。1つ目は、同じ SKU が複数のバリエーションに付いている場合、最初に見つかった1件だけが対象になることです。2つ目は、送信が12バリエーションずつに分けて行われることで、行数が多いほど時間がかかります。3つ目は、数百行を超える CSV での実行時間が検証されていないことです。初めて大量に登録するときは、数十行ずつに分けて試すのが安全です。
サイドバーの「設定」から開きます。この画面ですることは2つで、テーマへの追加と、管理画面の言語切り替えです。


「テーマを選択」で対象のテーマを選び、「テーマに追加」を押します。公開中のテーマには「(公開中のテーマ)」と付くので、下書きのテーマと取り違えにくくなっています。ボタンは2つです。
ここで大事なのは、追加先が商品ページに固定されていることです。トップページやコレクションページではありません。

画面には2つの案内が出ています。1つは、テーマのバージョンによっては自動追加がうまくいかないことがあるという注意です。その場合はテーマエディタで商品ページを開き、「ブロックを追加」から手動で選びます。もう1つは、商品ページ以外にも置きたい場合はテーマエディタで手動追加する、という案内です。

「言語を選択」で言語を選び、「変更する」を押すと、アプリの管理画面の表示言語が変わります。対応しているのは日本語、英語、チェコ語、デンマーク語、ドイツ語、スペイン語、フィンランド語、フランス語、イタリア語、韓国語、ノルウェー語(ブークモール)、オランダ語、ポーランド語、ポルトガル語(ブラジル)、ポルトガル語(ポルトガル)、スウェーデン語、タイ語、トルコ語、中国語(簡体字)、中国語(繁体字)の20言語です。
何も設定しない場合は、Shopify 管理画面のアカウント言語に自動で合わせます。ここで明示的に選ぶと、そちらが優先されます。
この設定はブラウザごとに保存されます。別のパソコンやブラウザでアプリを開いたときは、そちらであらためて設定してください。ストア全体の設定ではないので、他のスタッフの表示には影響しません。
「テーマに追加」を押すと、テーマエディタが新しいタブで開きます。

左のサイドバーに「栄養成分表示ラベル」というブロックが追加された状態になります。ここで**「保存」を押すまで、この追加は反映されません**。追加したのに商品ページに出ないときは、まずここを疑ってください。
位置を変えたい場合は、サイドバーでブロックをドラッグします。商品説明の直後でも、カートボタンの下でも、ストアの構成に合わせて置けます。
ブロックを選ぶと、右側に設定パネルが開きます。設定は「商品」「デザイン設定」「上級者向け」の3つのまとまりに分かれていて、全部で17項目あります。
「商品」は、どの商品の栄養成分を出すかを指定する項目です。商品ページに置いた場合は、そのページで表示している商品が自動で入ります。通常は触る必要がありません。商品ページ以外に置いて特定の商品を出したいときだけ、ここで商品を選びます。

レイアウトは見た目だけの違いではありません。表形式は本物の表として、カード形式は定義リストとして出力しています。どちらを選んでも読み上げソフトが正しく読めるように作り分けられているので、好みで選んで構いません。
背景色の既定が透明なので、初期状態ではテーマの背景がそのまま透けます。ブロックだけ色を付けて目立たせたいときに変更します。

角丸の既定値 24px は指定できる最大値です。アレルゲンバッジは高さが小さいため、初期状態では角丸というより丸みを帯びた形になります。角張らせたいときは 0 に近づけます。

カードの間隔とカードの列数は、レイアウトが「カード形式」のときだけ効きます。表形式のままで変更しても見た目は変わりません。ここは画面上に説明が出ないので、変えても何も起きないと感じたらレイアウトを確認してください。
列数の 0 は「自動」で、画面幅に合わせて折り返します。スマートフォンからの購入が多いストアでは、0 のままにしておくほうが安全です。列数を固定すると狭い画面で横に潰れることがあります。

「追加のCSS」は、このブロックにだけ適用する CSS を書ける欄です。画面にも「セレクタは含めないでください」と注意が出ているとおり、書くのは中身のプロパティだけです。
たとえば次のように書きます。
letter-spacing: 0.02em;
line-height: 1.8;
.my-class { ... } のようにセレクタから書く必要はありません。ここに書いた内容はこのブロックの中だけに閉じ込められるので、うっかりテーマ全体のレイアウトを壊してしまう心配はありません。同じページに複数のブロックを置いても、設定が混ざることはありません。
ここまでの設定が、商品ページではこう見えます。

商品説明の下に、栄養成分の表とアレルゲンのバッジが並びます。お客様は商品ページを離れることなく、気になる数値とアレルゲンをその場で確かめられます。

見出しの「栄養成分」は、お客様が見ている言語に合わせて表示されます。表の中の項目名は、管理画面で入力した文字がそのまま出ます。

アレルゲンはバッジで並びます。この品目名も、お客様の言語に合わせて表示されます。管理画面で「小麦」にチェックを入れておけば、英語のお客様には「Wheat」と出ます。
お客様が味を選び直すと、数値もアレルゲンも入れ替わります。ページの再読み込みは起きません。

ハニーアーモンドでは 190kcal・小麦・アーモンドだったものが、ダークチョコレートでは 210kcal・乳・小麦に変わっています。仕組みとしては、最初にページを読み込んだ時点で全バリエーションぶんのデータをページに埋め込んでおき、切り替えを検知して表示だけ差し替えています。サーバーへの問い合わせが発生しないので、切り替えが速いのが利点です。
データを登録していないバリエーションに切り替えると、そのセクションは非表示になります。前のバリエーションの数値が残ってしまうことはありません。
なお、その商品のどのバリエーションにもデータが1件も無い場合は、ブロックごと何も出力されません。対象外の商品に空の見出しだけが残ることを避けるための仕様です。
アプリを使わずに同じことをするなら、メタフィールドと Liquid で組むことになります。どのくらいの手間になるかを見ておくと、アプリを使う判断がしやすくなります。
まず、バリエーションに栄養成分を持たせる場所を作ります。Shopify 管理画面の「設定」から「メタフィールドとメタオブジェクト」を開き、バリエーション(Variant)に対して JSON 型のメタフィールドを定義します。名前空間とキーを決め、ストアフロントから読めるようにアクセス権を公開に設定します。
この時点で、管理画面から1件ずつ JSON を手で書くことになります。次のような形です。
[
{ "label": "熱量", "value": "190", "unit": "kcal" },
{ "label": "たんぱく質", "value": "4", "unit": "g" }
]
バリエーションが3つある商品なら3回、10商品あれば30回この作業をします。入力欄はただのテキストエリアなので、カンマや括弧を1つ落とすと壊れますが、その場ではエラーになりません。壊れていることに気づくのは、商品ページで表示が消えたときです。
次に、テーマの商品テンプレートに表示用のコードを足します。セクションかスニペットを作り、次のように書きます。
{%- assign nf = product.selected_or_first_available_variant.metafields.custom.nutrition_facts.value -%}
{%- if nf and nf.size > 0 -%}
<div class="my-nutrition">
<h2>栄養成分</h2>
<table>
{%- for item in nf -%}
<tr>
<th>{{ item.label }}</th>
<td>{{ item.value }}{{ item.unit }}</td>
</tr>
{%- endfor -%}
</table>
</div>
{%- endif -%}
selected_or_first_available_variant で、いま選ばれているバリエーションを取ります。データが空のときに空の見出しだけが残らないよう、if で件数を見てから出力しているのが要点です。ここまでで、ページを開いた時点の表示はできます。
問題はここからです。上の Liquid はサーバー側で1回描画されるだけなので、お客様がバリエーションを切り替えても表示は変わりません。追従させるには、全バリエーションぶんのデータをページに埋め込んでおき、切り替えイベントを拾って JavaScript で書き換えます。
<script type="application/json" data-nutrition-source>
{
{%- for v in product.variants -%}
"{{ v.id }}": {{ v.metafields.custom.nutrition_facts.value | json }}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
}
</script>
そのうえで、テーマが出すイベントを購読します。ここが自作でいちばん手間のかかるところで、テーマによってイベントが違います。
const root = document.querySelector('.my-nutrition');
const data = JSON.parse(document.querySelector('[data-nutrition-source]').textContent);
const render = (variantId) => {
const items = data[variantId];
// データが無いバリエーションでも必ず呼ぶこと。呼ばないと前の表示が残る
root.hidden = !items || items.length === 0;
if (!root.hidden) {
root.querySelector('table').innerHTML = items
.map((i) => `<tr><th>${i.label}</th><td>${i.value}${i.unit}</td></tr>`)
.join('');
}
};
// Dawn 系
if (window.subscribe && window.PUB_SUB_EVENTS) {
window.subscribe(window.PUB_SUB_EVENTS.optionValueSelectionChange, (e) => render(e.data.variant.id));
}
// Horizon
document.addEventListener('shopify:product:select', (e) => render(e.detail.variantId));
// その他
document.addEventListener('variant:change', (e) => render(e.detail.variant.id));
コメントに書いたところが、実際に踏みやすい落とし穴です。データが無いバリエーションのときに「何もしない」と書いてしまうと、前のバリエーションの栄養成分が残ったまま表示されます。エラーは出ないので、切り替えて確かめるまで気づけません。
さらに、埋め込んだ <script> タグの置き場所にも注意が要ります。ブロックの外側に置くと、querySelector で見つからず、イベントの購読そのものが登録されません。この場合も初期表示だけは正しく出るので、見た目では検出できません。
ここまでで、自作に必要な作業はこうなります。
アプリを使う場合は、管理画面で数値を入れて保存し、テーマにブロックを追加するだけです。月額 $2.99 と、上の作業とテーマ変更のたびの保守を比べて判断することになります。CSV 一括登録が要る規模なら、自作でその仕組みまで作るのはかなりの負担です。
導入して表示されない場合、確認する順番はこうです。
バリエーションを切り替えても表示が変わらない場合は、まず Dawn や Horizon など標準のテーマで試して、テーマ側の問題かどうかを切り分けます。独自テーマでイベントが出ていない場合は、初期表示のまま固定されます。
項目名が英語のまま出る場合は仕様どおりです。項目名と単位は入力した文字がそのまま出るので、日本語で出したい場合は編集画面で書き換えてください。
「シンプル栄養成分・アレルギー表示|食品表示」は、食品を扱う Shopify ストアが商品ページに載せたい情報を、追加の制作費をかけずに整えるためのアプリです。
料金は月額 $2.99、年額 $29.99 で、7日間の無料体験が付いています。項目名が翻訳されないことと、一覧が登録済み商品だけであることを理解したうえで使えば、食品表示まわりの運用はかなり楽になります。
この記事の内容は、以下をもとにしています。