免费

样片作品集

把作品集做成一张摄影样片:胶片条、红色油性笔、放大镜和暗房模式。一个离线文件,没有图片也完整好看。

安装

把“样片作品集”技能安装到 ~/.claude/skills/contact-sheet-portfolio。
下载下面每个文件,保存到这个文件夹:
https://skilllift.pages.dev/packs/contact-sheet-portfolio/SKILL.zh.md → SKILL.md
https://skilllift.pages.dev/packs/contact-sheet-portfolio/references/style.md → references/style.md
https://skilllift.pages.dev/packs/contact-sheet-portfolio/assets/template.html → assets/template.html
https://skilllift.pages.dev/packs/contact-sheet-portfolio/scripts/build.mjs → scripts/build.mjs
https://skilllift.pages.dev/packs/contact-sheet-portfolio/scripts/check.mjs → scripts/check.mjs
https://skilllift.pages.dev/packs/contact-sheet-portfolio/scripts/drive.mjs → scripts/drive.mjs
然后读一遍 SKILL.md,告诉我它是做什么的,并提醒我开一个新会话。

解压到 ~/.claude/skills

同一个技能,不同的需求

左边是预览。右边由另一个 Agent 按不同需求制作,它只有安装好的技能,没看过预览。

预览
预览
新需求,由另一个 Agent 制作
新需求,由另一个 Agent 制作

这是按虚构需求做的一次测试。系统字体不同时,字形可能略有差异。

把独立设计师、摄影师、插画师或小工作室的作品集做成一个可离线打开的 HTML 文件,样子是一张摄影样片:黑色胶片条、帧号、红色油性笔的圈和叉、悬停放大镜、看片台和暗房两种模式。所有内容都从一段 JSON 渲染。没有图片也能用,因为它会根据每个项目的资料生成抽象画面,并标明是占位图;提供图片后自动替换。本人的作品永远完整显示,保持原有比例,不加标记,可以打开大图。有人要作品集、个人作品网站、工作室网站,或者点名要这个风格时使用。

交付前必须通过

  • check.mjs 对最终文件退出码为 0,且没有用 --static-only。
  • drive.mjs 显示 0 失败。
  • 我看过每一页的截图,1280 和 390 都看了,看片台和暗房都看了。
  • 每个合作方、评价、奖项、价格和数字都来自对方。
  • 没给的价格写成 null 或 [],并告诉了对方是哪些。
  • 提供的图片都完整显示,有替代文字,可以打开大图。
  • 页面上还留着的“占位图”,都告诉过对方。
  • 没有残留示例内容。虚构标注只用于编出来的人。
  • 推断的内容和假设都列在消息里。
阅读完整 SKILL.md
---
name: contact-sheet-portfolio
description: 把独立设计师、摄影师、插画师或小工作室的作品集做成一个可离线打开的 HTML 文件,样子是一张摄影样片:黑色胶片条、帧号、红色油性笔的圈和叉、悬停放大镜、看片台和暗房两种模式。所有内容都从一段 JSON 渲染。没有图片也能用,因为它会根据每个项目的资料生成抽象画面,并标明是占位图;提供图片后自动替换。本人的作品永远完整显示,保持原有比例,不加标记,可以打开大图。有人要作品集、个人作品网站、工作室网站,或者点名要这个风格时使用。
version: 1.0.0
---

# 样片作品集

样子和全部功能都在 `assets/template.html` 里。你要做的只是写一份 JSON。`scripts/build.mjs` 会把它和图片一起嵌进模板的副本。不要改 HTML、CSS 或 JavaScript:`scripts/check.mjs` 会逐字节对比引擎和模板。

需要具体的样式数值或规则背后的理由时,再读 `references/style.md`。`example/site.json` 是一个虚构人物的完整示例。看它的结构就好,内容一句也不要抄:检查脚本会拒绝残留的示例文字。

## 要问的信息

简报里没有的,一次问完,放在同一条消息里。

