storefront-builder について
storefront-builder は、Saleor のストアフロント構築を段階ごとに案内するエージェントスキルです。既存プロジェクトの規約に従い、指定されたステップだけを実行します。
主な機能
- 01Saleor GraphQL APIへの接続
- 02コード生成とクライアントの設定
- 03ストアフロントのデザイントークン定義
- 04商品一覧・詳細ページの構築
- 05価格・画像・在庫データの処理
活用シーン
- 01既存プロジェクトをSaleorに接続
- 02ストアフロントの配色と書体を決定
- 03商品一覧とバリエーション選択ページを作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ saleor/ agent-skills --skill storefront-builderインストール後、Claude Code で/storefront-builderを入力して読み込まれたか確認します。
詳細
Saleor Storefront Playbook
このスキルは Saleor のデータコントラクトと、UX/データレイヤーの動作を担当します。フレームワークの足場作り、CSS の設定、環境変数の読み込み方法は担当しません。エージェントは、これらをローカルプロジェクトから確認します。
$ARGUMENTS を解析して、実行するステップを特定してください。
ステップの振り分け
$ARGUMENTS の最初の単語をステップ番号として読み取り、該当するセクションに進んでください。そのステップだけを実行してから停止し、ユーザーが次のステップを依頼するまで待ってください。ステップを自動的に連続実行してはいけません。
1→ ステップ 1:プロジェクトの初期設定2→ ステップ 2:デザインと美しさ3→ ステップ 3:カタログ — 商品一覧+PDP
ステップが指定されていない場合、または認識できない場合は、次を出力してください。
Saleor Storefront Builder
Usage: /storefront-builder <step>
Steps:
1 Bootstrap — wire GraphQL client, codegen, Saleor API connection
2 Design & aesthetic — color palette, typography, accent color
3 Catalog — product list page + product detail page with variant selection
Example: /storefront-builder 1
ステップ 1:プロジェクトの初期設定
既存のプロジェクトを Saleor の GraphQL API に接続し、クライアントを適切に分離してコード生成を設定します。
0. Saleor インスタンスの確認
ユーザーに尋ねてください。
「Saleor インスタンスの準備はできていますか?
- いいえ — https://cloud.saleor.io/ で作成してください(無料プランを利用できます)。その後、API URL を持って戻ってきてください。
- はい — ストアフロント/API の URL を貼り付けてください。作業を開始します。」
続行する前にユーザーの返答を待ってください。まだインスタンスを用意していない場合は、ここで停止し、ユーザーが設定を完了するまで待ってください。URL が提供された場合は、ステップ 6 で使うために控えておいてください。
1. プロジェクトを調べる
package.json と、存在するフレームワーク設定ファイル(nuxt.config.ts、next.config.*、svelte.config.js、remix.config.js、vite.config.* など)を読み取り、次の内容を確認してください。
- フレームワークとバージョン
- 使用中のパッケージマネージャー(ロックファイルを確認:
pnpm-lock.yaml、yarn.lock、package-lock.json) - 既存の GraphQL 設定(ある場合)
- インポートエイリアスの規則(例:
@/、~/、#) - ソースディレクトリの構成(
src/、app/、ルート直下)
上記について質問してはいけません。プロジェクトから判断してください。必要な情報を特定できず、作業の続行に必要な場合にのみ質問してください。
2. AGENTS.md を作成する
リポジトリのルートに AGENTS.md がまだ存在しない場合は、ここで作成してください。これにより、このリポジトリで今後やり取りする際に、Saleor 固有のルールが AI ハーネスに適用されます。
インストール済みのスキルを確認してください。
ls .agent-skills/saleor-storefront/AGENTS.md 2>/dev/null && echo "STOREFRONT" || echo ""
ls .agent-skills/saleor-configurator/AGENTS.md 2>/dev/null && echo "CONFIGURATOR" || echo ""
存在するスキルへの @ 参照だけを含めて、AGENTS.md を記述してください。
# Saleor Storefront
This is a Saleor-powered storefront.
## Workflow
When running `/storefront-builder`, execute only the requested step, then stop and wait for the user to ask for the next one. Never chain steps automatically.
## Saleor rules
@.agent-skills/saleor-storefront/AGENTS.md
@.agent-skills/saleor-configurator/AGENTS.md
AGENTS.md がすでに存在する場合は、このステップ全体をスキップしてください。上書きしてはいけません。
3. GraphQL の依存関係をインストールする
ステップ 1 で確認したパッケージマネージャーを使ってください。
graphql-request graphql
@graphql-codegen/cli @graphql-codegen/client-preset (dev)
4. コード生成の設定を作成する
プロジェクトルートにコード生成設定ファイルを作成してください(ファイル名は、プロジェクトの規則に応じて codegen.ts または codegen.js)。設定する主な値は次のとおりです。
- schema:Saleor GraphQL API の URL。プロジェクトで使用している環境変数から読み込みます(設定されていない場合は
SALEOR_API_URL)。 - documents:ローカルの規則に従い、プロジェクトの GraphQL ファイルディレクトリを指す glob
- generates:
gqlTagName: "graphql"を指定してclientプリセットを使用
package.json に codegen スクリプトを追加してください。
5. Saleor API クライアントを作成する
2 クライアントパターン — これは Saleor の正しい実装に必須のルールです。省略できません。
プロジェクトのライブラリ/ユーティリティの規則に合う場所にクライアントモジュールを作成してください。2 つのクライアントをエクスポートします。
saleorClient — anonymous, no auth headers — safe for RSC, SSG, public product queries
saleorAuthClient — server-only, reads app token from env — NEVER use in browser bundles
**2 つのクライアントが重要な理由:**公開クエリやキャッシュ対象のクエリにアプリトークンを渡すと、特権アクセスが漏えいし、顧客データが露出する可能性があります。匿名クエリは匿名のままにしてください。
認証クライアントでは、トークンの環境変数が設定されている場合にのみ Authorization ヘッダーを含めてください(フロントエンド環境で変数が未設定でもモジュールがエラーにならないよう、条件分岐で保護してください)。
6. 環境を設定する
プロジェクトで使用している環境変数の命名規則を確認します(例:Next.js ではブラウザーからアクセスできる変数に NEXT_PUBLIC_* を使用し、Nuxt では NUXT_PUBLIC_* を使用します)。
必須の変数:
[PUBLIC_PREFIX]_SALEOR_API_URL— Saleor GraphQL エンドポイント[PUBLIC_PREFIX]_SALEOR_CHANNEL— デフォルトのチャネルスラッグSALEOR_APP_TOKEN(パブリックプレフィックスなし — サーバーサイド専用)
プロジェクトの環境ファイル(.env.local、.env など)に、プレースホルダー値とコメントを記述するか、既存のファイルを更新します。入力する Saleor API URL とチャネルスラッグがあるか、ユーザーに確認します。
ヒント — Configurator で既存のストアを調査する 既存の Saleor インスタンスにアクセスでき、設定されているチャネル、カテゴリ、商品がわからない場合は、Configurator CLI を使用します。
export SALEOR_URL=https://your-store.saleor.cloud/graphql/ export SALEOR_TOKEN=YOUR_TOKEN pnpm dlx @saleor/configurator introspect生成された
config.ymlを確認すると、正確なチャネルスラッグ、公開済みの商品、カテゴリ構造がわかります。これらの値を環境変数とクエリにそのまま使用します。
7. 検証する
API URL が設定されている場合は、コード生成を実行してスキーマに接続できることを確認します。
[package-manager] codegen 2>&1 | head -20
ネットワークエラーが発生した場合は、問題の原因を調査します(URL の誤り、認証情報の不足など)。
8. まとめ
[✓/–] AGENTS.md: [created / already existed]
✓ Framework: [detected framework]
✓ Package manager: [pm]
✓ Deps: graphql-request, @graphql-codegen/cli, @graphql-codegen/client-preset
✓ Clients: [path] (public + authenticated)
✓ Codegen: codegen.ts
[✓/⚠] API URL: [set / not set]
[✓/⚠] Channel: [slug / placeholder]
Next: /storefront-builder 2
まとめを出力したら、そこで停止してください。ユーザーから明示的に依頼されない限り、ステップ 2 に進まないでください。
ステップ 2:デザインと美的方向性
UI コードを書く前に、ストアフロントのビジュアルアイデンティティを定義します。このステップでは、以降のすべてのステップで読み込むテーマモジュールとデザイントークンを作成します。ファイルパスとトークン形式は、プロジェクトの既存の規約に従います。
1. プロジェクトのスタイリング設定を確認する
プロジェクトを読み、次の点を確認します。
- 使用している CSS フレームワーク(Tailwind、CSS Modules、styled-components、UnoCSS、vanilla CSS など)
- 既存のデザイントークンの規約(CSS カスタムプロパティ、
theme.*ファイル、Tailwind 設定など) - 共通スタイルの配置場所
Tailwind や特定の CSS の手法を決めつけず、プロジェクトから判断します。
2. 美的方向性について質問する
1 つのメッセージで、ユーザーに 3 つの質問を会話形式で尋ねます。フォームのような聞き方は避けてください。
「ストアフロントの見た目を決めましょう。いくつか簡単に質問します。
- 参考にしたいものはありますか?(ブランド、URL、スクリーンショットなど。なくてもかまいません)
- 全体的な雰囲気はどのようなものがよいですか?たとえば、ミニマルで明るい、ダークで高級感のある、大胆でカラフル、柔らかく温かみのある、クラシックでエディトリアルな雰囲気などです。ご自身の言葉で説明していただいてもかまいません。
- アクセントカラーの希望はありますか?ボタンやリンクに使います。16 進数、色名で指定するか、お任せください。」
ユーザーからの情報が少ない場合は、先に進む前に追加で 1 つ質問します。
3. トークンを決める
背景、サーフェス、ボーダー、テキスト(プライマリ)、テキスト(セカンダリ)、アクセント、アクセント(ホバー時)、ボーダー半径、見出しフォント、本文フォントの値を決めます。
4. テーマトークンを記述する
プロジェクトの規約に沿った場所にテーマモジュールを記述します。次の情報を含むコメントブロックを追加します。
- スタイルプリセット名
- 参考にしたもの(あれば)
- アクセントカラーを選んだ理由
- タイポグラフィの選択理由
プロジェクトの既存の規約に従って、トークンをスタイリングシステムに組み込みます。
- Tailwind:トークンの値を使って
tailwind.config.*を拡張する - CSS カスタムプロパティ:プロジェクトのグローバル CSS ファイルに記述する
- その他:既存の方式に従う
グローバル CSS またはベース CSS を更新し、背景とテキストのデフォルトを適用します。
5. まとめ
✓ Style: [preset name]
✓ Accent: [color]
✓ Typography: [font choice]
✓ Theme tokens: [path]
✓ Styling system updated: [tailwind.config / globals.css / etc.]
Next: /storefront-builder 3
まとめを出力したら、そこで停止してください。ユーザーから明示的に依頼されない限り、ステップ 3 に進まないでください。
ステップ 3:カタログ — 商品一覧と商品詳細ページ(PDP)
バリエーション選択機能を備えた商品一覧ページと商品詳細ページを作成します。
前提条件の確認
Saleor クライアントモジュールが存在することを確認します(Step 1 で設定した内容をもとに検索してください)。存在しない場合は、まず /storefront-builder 1 を実行するようユーザーに伝えます。
プロジェクトの env ファイルにチャネルスラッグがあるか確認します。見つからず、引数としても渡されていない場合は、次のように尋ねます。
"Saleor のチャネルスラッグは何ですか?(Saleor Dashboard → Channels、または Enter キーで 'default-channel')"
プロジェクトのフレームワークとルーティング規約を調べ、ページの作成場所とデータ取得の方法(サーバーコンポーネント、getStaticProps、ローダー、load 関数、asyncData など)を確認します。
1. GraphQL クエリ — Saleor のデータ契約
プロジェクトの GraphQL ドキュメントディレクトリに products.graphql ファイルを作成します。
ProductCard フラグメント
商品一覧に必要なフィールド:
fragment ProductCard on Product {
id
name
slug
thumbnail {
url
alt
}
pricing {
priceRange {
start {
gross {
amount
currency
}
}
}
}
category {
name
slug
}
}
これらのフィールドが必要な理由:
thumbnailは null の場合があるため、必ず代替画像またはプレースホルダーを用意してくださいpricing.priceRange.startは null の場合があるため、価格の表示前にチェックしてくださいcategoryは null の場合があるため、カテゴリラベルの表示前にチェックしてください
ProductDetails フラグメント
商品詳細ページに必要なフィールド:
fragment ProductDetails on Product {
id
name
slug
description
thumbnail {
url
alt
}
media {
url
alt
type
}
pricing {
priceRange {
start {
gross {
amount
currency
}
}
}
}
category {
name
slug
}
variants {
id
name
sku
pricing {
price {
gross {
amount
currency
}
}
priceUndiscounted {
gross {
amount
currency
}
}
}
selectionAttributes: attributes(variantSelection: VARIANT_SELECTION) {
attribute {
name
slug
}
values {
name
slug
}
}
quantityAvailable
}
}
これらのフィールドが必要な理由:
- 商品詳細ページでは
thumbnailよりmedia配列を優先し、mediaが空の場合はthumbnailを使用してください variants.pricingは null の場合があるため、amountにアクセスする前にチェックしてください- 匿名ユーザーの場合、
quantityAvailableは null になることがあります。null の場合は在庫ありとして扱ってください(在庫数を 1 とみなします) selectionAttributesはvariantSelection: VARIANT_SELECTIONフィルターを使用します。これにより、商品レベルの属性ではなく、バリアントを区別する属性(サイズ、色など)のみが返されます
クエリ
query ProductList($channel: String!, $first: Int = 20, $after: String) {
products(channel: $channel, first: $first, after: $after) {
edges {
node {
...ProductCard
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
query ProductBySlug($slug: String!, $channel: String!) {
product(slug: $slug, channel: $channel) {
...ProductDetails
}
}
チャネルは常に必須です — channel を指定しないクエリでは、価格や在庫状況のデータが返されません。
クエリを作成したら、codegen を実行します。
2. Saleor のデータ処理ルール
ページとコンポーネントを実装する際は、以下のルールに従ってください。
説明の解析
Saleor は description を EditorJS JSON として保存します。生のまま表示せず、安全に解析してください。
function extractDescriptionText(description: unknown): string {
try {
const parsed = typeof description === "string" ? JSON.parse(description) : description;
return parsed?.blocks
?.map((b: { data?: { text?: string } }) => b.data?.text ?? "")
.filter(Boolean)
.join(" ") ?? "";
} catch {
return "";
}
}
価格のフォーマット
通貨記号をハードコードせず、レスポンスの通貨を使って、常に Intl.NumberFormat を使用してください。
function formatPrice(amount: number, currency: string) {
return new Intl.NumberFormat(undefined, { style: "currency", currency }).format(amount);
}
ユーザーのブラウザーのロケールを尊重するには、ロケールに undefined を指定してください(または、プロジェクトにロケールシステムがある場合はロケールを渡してください)。
画像の処理
- 商品詳細ページでは、
thumbnailよりproduct.media[0]を優先し、mediaが空の場合はthumbnailを使用してください - 画像が欠けていないか必ず確認し、壊れた
<img>タグではなく、ニュートラルなプレースホルダーを表示してください - alt テキストがない場合は、
alt ?? product.nameを代替テキストとして使用してください
在庫/購入可能状況の意味
quantityAvailable === null→ 購入可能として扱います(匿名ユーザーには在庫情報が表示されません)quantityAvailable === 0→ 在庫切れです。選択できないようにし、視覚的なインジケーター(取り消し線または薄い表示)を付けてくださいquantityAvailable > 0→ 在庫ありです
バリアント選択の UX
- すべてのバリアントを表示し、在庫切れのものは非表示にせず選択できないようにしてください。表示することで、ユーザーはどの選択肢があるか把握できます
- 属性がある場合は、
selectionAttributesを使用してバリアントにラベル(例:「サイズ:M」「色:赤」)を付けてください - 商品にバリアントが 1 つしかなく、選択属性もない場合は、セレクターを省略して「カートに追加」に進んでください
- 選択したバリアントの
pricing.priceは、商品レベルのpricing.priceRangeより優先されます。選択時に表示価格を更新してください
空の状態/エラー状態
- 商品一覧に結果がない場合:問題解決のヒント(チャネルスラッグが間違っている、または商品が公開されていないなど)を添えた明確なメッセージを表示してください
- 商品が見つからない場合(
ProductBySlugが null):フレームワークの not-found/404 の仕組みを使用してください - 価格情報がない場合:$0 や NaN を表示せず、価格全体を省略してください
3. 共通ナビゲーションの作成
プロジェクトのコンポーネントディレクトリに、ローカルの命名規約に従ってナビゲーション/ヘッダーコンポーネントを作成します。ナビゲーションには、Step 2 で設定したテーマトークンを使用してください(Step 2 を省略した場合は、適切なニュートラルなデフォルト値を使用します)。
プロジェクトで確認したフレームワークの規約に従って、ナビゲーションをルートレイアウト/アプリシェルに組み込みます。
4. 商品一覧ページを作成する
プロジェクトのルーティング規則に合ったパス(例:app/page.tsx、pages/index.tsx、pages/index.vue、src/routes/+page.svelte、app/routes/_index.tsx)に商品一覧ページを作成します。
データ取得のパターン:フレームワークが提供する方法を使用します(非同期サーバーコンポーネント、getStaticProps/ISR、load 関数、asyncData、Remix loader など)。SSG に対応したフレームワークでは、適切な再検証間隔(例:60s)を設定します。
セクション 2 のすべてのデータ処理ルールを適用します。null 許容値のチェック、価格の適切なフォーマット、空状態の表示を行います。
5. PDP を作成する
ルーティング規則に合ったパス(例:app/p/[slug]/page.tsx、pages/p/[slug].tsx、pages/p/[slug].vue、src/routes/p/[slug]/+page.svelte)に PDP を作成します。
セクション 2 のすべてのデータ処理ルールを適用します。
6. VariantSelector コンポーネントを作成する
プロジェクトのコンポーネントディレクトリに VariantSelector コンポーネントを作成します。クライアント側で操作できるようにしてください(React state、Vue ref、Svelte store など、フレームワークのインタラクティビティ機能を使用します)。
動作:
- すべてのバリエーションを表示し、在庫切れのものは無効にします(非表示にはしません)。
- 選択中のバリエーションを強調表示します。
- バリエーションを選択すると、表示価格を更新します(バリエーションの
pricing.priceが優先されます)。 - バリエーションの選択が必須の場合、選択するまで「カートに追加」ボタンを無効にします。
- 単一バリエーションの商品/属性のない商品では、セレクターを省略し、「カートに追加」を直接表示します。
- この段階では「カートに追加」は機能しません。プレースホルダーのみとし、その旨をコメントで明記します。
7. 実行して確認する
プロジェクトの開発コマンドで開発サーバーを起動します。データが正しく読み込まれることを確認するため、商品一覧と PDP の URL をユーザーに案内します。
よくある問題:
- 一覧が空:チャンネルのスラッグが間違っているか、そのチャンネルで商品が公開されていません。
configurator introspectを実行してストアを調べるよう提案します。 - コード生成エラー:API URL が設定されていないか、アクセスできません。
- どのスラッグでも商品が見つからない:チャンネルが一致していないか、そのチャンネルで商品が公開されていません。
概要
✓ GraphQL queries: [path]/products.graphql
✓ Types generated
✓ Navigation: [path] (wired into root layout)
✓ Product list: [route]
✓ Product detail: [route]
✓ VariantSelector: [path]
Note: "Add to Cart" is present but non-functional — checkout is not covered by this skill
This is the last step currently available in this skill.
概要を出力した後、停止してください。
Saleor の正確性に関するルール(常に適用)
これらのルールは、すべての手順と今後のストアフロント作業に適用されます。
- 必ず
channelを渡す — 商品/価格/在庫状況に関するすべてのクエリで必要です。省略するとデータが返されません。 descriptionを安全に解析する — これは EditorJS の JSON であり、プレーンテキストや HTML ではありません。SALEOR_APP_TOKENをブラウザーに絶対に公開しない — 2 クライアントパターンを使用します。認証クライアントはサーバー側でのみ使用します。quantityAvailableが null の場合は購入可能 — 匿名ユーザーには在庫数が返されません。null は「購入を制限しない」という意味です。- すべての階層で
pricingは null になり得る —amountにアクセスする前に、pricing、pricing.price、pricing.priceRange、grossをチェックします。 - 価格には
Intl.NumberFormatを使用する — 通貨記号をハードコードしたり、ロケールを決めつけたりしてはいけません。 - PDP のメディアの優先順位:
media[0]→thumbnail→ プレースホルダー - 在庫切れのバリエーションは非表示にせず、無効にする — 非表示にすると、商品の選択肢についてユーザーを混乱させます。