关于 storefront-builder
storefront-builder 是指导 AI Agent 分步构建 Saleor 店面的技能,涵盖项目连接、视觉设计和商品目录。它强调遵循项目现有约定,并仅执行用户指定的步骤。
主要功能
- 01连接 Saleor 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 操作指南
此技能负责 Saleor 数据契约和 UX/数据层行为。它不负责框架脚手架、CSS 配置或环境变量加载细节——Agent 会从本地项目中了解这些内容。
解析 $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 返回。
- 有——请粘贴你的 storefront/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:使用
client预设,并设置gqlTagName: "graphql"
在 package.json 中添加 codegen 脚本。
5. 创建 Saleor API 客户端
双客户端模式——这是 Saleor 的正确性要求,不可省略:
在符合项目库/工具函数约定的位置编写客户端模块,并导出两个客户端:
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
**为什么双客户端很重要:**在公开/可缓存查询中传递应用令牌会泄露特权访问权限,并可能暴露客户数据。匿名查询必须保持匿名。
只有在设置了令牌环境变量时,认证客户端才应包含 Authorization 标头(使用条件判断,以免在缺少该变量的前端环境中导致模块抛出错误)。
6. 配置环境
根据项目确定环境变量命名约定(例如,Next.js 使用 NEXT_PUBLIC_* 表示可在浏览器访问的变量,Nuxt 使用 NUXT_PUBLIC_*,等等)。
必需变量:
[PUBLIC_PREFIX]_SALEOR_API_URL— Saleor GraphQL 端点[PUBLIC_PREFIX]_SALEOR_CHANNEL— 默认渠道 slugSALEOR_APP_TOKEN(无公开前缀——仅限服务端使用)
在项目的环境文件(.env.local、.env 等)中写入或更新占位值和注释。询问用户是否有 Saleor API URL 和渠道 slug,以便填写。
提示——使用 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,查找准确的渠道 slug、已发布商品和类别结构——在环境变量和查询中直接使用这些值。
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、原生 CSS 等)
- 现有的设计令牌约定(CSS 自定义属性、
theme.*文件、Tailwind 配置等) - 共享样式所在的位置
不要假设项目使用 Tailwind 或任何特定的 CSS 方案——应根据项目实际情况判断。
2. 询问用户偏好的美感
在一条消息中向用户提出三个问题——采用对话式语气,而非表单:
“我们来确定一下店面的视觉风格。有几个简单的问题:
- 你有喜欢的参考吗?(某个品牌、URL、截图——也可以跳过)
- 整体感觉希望是什么样的?如果有帮助,可以从这些风格开始:简约明亮、暗调奢华、大胆多彩、柔和温暖、经典编辑风——也可以用自己的话描述。
- 你有想要的强调色吗?它会用于按钮和链接。可以提供十六进制值、颜色名称,或者交给我决定。”
如果用户提供的信息很少,继续之前先追问一次。
3. 确定令牌
确定以下值:背景、表面、边框、主文本、次级文本、强调色、悬停强调色、边框圆角、标题字体、正文字体。
4. 编写主题令牌
按照项目的现有约定,在相应位置编写主题模块。添加注释块,记录:
- 样式预设名称
- 参考来源(如有)
- 强调色的选择理由
- 字体选择
按照项目本地约定,将令牌接入样式系统:
- Tailwind:使用令牌值扩展
tailwind.config.* - 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 文件中是否有频道 slug。如果缺失且未作为参数传入,请询问:
"你的 Saleor 频道 slug 是什么?(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可为空 — 始终使用备用图片或占位图进行保护pricing.priceRange.start可为空 — 渲染价格前进行保护category可为空 — 渲染类别标签前进行保护
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
}
}
这些字段的原因:
- PDP 上优先使用
media数组而非thumbnail— 当media为空时,使用thumbnail作为备用 variants.pricing可为空 — 访问amount前进行保护- 匿名用户的
quantityAvailable可为空 — 将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,以遵循用户的浏览器区域设置(如果项目有区域设置系统,也可以传入区域)。
图片处理
- 在 PDP 上:优先使用
product.media[0],而非thumbnail;如果media为空,则回退到thumbnail - 始终处理图片缺失的情况 — 渲染中性占位图,而不是损坏的
<img>标签 - 将
alt ?? product.name用作备用替代文本
库存 / 可用性语义
quantityAvailable === null→ 视为有货(匿名用户看不到库存)quantityAvailable === 0→ 缺货 — 禁用选择并显示视觉指示(删除线或弱化显示)quantityAvailable > 0→ 有货
变体选择体验
- 显示所有变体;缺货变体应禁用,而非隐藏 — 让用户了解有哪些选项
- 存在
selectionAttributes时,用它们标注变体(例如“尺码:M”“颜色:红色”) - 如果商品只有一个变体且没有选择属性,则跳过选择器,直接显示“加入购物车”
- 用户选择的变体的
pricing.price会覆盖商品级的pricing.priceRange— 选择变体时更新显示的价格
空状态 / 错误状态
- 商品列表没有结果:显示清晰的消息并附带排查提示(频道 slug 错误或商品尚未发布)
- 找不到商品(
ProductBySlug返回 null):使用框架的未找到/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 节中的所有数据处理规则:防护可空值、正确格式化价格、显示空状态。
5. 编写 PDP
按照路由约定,在合适的路径编写 PDP(例如 app/p/[slug]/page.tsx、pages/p/[slug].tsx、pages/p/[slug].vue、src/routes/p/[slug]/+page.svelte)。
应用第 2 节中的所有数据处理规则。
6. 编写 VariantSelector 组件
在项目的组件目录中编写 VariantSelector 组件。它必须支持客户端交互(使用框架提供的任意交互原语,例如 React state、Vue ref、Svelte store 等)。
行为:
- 显示所有变体;对缺货变体禁用(不要隐藏)
- 突出显示已选变体
- 选择变体后更新显示的价格(变体的
pricing.price优先) - 在选择变体为必需项时,选中变体前禁用 Add to Cart 按钮
- 单变体/无属性商品:跳过选择器,直接显示 Add to Cart
- 此步骤中 Add to Cart 不具备实际功能——仅作占位,请在注释中明确说明
7. 运行并验证
使用项目的开发命令启动开发服务器。引导用户访问商品列表页和 PDP URL,以确认数据加载正常。
常见问题:
- 列表为空:频道 slug 错误,或商品未在该频道发布——建议运行
configurator introspect检查商店 - Codegen 错误:API URL 未设置或无法访问
- 所有 slug 都找不到商品:频道不匹配,或商品未在该频道发布
总结
✓ 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——使用双客户端模式;认证客户端仅在服务端使用 quantityAvailable为 null = 有货——匿名用户不会收到库存数量;null 表示“不要阻止购买”- 每一层的
pricing都可能为 null——访问amount前,请检查pricing、pricing.price、pricing.priceRange和gross - 使用
Intl.NumberFormat格式化价格——切勿硬编码货币符号或假设区域设置 - PDP 媒体优先级:
media[0]→thumbnail→ 占位图 - 禁用缺货变体,而不是隐藏它们——隐藏会让用户对商品提供哪些选项感到困惑