必需:
1. 个人或工作室的名字。
2. 一句话说明在做什么。
3. 项目:每个项目要有标题和一些真实的文字,比如对方要什么、过程怎样、最后怎样。

可选,没有就不出现在网站上:
4. 服务,有价格就给价格。
5. 本人的图片文件。放在 `site.json` 旁边的文件夹里,长边约 2400 像素。
6. 札记、关于文字、联系方式。
7. 合作方、评价、奖项或数字。只用对方给的。
8. 语言:英文或中文。一个网站一种语言。两种都要就做两个文件。

## 步骤

1. 按下面的字段表写 `site.json`。图片的 `src` 写本地文件路径,相对于 `site.json`。
2. 生成:`node scripts/build.mjs site.json index.html`
3. 检查:`node scripts/check.mjs index.html`。它先读静态文件,再打开每个路由,宽度 1280、768、390,动效开和减弱都跑,两个较小的宽度模拟触屏。按报告改 JSON,重跑到通过为止。视项目数量约 10 到 60 秒。
4. 实测:`node scripts/drive.mjs index.html --shots shots/`。它像访客一样操作:筛选、搜索、排序、视图、模式、放大镜、价格明细、表单、404。不带 `--shots` 约 1 分钟,带上它要 2 到 10 分钟,取决于路由数量。
5. 看 `shots/` 里的截图。`route_<宽度>_<light|dark>_<页面>.png` 覆盖每个路由、两种模式、1280、768 和 390 三种宽度。1280 和 390 的每一页都要看。脚本通过不等于看过。标题断行难看就改文字,不要动 CSS。

运行要求:Node 18 或以上。浏览器检查需要 Playwright:`npm install playwright`,然后 `npx playwright install chromium`。如果装在别处,把 `NODE_PATH` 设为那个 `node_modules` 文件夹。没有 Playwright 时,`check.mjs` 以退出码 2 停止。`--static-only` 只跑静态部分,不需要任何依赖,但那个结果不代表页面被看过。

## 字段

限制都按字符数算,`check.mjs` 会强制执行。“简报太少时”说明对方给的信息不够时怎么办。总原则不变:复述对方说过的事实,不编造,在交付消息里列出每一句你推断出来的内容。

### 顶层

| 字段 | 含义 | 限制 | 简报太少时 |
|---|---|---|---|
| `meta.lang` | `"en"` 或 `"zh"`。决定全部界面文字和字体 | | 跟随简报的语言 |
| `meta.mode` | 初始模式,`"light"`(看片台)或 `"dark"`(暗房) | | `"light"` |
| `meta.fictional` | 只有人物是编出来的才写 `true`,比如演示。每页顶部显示一行红色标注。`example/` 里必须为 `true` | | 真实的人写 `false` |
| `meta.userFacts` | 从简报里原样抄来的句子,其中含百分比、客户数、从业年数或获奖说法。检查脚本据此放行 | | `[]` |
| `meta.terms` | `"film"`(默认)用帧、卷、样片这些词。`"plain"` 改用作品、精选作品。样子不变 | | 摄影师用 `"film"`。插画师、设计师和其他人用 `"plain"`,除非对方喜欢胶片说法 |
| `meta.latinAllowed` | 仅中文网站:允许显示的拉丁字母品牌词,比如 `"Leica"` | | `[]` |
| `name` | 个人或工作室名。会变成巨大的标题,自动撑满宽度 | 60 | 必需,要问 |
| `discipline` | 职业,用于浏览器标题,比如“插画师” | 40 | 没说就不写 |
| `practice` | 一句话说明工作内容,显示在名字下面 | 160 | 用对方的原话。如果只给了职业,就写职业:“产品摄影师。” |

### `projects[]`

| 字段 | 含义 | 限制 | 简报太少时 |
|---|---|---|---|
| `title` | 项目名,显示得很大 | 70 | 必需 |
| `category` | 一个短标签,会变成筛选项 | 24 | 不写。至少有 2 个类别才显示筛选 |
| `year` | 用于排序,按原样显示 | 12 | 不写,绝不猜。至少有 2 个年份才显示按时间排序 |
| `client`、`role` | 有才显示在案例页 | 50、60 | 不写。对方没提的委托方绝不写 |
| `summary` | 一句话,用于列表和案例页顶部 | 180 | 把对方说的缩短复述 |
| `brief` | 对方要什么、为什么,或者这件作品的想法。必需 | 900 | 复述对方说的。对这个项目一无所知就去问,或者去掉它 |
| `origin` | `"self"` 表示自发创作。标题会写“缘起”而不是“需求” | | 没有委托方时使用 |
| `process[]` | 步骤 `{title, text, kept}`。`title` 限 50,`text` 限 500 | | 不写,这一节就不出现。只写对方描述过的步骤 |
| `process[].kept` | `false` 表示放弃的方向,标“舍弃”。红叉只画在生成画面上,绝不画在本人作品上 | | 只有对方说过放弃了什么才用 |
| `process[].chosen` | `true` 给这一步标“选中”。不设就没有标注 | | 不写 |
| `process[].image` | `{src, alt, caption}`:这一步的图片,完整显示,可打开大图。有图的项目里,没有这个字段的步骤只显示文字 | | 不写 |
| `result` | 最后怎样,用文字写 | 900 | 宁可不写,也不要猜结果 |
| `chosen` | `true` 表示放上首页并画红圈,最多 6 个 | | 全部不设,前 6 个就算选中 |
| `images[]` | `{src, alt, caption, focus, mark}`。提供的图片会替换生成画面。`alt` 必填,`caption` 限 90 | | `[]`:自动生成画面,每张都标“占位图” |
| `images[].focus` | `"40% 30%"`(横向、纵向):裁切时保留的位置。只有写了这个字段的图才会被裁切,裁切图是单独一帧,标“局部” | | 不写:完全不裁切 |
| `images[].mark` | `"chosen"` 沿画框边缘画一圈红笔线,绝不压在画面上。`"cut"` 再在片边加一个红色 × | | 不写:没有标记。对方要求才设 |
| `details` | `false` 关闭这个项目的所有“局部”裁切 | | 不写 |
| `figures[]` | `{value, label, source:"user"}`。只放对方给的数字 | | 不写,这一节就不出现 |
| `quote` | `{text, by, source:"user"}`。只放对方给的原话 | | 不写 |
| `link` | `{label, href}`。案例页上的外部链接 | | 没有真实链接就不写 |

### `services[]`,没有就不出现

| 字段 | 含义 | 限制 | 简报太少时 |
|---|---|---|---|
| `title`、`summary` | 名称和一两句说明 | 50、240 | 只用对方的话 |
| `priceText` | 价格按对方的原话写:`"1800 元起"`、`"面议"`、`"From £900"`。首页、列表、详情页都原样显示,不计算合计 | 24 | 简报用文字描述价格、而不是逐项给数字时,一律用它 |
| `currency` | 货币前缀,比如 `"¥"`、`"A$"`。只要有数字金额就必填 | 6 | 简报写“元”时,用 `"currency":"元","currencyAfter":true`,或者用 `priceText` |
| `currencyAfter` | `true` 把货币放在数字后面:`1,800 元` | | 不写 |
| `prices[]` | `{label, amount, priceText, note, optional}`。`label` 限 60,`note` 限 80 | | 见下文 |
| `prices[].optional` | `true` 会加一个默认不勾选的复选框,勾选后计入合计 | | 对方说是加购项才用 |
| `includes[]` | 包含内容 | 每条 90 | 只写对方说过的 |
| `timeline[]` | 步骤 `{title, text, duration}`。限 40、180、20 | | 不写,时间线就不出现 |

**价格。** 绝不估价,也不要按市场价填。首页、列表和详情页都按同一条规则显示:
- 起步价和文字价格(“1800 元起”“面议”“From £900”):写 `priceText`。原样显示,没有合计。不要把这些说法写进数字旁边的 `label` 或 `note`,检查脚本会拒绝。
- 完全没有价格:写 `"prices": []`,也不写 `priceText`。页面显示“价格面议”,按钮照常可用。
- 知道有这一项但不知道价格:写 `"amount": null`。这一行显示“面议”,汇总显示“价格面议”,绝不显示部分金额之和。
- 全是数字的逐项价格:金额写数字(`1800`,不要写 `"¥1,800"`)。引擎负责相加。有可选项时,首页和列表显示“起”。只有一项时标“价格”,多项时标“合计”。
- 在交付消息里说明哪些价格留空了。

### 其他

| 字段 | 含义 | 限制 | 简报太少时 |
|---|---|---|---|
| `about.text[]` | 段落 | 每段 700 | 不写,关于页就显示那句话和几帧画面 |
| `about.portrait` | `{src, alt}` | | 不写 |
| `about.clients[]`、`about.awards[]` | `{name, source:"user"}`,奖项可加 `year` | | 不写,这些部分就不出现 |
| `journal[]` | `{title, date:"YYYY-MM-DD", text:[...]}`,`excerpt` 可选。标题限 90,每段 900 | | 不写,札记就不出现。对方没要就不要代写 |
| `contact.email` | 真实邮箱。显示为链接,也用于“用我的邮箱应用打开” | | 留空。绝不建议填假地址 |
| `contact.phone`、`contact.location` | 原样显示 | 30、60 | 不写 |
| `contact.links[]` | `{label, href}`,只放真实链接。标签限 24 | | `[]` |
| `contact.bookingDays` | 可选的星期,比如 `["sat","sun"]` | | 不写:周一到周五 |

不要推荐“这里写客户名”这类可见占位符。信息缺失时,正确做法就是不写这个字段。上面这些省略都能通过检查。

## 路由

`#/` 首页,`#/projects`(`?q=`、`?cat=`、`?sort=new|old|az`、`?view=list` 都保留在地址里),`#/projects/<slug>`,`#/services`,`#/services/<slug>`,`#/journal`,`#/journal/<slug>`,`#/about`,`#/contact`(`?service=<slug>` 会选中该服务并打开约时间),其他地址都是专门设计的 404。slug 由标题生成,中文标题也可以。要固定某个地址,就给该项写 `slug`。

## 引擎自动做的事

- **没有就不出现,而不是留空。** 没有服务、札记、过程、结果、数字、评价、合作方或奖项,就没有对应部分和导航链接。访问不存在部分的路由会到 404。
- **生成画面。** 没有图片的项目会得到一张由标题、类别、年份和摘要生成的画面。共十种构图、六种暗房色调,相邻项目会错开。改标题,画面也会变。一条胶片上的其他帧是同一场景换了角度和曝光。每张生成画面都带“占位图”标签,首页、作品页和案例页各有一句话说明它们不是作品本身。这些都不能关闭。
- **本人的图片。** 每张都完整显示、保持原有比例:竖图用竖框,方图用方框,不压暗、不加颗粒、不倾斜、不加标记。同一行的帧高度相同,所以不会为了排版而裁切;特别高的作品按屏幕高度限制,两边是胶片。案例页第一张图放在最前面,其余放在文字之后,都能打开大图。文件加载失败时回退到带标签的生成画面。要用 `build.mjs` 生成,它会嵌入文件并记录尺寸,并提醒 `site.json` 文件夹之外的图片和超过 2 MB 的文件。
- **大图。** 黑底上显示完整图片,上面不盖任何东西。支持 Esc、方向键,Tab 不会跑到外面;触屏上左右滑动换图,下滑或点“关闭”退出,可以双指放大;浏览器返回键也能关闭。焦点回到原来的帧。
- **标记。** 红圈和红叉只出现在生成画面上:每条胶片上选中的一帧画圈,另有一两帧画叉,`kept:false` 的步骤画叉。本人作品上没有标记,除非 `images[].mark` 要求在画框边缘画线。导航、筛选和选中的日子都用红圈。
- **按数量排版。** 网格不留空位:每行最多相差一帧,较短的行里画面变大填满,所以 1、2、7、20 个项目都能铺满宽度。至少 2 个项目才显示搜索。
- **首页名字。** 能一行读完就撑满一行。较长的名字会换行,塞不下的长单词会加软连字符。
- **排序。** 底片顺序就是 JSON 顺序。帧号跟随 JSON 顺序,不随排序改变。
- **模式。** 访客的选择保存在浏览器里。`meta.mode` 只决定初始模式。
- **放大镜。** 鼠标悬停在帧上放大 2.8 倍。触屏设备上它会让开,点一下打开项目。没有任何内容依赖它。
- **动效。** 画面显影、标记画出,都在进入视野时发生,不到一秒。没有动效时一切照常可见;减弱动效时完全不运行。
- **标签页。** 日期和时段只在“提一个时间”这一页显示。
- **表单。** 校验称呼、邮箱、内容,约时间时还要选日子。之后显示演示确认,明确说没有发出任何内容,也没有约下时间,并提供可复制的文字;有邮箱时再给一个打开邮箱应用的链接。页面没有服务器。如果对方要真的能发送的表单,告诉他这需要外部服务,这个离线文件里做不到。绝不要改写成真的预约。
- **虚构标注。** `meta.fictional:true` 时,每页顶部一行。不要再加别的。

## 交付物

- `index.html`:一个文件,没有外部请求,双击就能用。
- `site.json`:源文件,方便以后修改。
- 消息里说明:推断的内容、留空的价格、所做的假设、没有出现的部分。

## 遇到这些情况

- **简报很少。** 复述事实,不编造,字段不写,列出推断的内容。三个项目各一句话,也是一个有效的小网站。
- **没人能回答问题。** 用手头的信息继续做,把假设列在消息最前面。
- **想要更短或更长。** 少放项目或少设 `chosen` 就更短。要更长就加真实文字:20 个项目已经测过。限制不变。
- **没有真实链接或邮箱。** 不写。表单照常可用,确认页提供可复制的文字。
- **对方要评价、客户标志或数字,但手上没有。** 告诉他,提供之前这部分不出现。
- **需要一个演示人物。** 设 `meta.fictional:true`。编出来的人必须标注。
- **只有部分项目有图。** 可以。这些项目显示本人的图,其余显示带标签的占位图。告诉对方哪些还缺图:真正公开之前应该把占位图都换掉。
- **简报要你代写项目文字。** 这时以这个要求为准,而不是“只用对方的话”:根据对方给的内容来写,并全部列出来请对方核对。依然不能把委托方、数字或结果当事实编造,`kept:false` 也只用于对方说过的事。
- **作者不希望裁切或标记。** 默认就是这样:不写 `focus` 和 `mark`。

## 交付前检查

- [ ] `check.mjs` 对最终文件退出码为 0,且没有用 `--static-only`。
- [ ] `drive.mjs` 显示 0 失败。
- [ ] 我看过每一页的截图,1280 和 390 都看了,看片台和暗房都看了。
- [ ] 每个合作方、评价、奖项、价格和数字都来自对方。
- [ ] 没给的价格写成 `null` 或 `[]`,并告诉了对方是哪些。
- [ ] 提供的图片都完整显示,有替代文字,可以打开大图。
- [ ] 页面上还留着的“占位图”,都告诉过对方。
- [ ] 没有残留示例内容。虚构标注只用于编出来的人。
- [ ] 推断的内容和假设都列在消息里。

## 不适合的情况

- 需要文件内真正的预约、付款或能发送的联系表单。
- 需要非技术人员长期维护很多页面:用内容管理系统。
- 品牌有严格的专属配色和字体,因为这个样子是固定的。
- 只有一个行动按钮的单页销售网站。
- 作者完全不想让作品放在黑色胶片上:胶片条和片边就是这个概念本身,不能去掉。

用在这些流程里

相似技能