Add initial project structure with essential files

- Created .gitignore to exclude build artifacts and dependencies.
- Added index.html as the main entry point for the application.
- Included LICENSE file with Apache 2.0 terms.
- Initialized package.json and package-lock.json for project dependencies.
- Added pnpm-lock.yaml for package management.
- Created QUICKSTART.md for setup instructions.
- Added README.md and README.zh-CN.md for project documentation in English and Chinese.
This commit is contained in:
pftom
2026-04-28 12:25:59 +08:00
commit a98096a042
258 changed files with 67862 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 op7418 (歸藏)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER DEALINGS IN
THE SOFTWARE.
+119
View File
@@ -0,0 +1,119 @@
# Magazine Web PPT · Editorial-Style Web Slide Deck Skill
A [Claude Code / Claude Agent Skills](https://agentskills.io/) skill that generates **single-file HTML horizontal-swipe decks** with an "**editorial magazine × electronic ink**" aesthetic — picture *Monocle* with code stitched in.
> Distilled by [Guizang](https://x.com/op7418) from offline talks like "One-Person Company: Organizations Folded by AI" and "A New Way of Working." Every pitfall hit during those decks is logged in `checklist.md`.
![Magazine Web PPT preview](https://github.com/user-attachments/assets/5dc316a2-401c-4e37-9123-ea081b6ae470)
## What you get
- 🖋 **Three-tier type system**: serif for headlines, sans-serif for body, mono for metadata
- 🌊 **WebGL fluid / dispersion backgrounds** — visible on hero pages, restrained on body pages
- 📐 **Horizontal swipe navigation**: ← → arrows / scroll wheel / touch swipe / bottom dots / ESC for index
- 🎨 **5 curated theme presets**: Ink Classic / Indigo Porcelain / Forest Ink / Kraft Paper / Dune
- 🧩 **10 page layouts**: cover, act divider, big numbers, lead image + text, image grid, pipeline, hero question, big quote, before/after, image + text mix
- 📄 **Single HTML file** — no build, no server, open directly in the browser
## Fits / Doesn't fit
**✅ Fits**: offline talks, industry keynotes, private salons, AI product launches, demo day, presentations with strong personal voice
**❌ Doesn't fit**: data-heavy tables, training decks (density too low), multi-user collaborative editing (static HTML)
## Install
### Option 1: Paste this to an AI (recommended)
> Install the `guizang-ppt-skill` Claude Code skill for me. Steps:
>
> 1. Make sure `~/.claude/skills/` exists (create if not)
> 2. Run `git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt`
> 3. Verify: `ls ~/.claude/skills/magazine-web-ppt/` should show `SKILL.md`, `assets/`, `references/`
> 4. Tell me when done. Later, saying things like "make me a magazine-style deck" will trigger this skill.
Paste the block above into Claude Code / Cursor / any AI agent with shell access and it handles the install.
### Option 2: Manual CLI
```bash
git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt
```
### How to trigger it
Once installed, Claude Code auto-detects the skill. Trigger phrases:
- "Make me a magazine-style deck"
- "Generate a horizontal swipe deck"
- "Editorial magazine style presentation"
- "Electronic ink slides for my talk"
## Workflow
The skill is a structured 6-step flow; Claude walks you through each:
1. **Clarify intent** — 6-question checklist: audience, duration, source material, images, theme, hard constraints
2. **Copy template**`assets/template.html` → project folder, update `<title>`, swap theme vars
3. **Fill content** — pick from 10 layout skeletons, paste, edit copy (with class-name pre-flight + theme rhythm plan)
4. **Self-check** — match against `references/checklist.md`; P0 issues must all pass
5. **Preview** — open the HTML in a browser
6. **Iterate** — use inline styles to tune font size, height, spacing
Full spec in [`SKILL.md`](./SKILL.md).
## Directory
```
magazine-web-ppt/
├── SKILL.md ← main skill file: workflow, principles, common mistakes
├── README.md ← Chinese README
├── README.en.md ← this file
├── assets/
│ └── template.html ← runnable seed HTML (CSS + WebGL + swipe JS pre-wired)
└── references/
├── components.md ← component catalog (type, color, grid, icons, callout, stat, pipeline)
├── layouts.md ← 10 layout skeletons (paste-ready)
├── themes.md ← 5 theme presets (pick, don't customize)
└── checklist.md ← quality checklist (P0 / P1 / P2 / P3 tiers)
```
## Theme presets
Pick from `references/themes.md`. **Custom hex values are not allowed** — protecting the aesthetic matters more than freedom of choice.
| Theme | Best for |
|------|---------|
| 🖋 Ink Classic | general default, commercial launches, when in doubt |
| 🌊 Indigo Porcelain | tech / research / AI / technical keynotes |
| 🌿 Forest Ink | nature / sustainability / culture / non-fiction |
| 🍂 Kraft Paper | nostalgic / humanist / literary / indie zines |
| 🌙 Dune | art / design / creative / gallery |
Switching themes only requires replacing the 6 variables at the top of `template.html`'s `:root{}` block — all other CSS flows through `var(--...)`.
## Core design principles
1. **Restraint over flash** — WebGL backgrounds only bleed through on hero pages
2. **Structure over decoration** — information hierarchy via type size + typeface + grid whitespace, not shadows or floating cards
3. **Images are first-class citizens** — crop only from the bottom; top and sides stay intact
4. **Rhythm lives on hero pages** — hero / non-hero alternation keeps the eye from fatiguing
5. **Terms stay consistent** — Skills is Skills; no mix-and-match translations
## Visual references
- [*Monocle*](https://monocle.com) magazine layouts
- YC Garry Tan — "Thin Harness, Fat Skills"
- Guizang's offline talk deck series
## Contributing
Bugs, layout issues, new layout requests — Issues and PRs welcome. Prioritize:
- Add new classes to `template.html` first; don't let `layouts.md` reference undefined classes
- Log pitfalls into `checklist.md` at the matching P0 / P1 / P2 / P3 tier
- New theme colors go into `themes.md` with a recommended use case
## License
MIT © 2026 [op7418](https://github.com/op7418)
+120
View File
@@ -0,0 +1,120 @@
# Magazine Web PPT · 电子杂志风网页 PPT Skill
> 🌏 **English version: [README.en.md](./README.en.md)**
一个 [Claude Code / Claude Agent Skills](https://agentskills.io/) 技能,用于生成**单文件 HTML 横向翻页 PPT**,视觉基调是"**电子杂志 × 电子墨水**"——像 *Monocle* 贴上了代码的样子。
> 由 [歸藏](https://x.com/op7418) 在"一人公司:被 AI 折叠的组织"、"一种新的工作方式"等线下分享中沉淀而成,踩过的每一个坑都写进了 `checklist.md`。
![Magazine Web PPT 效果展示](https://github.com/user-attachments/assets/5dc316a2-401c-4e37-9123-ea081b6ae470)
## 效果
- 🖋 **衬线大标题 + 非衬线正文 + 等宽元数据**的三级字体分工
- 🌊 **WebGL 流体/色散背景**,hero 页可见,正文页克制
- 📐 **横向左右翻页**:键盘 ← → / 滚轮 / 触屏滑动 / 底部圆点 / ESC 索引
- 🎨 **5 套主题色预设**:墨水经典 / 靛蓝瓷 / 森林墨 / 牛皮纸 / 沙丘
- 🧩 **10 种页面布局**:开场封面、章节幕封、数据大字报、左文右图、图片网格、Pipeline、悬念问题、大引用、Before/After 对比、图文混排
- 📄 **单文件 HTML**:不需要构建、不需要服务器,浏览器直接打开
## 适合 / 不适合
**✅ 合适**:线下分享 / 行业内部讲话 / 私享会 / AI 产品发布 / demo day / 带强烈个人风格的演讲
**❌ 不合适**:大段表格数据 / 培训课件(信息密度不够)/ 需要多人协作编辑(静态 HTML)
## 安装
### 方式一:把下面这段话直接发给 AI(推荐)
> 帮我安装 `guizang-ppt-skill` 这个 Claude Code skill。请按下面步骤做:
>
> 1. 确保 `~/.claude/skills/` 目录存在(不存在就创建)
> 2. 执行 `git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt`
> 3. 验证:`ls ~/.claude/skills/magazine-web-ppt/` 应该看到 `SKILL.md`、`assets/`、`references/` 三项
> 4. 告诉我安装好了,之后我说"做一份杂志风 PPT"之类的话就会触发这个 skill
把这段话复制粘贴给 Claude Code / Cursor / 任何有 shell 权限的 AI Agent,它会自动完成安装。
### 方式二:手动命令行
```bash
git clone https://github.com/op7418/guizang-ppt-skill.git ~/.claude/skills/magazine-web-ppt
```
### 触发方式
装好后,Claude Code 会在对话里自动发现并调用这个 skill。触发关键词:
- "帮我做一份杂志风 PPT"
- "生成一个 horizontal swipe deck"
- "editorial magazine style presentation"
- "electronic ink 风格演讲 slides"
## 使用流程
Skill 本身是结构化的 6 步工作流,Claude 会逐步引导:
1. **需求澄清** — 6 问清单:受众、时长、素材、图片、主题色、硬约束
2. **拷贝模板**`assets/template.html` → 项目目录,改 `<title>`,换主题色
3. **填充内容** — 从 10 种 layout 骨架里挑、粘、改文案(先做类名预检 + 主题节奏规划)
4. **自检** — 对照 `references/checklist.md`,P0 级问题必须全过
5. **预览** — 浏览器直接打开
6. **迭代** — inline style 改字号/高度/间距
详细说明见 [`SKILL.md`](./SKILL.md)。
## 目录结构
```
magazine-web-ppt/
├── SKILL.md ← Skill 主文件:工作流、原则、常见错误
├── README.md ← 本文件
├── assets/
│ └── template.html ← 完整可运行的种子 HTML(CSS + WebGL + 翻页 JS 全配好)
└── references/
├── components.md ← 组件手册(字体、色、网格、图标、callout、stat、pipeline)
├── layouts.md ← 10 种页面布局骨架(可直接粘贴)
├── themes.md ← 5 套主题色预设(只能选不能自定义)
└── checklist.md ← 质量检查清单(P0 / P1 / P2 / P3 分级)
```
## 主题色预设
`references/themes.md` 里选一套——**不允许自定义 hex 值**,保护美学比给自由更重要。
| 主题 | 适合场景 |
|------|---------|
| 🖋 墨水经典 | 通用默认、商业发布、不知道选啥 |
| 🌊 靛蓝瓷 | 科技 / 研究 / AI / 技术发布会 |
| 🌿 森林墨 | 自然 / 可持续 / 文化 / 非虚构 |
| 🍂 牛皮纸 | 怀旧 / 人文 / 文学 / 独立杂志 |
| 🌙 沙丘 | 艺术 / 设计 / 创意 / 画廊 |
切换主题只需替换 `template.html` 开头 `:root{}` 里的 6 行变量,其他 CSS 全走 `var(--...)`
## 核心设计原则
1. **克制优于炫技** — WebGL 背景只在 hero 页透出
2. **结构优于装饰** — 信息靠字号 + 字体对比 + 网格留白,不用阴影和浮动卡片
3. **图片是第一公民** — 只裁底部,顶部和左右完整
4. **节奏靠 hero 页** — hero / non-hero 交替,才不累眼睛
5. **术语统一** — Skills 就是 Skills,不中英混译
## 视觉参考
- [*Monocle*](https://monocle.com) 杂志的版式
- YC Garry Tan "Thin Harness, Fat Skills"
- 歸藏线下分享 PPT 系列
## 贡献
Bug、排版问题、新布局需求——欢迎开 Issue 或 PR。改动请优先:
-`template.html` 里补类,不要让 layouts.md 使用未定义的类
- 把踩过的坑写到 `checklist.md` 对应的 P0 / P1 / P2 / P3 级别
- 新主题色进 `themes.md` 并给出适合的场景
## License
MIT © 2026 [op7418](https://github.com/op7418)
+314
View File
@@ -0,0 +1,314 @@
---
name: magazine-web-ppt
description: 生成"电子杂志 × 电子墨水"风格的横向翻页网页 PPT(单 HTML 文件),含 WebGL 流体背景、衬线标题 + 非衬线正文、章节幕封、数据大字报、图片网格等模板。当用户需要制作分享 / 演讲 / 发布会风格的网页 PPT,或提到"杂志风 PPT"、"horizontal swipe deck"、"editorial magazine"、"e-ink presentation"时使用。
triggers:
- "ppt"
- "deck"
- "slides"
- "presentation"
- "magazine"
- "杂志"
- "杂志风 PPT"
- "horizontal swipe"
- "horizontal swipe deck"
- "editorial magazine"
- "e-ink presentation"
- "网页 PPT"
- "发布会"
- "分享 PPT"
ocd:
mode: deck
scenario: marketing
featured: 9
default_for: deck
upstream: "https://github.com/op7418/guizang-ppt-skill"
preview:
type: html
entry: index.html
design_system:
requires: false
example_prompt: "帮我做一份杂志风的 PPT —— 关于'一人公司 · 被 AI 折叠的组织',25 分钟分享会,目标受众是设计师 + 创业者。先推荐一个方向(Monocle / WIRED / Kinfolk / Domus / Lab)让我选。"
---
# Magazine Web Ppt
## 这个 Skill 做什么
生成一份**单文件 HTML**的横向翻页 PPT,视觉基调是:
- **电子杂志 + 电子墨水**混血风格
- **WebGL 流体 / 等高线 / 色散背景**(hero 页可见)
- **衬线标题(Noto Serif SC + Playfair Display+ 非衬线正文(Noto Sans SC + Inter+ 等宽元数据(IBM Plex Mono**
- **Lucide 线性图标**(不用 emoji
- **横向左右翻页**(键盘 ← →、滚轮、触屏滑动、底部圆点、ESC 索引)
- **主题平滑插值**:翻到 hero 页时颜色和 shader 柔顺过渡
这个 skill 的美学不是"商务 PPT",也不是"消费互联网 UI"——它像 *Monocle* 杂志贴上了代码后的样子。
## 何时使用
**合适的场景**
- 线下分享 / 行业内部讲话 / 私享会
- AI 新产品发布 / demo day
- 带有强烈个人风格的演讲
- 需要"一次做完,不用翻页工具"的网页版 slides
**不合适的场景**
- 大段表格数据、图表叠加(用常规 PPT)
- 培训课件(信息密度不够)
- 需要多人协作编辑(这是静态 HTML)
## 工作流
### Step 0 · 选方向(Direction · 必做的第一步)
**在问 6 个澄清问题之前,先让用户在 5 个 magazine 方向里挑一个**。每个方向都把"主题色 / 推荐 layout / chrome 风格 / 推荐 slide 数"打包好,挑了方向就回答掉一半澄清问题。
打开 `references/styles.md`,**整段拷过来**给用户看 5 个方向的 1-line summary,然后让他选:
```
1. Monocle Editorial · 国际杂志风 ✦ 默认
2. WIRED Tech · 数据 + 工程
3. Kinfolk Slow · 慢生活 / 人文
4. Domus Architectural · 建筑 / 空间感
5. Lab / Reference · 学术 + 工艺手册
```
如果用户说"不知道,你推荐"——**默认推 Monocle Editorial**,因为它失败概率最低。如果用户提到"AI / benchmark / 技术发布"——推 WIRED;"读书 / 私享 / 朋友圈"——推 Kinfolk;"设计 / 建筑 / portfolio"——推 Domus;"研究 / 学术 / 方法论"——推 Lab。
挑完方向后,在项目目录下创建或更新 `项目记录.md`,第一行写清方向 + 主题色 + 受众 + 时长(模板见 `styles.md` 末尾)。**全程不要换方向**——半路换 = 前面全废。
### Step 1 · 需求澄清(**动手前必做**)
**如果用户已经给了完整的大纲 + 图片**,可以跳过直接进 Step 2。
**如果用户只给了主题或一个模糊想法**,用这 6 个问题逐个对齐后再动手。不要基于猜测就开始写 slide——一旦结构定错,后期翻修代价很高:
#### 6 问澄清清单
> 第 5 题已在 Step 0 选方向时一并回答(方向→主题色)。下面的 5 题里,第 5 题留白即可。
| # | 问题 | 为什么要问 |
|---|------|-----------|
| 1 | **受众是谁?分享场景?**(行业内部 / 商业发布 / demo day / 私享会) | 决定语言风格和深度 |
| 2 | **分享时长?** | 15 分钟 ≈ 10 页,30 分钟 ≈ 20 页,45 分钟 ≈ 25-30 页(每个方向的推荐范围见 `styles.md`) |
| 3 | **有没有原始素材?**(文档 / 数据 / 旧 PPT / 文章链接) | 有素材就基于素材,没有就帮他搭 |
| 4 | **有没有图片?放在哪?** | 详见下方"图片约定" |
| 5 | ~~**想要哪套主题色?**~~ | ✓ 已在 Step 0 由方向决定 |
| 6 | **有没有硬约束?**(必须包含 XX 数据 / 不能出现 YY) | 避免返工 |
#### 大纲协助(如果用户没有大纲)
用"叙事弧"模板搭骨架,再填内容:
```
钩子(Hook) → 1 页 : 抛一个反差 / 问题 / 硬数据让人停下来
定调(Context) → 1-2 页 : 说明背景 / 你是谁 / 为什么讲这个
主体(Core) → 3-5 页 : 核心内容,用 Layout 4/5/6/9/10 穿插
转折(Shift) → 1 页 : 打破预期 / 提出新观点
收束(Takeaway) → 1-2 页 : 金句 / 悬念问题 / 行动建议
```
叙事弧 + 页数规划 + 主题节奏表(见 `layouts.md`),**三张表对齐后**再进 Step 2。
大纲建议保存为 `项目记录.md``大纲-v1.md`,便于后续迭代。
#### 图片约定(告知用户)
在动手前向用户说清:
- **文件夹位置**:`项目/XXX/ppt/images/` 下(和 `index.html` 同级)
- **命名规范**:`{页号}-{语义}.{ext}`,例如 `01-cover.jpg` / `03-figma.jpg` / `05-dashboard.png`
- 页号补零便于排序
- 语义用英文,短、具体、和内容对应
- **规格建议**:
- 单张 ≥ 1600px 宽(避免大屏模糊)
- JPG 用于照片/截图,PNG 用于透明 UI/图表
- 总大小控制在 10MB 内(影响翻页流畅度)
- **如何替换**:保持**同名覆盖**最稳(HTML 里不用改路径);如果文件名变了,记得全局搜 `images/旧名` 改成新名
- **没图怎么办**:和用户对齐,可以先用占位色块生成结构,等图片后期补;但要告知 layout 4/5/10 等图文混排页没图就没法验证视觉效果
### Step 2 · 拷贝模板
`assets/template.html` 拷贝一份到目标位置(通常是 `项目/XXX/ppt/index.html`),同时在同级建一个 `images/` 文件夹准备接图片。
```bash
mkdir -p "项目/XXX/ppt/images"
cp "<SKILL_ROOT>/assets/template.html" "项目/XXX/ppt/index.html"
```
`template.html` 是一个**完整可运行**的文件——CSS、WebGL shader、翻页 JS、字体/图标 CDN 全已预设好,只有 `<main id="deck">` 里面是 3 个示例 slide(封面、章节幕封、空白填充页)。
#### 2.1 · 必改占位符(**容易漏**)
拷贝后立刻改掉以下占位符,否则浏览器 Tab 会显示"[必填] 替换为 PPT 标题"这种尴尬文字:
| 位置 | 原始 | 需改为 |
|------|------|--------|
| `<title>` | `[必填] 替换为 PPT 标题 · Deck Title` | 实际 deck 标题(如 `一种新的工作方式 · Luke Wroblewski`) |
每次拷贝完 template.html 第一件事:grep 一下"[必填]" 确认全部替换完。
#### 2.2 · 选定主题色(5 套预设 · 不允许自定义)
本 skill **只允许从 5 套精心调配的预设里选一套**,不接受用户自定义 hex 值——颜色搭配错了画面瞬间变丑,保护美学比给自由更重要。
| # | 主题 | 适合 |
|---|------|------|
| 1 | 🖋 墨水经典 | 通用 / 商业发布 / 不知道选啥的默认 |
| 2 | 🌊 靛蓝瓷 | 科技 / 研究 / 数据 / 技术发布会 |
| 3 | 🌿 森林墨 | 自然 / 可持续 / 文化 / 非虚构 |
| 4 | 🍂 牛皮纸 | 怀旧 / 人文 / 文学 / 独立杂志 |
| 5 | 🌙 沙丘 | 艺术 / 设计 / 创意 / 画廊 |
**操作**:
1. 基于内容主题推荐一套,或直接问用户选哪一套
2. 打开 `references/themes.md`,找到对应主题的 `:root`
3. **整体替换** `assets/template.html`(已拷贝版本)开头 `:root{` 块里标有"主题色"注释的那几行(`--ink` / `--ink-rgb` / `--paper` / `--paper-rgb` / `--paper-tint` / `--ink-tint`)
4. 其他 CSS 都走 `var(--...)`,无需任何其他改动
**硬规则**:
- 一份 deck 只用一套主题,不要中途换色
- 不要接受用户给的任意 hex 值——委婉拒绝并展示 5 套让选
- 不要混搭(例如 ink 取墨水经典、paper 取沙丘)——会彻底违和
### Step 3 · 填充内容
#### 3.0 · 预检:类名必须在 template.html 里有定义(**最重要**
**这是所有生成问题的源头**。layouts.md 的骨架使用了很多类名(`h-hero` / `h-xl` / `stat-card` / `pipeline` / `grid-2-7-5` 等),如果 `assets/template.html``<style>` 里没有对应定义,浏览器会 fallback 到默认样式——大标题变成非衬线、数据卡片挤成一团、pipeline 糊成一行、图片堆到页面底部。
**在写任何 slide 代码之前:**
1. **先 Read `assets/template.html`**(至少读到 `<style>` 块末尾)
2. **对照 layouts.md 的 Pre-flight 列表**,确认你要用的每个类都在 `<style>` 里存在
3. 如果某个类缺失:**在 template.html 的 `<style>` 里补上**,不要在每个 slide 里 inline 重写
4. **template.html 是唯一的类名来源**——不要发明新类名,如需自定义用 `style="..."` inline
常见容易遗漏的类(必须预先确认存在):
`h-hero` / `h-xl` / `h-sub` / `h-md` / `lead` / `kicker` / `meta-row` / `stat-card` / `stat-label` / `stat-nb` / `stat-unit` / `stat-note` / `pipeline-section` / `pipeline-label` / `pipeline` / `step` / `step-nb` / `step-title` / `step-desc` / `grid-2-7-5` / `grid-2-6-6` / `grid-2-8-4` / `grid-3-3` / `grid-6` / `grid-3` / `grid-4` / `frame` / `frame-img` / `img-cap` / `callout` / `callout-src` / `chrome` / `foot`
#### 3.0.5 · 规划主题节奏(**和类预检同等重要**)
**在挑布局之前**,必须先列出每一页的主题 class(`hero dark` / `hero light` / `light` / `dark`)并写到文档或草稿里对齐。详细规则看 `references/layouts.md` 开头的"主题节奏规划"一节。
**强制规则**:
- 每页 section 必须带 `light` / `dark` / `hero light` / `hero dark` 之一,不要只写 `hero`
- 连续 3 页以上同主题 = 视觉疲劳,不允许
- 8 页以上必须有 ≥1 个 `hero dark` + ≥1 个 `hero light`
- 整个 deck 不能只有 `light` 正文页,必须有 `dark` 正文页制造呼吸
- 每 3-4 页插入 1 个 hero 页(封面/幕封/问题/大引用)
**生成后自检**:`grep 'class="slide' index.html` 列出所有主题,人工确认节奏合理再交付。
#### 3.1 · 挑布局
**不要从零写 slide**。打开 `references/layouts.md`,里面有 10 种现成布局骨架,每种都是完整可粘贴的 `<section>` 代码块:
| Layout | 用途 |
|---|---|
| 1. 开场封面 | 第 1 页 |
| 2. 章节幕封 | 每幕开场 |
| 3. 数据大字报 | 抛硬数据 |
| 4. 左文右图(Quote + Image) | 身份反差 / 故事 |
| 5. 图片网格 | 多图对比 / 截图实证 |
| 6. 两列流水线(Pipeline) | 工作流程 |
| 7. 悬念收束 / 问题页 | 幕末 / 收尾 |
| 8. 大引用页(Big Quote) | 衬线金句 / takeaway |
| 9. 并列对比(Before / After) | 旧模式 vs 新模式 |
| 10. 图文混排(Lead Image + Side Text) | 信息密集的图文页 |
选对应 layout,粘过去,改文案和图片路径即可。**务必先完成 3.0 预检**。
#### 3.2 · 图片比例规范
永远用**标准比例**,不要用原图奇葩比例(如 `2592/1798`):
| 场景 | 推荐比例 |
|------|---------|
| 左文右图 主图 | 16:10 或 4:3 + `max-height:56vh` |
| 图片网格(多图对比) | **固定 `height:26vh`**,不用 aspect-ratio |
| 左小图 + 右文字 | 1:1 或 3:2 |
| 全屏主视觉 | 16:9 + `max-height:64vh` |
| 图文混排小插图 | 3:2 或 3:4 |
**图片绝不使用 `align-self:end`**——会滑到 cell 底被浏览器工具栏遮挡。用 grid 容器 + `align-items:start`(template 已预设)让图片贴顶即可;左列若想贴底,用 flex column + `justify-content:space-between`
组件细节(字体、颜色、网格、图标、callout、stat-card 等)在 `references/components.md`
### Step 4 · 对照检查清单自检
生成完一定要打开 `references/checklist.md`,逐项对照。里面总结了**真实迭代过程中踩过的所有坑**,P0 级别的问题(emoji、图片撑破、标题换行、字体分工)必须全部通过。
特别要注意的几条:
1. **大标题必须是衬线字体**——如果显示成非衬线,99% 是 Step 3.0 预检没做,`h-hero` 类在 template.html 里缺失
2. **图片网格里只用 `height:Nvh`,不用 `aspect-ratio`**(会撑破)
3. **图片不能堆到页面底部**——不要用 `align-self:end`,用 grid + `align-items:start`(见 Step 3.2)
4. **图片只能用标准比例**(16:10 / 4:3 / 3:2 / 1:1 / 16:9),不要复制原图的奇葩比例
5. **中文大标题 ≤ 5 字且 `nowrap`**(避免 1 字 1 行)
6. **用 Lucide,不用 emoji**
7. **标题用衬线,正文用非衬线,元数据用等宽**
### Step 5 · 本地预览
直接在浏览器打开 `index.html` 就行。macOS 下:
```bash
open "项目/XXX/ppt/index.html"
```
不需要本地服务器。图片走相对路径 `images/xxx.png`
### Step 6 · 迭代
根据用户反馈修改——模板的 CSS 已经高度参数化,90% 的调整都是改 inline style(字号 `font-size:Xvw` / 高度 `height:Yvh` / 间距 `gap:Zvh`)。
---
## 资源文件导览
```
magazine-web-ppt/
├── SKILL.md ← 你正在读
├── assets/
│ ├── template.html ← 完整的可运行模板(种子文件)
│ └── example-slides.html ← 9 页样例 deck(用于 Examples 预览)
└── references/
├── styles.md ← 5 个 magazine 方向(Monocle / WIRED / Kinfolk / Domus / Lab
├── components.md ← 组件手册(字体、色、网格、图标、callout、stat、pipeline...
├── layouts.md ← 10 种页面布局骨架(可直接粘贴)
├── themes.md ← 5 套主题色预设(只能选不能自定义)
└── checklist.md ← 质量检查清单(P0/P1/P2/P3 分级)
```
**加载顺序建议**
1. 先读完 `SKILL.md`(这个文件)了解整体
2. **Step 0 选方向时,读 `styles.md`**——5 个方向各自打包好了主题色 + 推荐 layout + chrome 风格
3. Step 1 需求澄清完成后,如果方向需要确认,再读 `themes.md` 看色板细节
4. **动手前 Read `assets/template.html` 的 `<style>` 块**——这是类名的唯一来源,缺类会导致整页样式崩
5.`layouts.md` 挑布局(顶部有 Pre-flight 类名清单和主题节奏规划)
6. 细节调整时读 `components.md` 查组件
7. 生成后读 `checklist.md` 自检(顶部 P0-0 规则强制预检)
## 核心设计原则(哲学)
> 这些原则是"一人公司"分享 PPT 的 5 轮迭代总结出来的。违反其中任何一条,视觉感都会垮。
1. **克制优于炫技** — WebGL 背景只在 hero 页透出,普通页几乎看不见
2. **结构优于装饰** — 不用阴影、不用浮动卡片、不用 padding box,一切信息靠**大字号 + 字体对比 + 网格留白**
3. **内容层级由字号和字体共同定义** — 最大衬线 = 主标题,中衬线 = 副标,大非衬线 = lead,小非衬线 = body,等宽 = 元数据
4. **图片是第一公民** — 图片只裁底部,保证顶部和左右完整;网格用 `height:Nvh` 固定,不要用 `aspect-ratio`
5. **节奏靠 hero 页** — hero 和 non-hero 交替,才不累眼睛
6. **术语统一** — Skills 就是 Skills,不要中英混合翻译
## 参考作品
本 skill 的视觉基调参考了:
- 歸藏 "一人公司:被 AI 折叠的组织" 分享(2026-04-2227 页)
- *Monocle* 杂志的版式
- YC 总裁 Garry Tan "Thin Harness, Fat Skills" 那篇博客的 demo
可以把它们当做风格锚点。
@@ -0,0 +1,318 @@
<!--
Example slides for the magazine-web-ppt skill.
Topic: "一人公司 · The Quiet Hardware" — a fictional but realistic 64-day
case study, mirroring the rhythm and content arc of the original 歸藏
guizang-ppt-skill demo. Used to power the Examples preview without
requiring real product imagery on disk — image slots stand in for what a
real deck would show.
Theme rhythm: hero dark → light → dark → light → hero light → dark →
hero dark → light → hero light. Hits all 8 layout categories.
-->
<!-- Layout 1 · Hero Cover ============================================ -->
<section class="slide hero dark">
<div class="chrome">
<div>A Talk · 2026.04.22</div>
<div>Vol.01</div>
</div>
<div class="frame" style="display:grid; gap:4vh; align-content:center; min-height:80vh">
<div class="kicker">私享会 · 创作者 Demo Day</div>
<h1 class="h-hero">一人公司</h1>
<h2 class="h-sub">被 AI 折叠的组织</h2>
<p class="lead" style="max-width:60vw">
一个独立创作者 —— 在 64 天里完成 11 万行代码、覆盖 9 个平台、跨过 5 个时区,<br>
生活节奏几乎没有被打扰。
</p>
<div class="meta-row">
<span>歸藏 Guizang</span><span>·</span><span>独立创作者</span><span>·</span><span>CodePilot 作者</span>
</div>
</div>
<div class="foot">
<div>一场关于 AI · 组织 · 个体的分享</div>
<div>— 2026 —</div>
</div>
</section>
<!-- Layout 2 · Big Numbers Grid ======================================= -->
<section class="slide light">
<div class="chrome">
<div>过去 64 天 · 开发篇</div>
<div>Act I / Dev · 02 / 09</div>
</div>
<div class="frame" style="padding-top:6vh">
<div class="kicker">一个人,做了什么。</div>
<h2 class="h-xl">过去 64 天</h2>
<p class="lead" style="margin-bottom:5vh">从 0 到开源 CodePilot。</p>
<div class="grid-6" style="margin-top:6vh">
<div class="stat-card">
<div class="stat-label">Duration</div>
<div class="stat-nb">64 <span class="stat-unit"></span></div>
<div class="stat-note">从立项到现在</div>
</div>
<div class="stat-card">
<div class="stat-label">Lines of Code</div>
<div class="stat-nb">110K+</div>
<div class="stat-note">一行一行写到 11 万+</div>
</div>
<div class="stat-card">
<div class="stat-label">GitHub Stars</div>
<div class="stat-nb">5,166</div>
<div class="stat-note">单仓库 · 60 天破 5K</div>
</div>
<div class="stat-card">
<div class="stat-label">Downloads</div>
<div class="stat-nb">41K+</div>
<div class="stat-note">装进了几万台电脑里</div>
</div>
<div class="stat-card">
<div class="stat-label">AI Providers</div>
<div class="stat-nb">19</div>
<div class="stat-note">跨平台模型接入</div>
</div>
<div class="stat-card">
<div class="stat-label">Commits</div>
<div class="stat-nb">608+</div>
<div class="stat-note">没有协作者</div>
</div>
</div>
</div>
<div class="foot">
<div>项目 · CodePilot | github.com/codepilot</div>
<div>Act I · Dev Numbers</div>
</div>
</section>
<!-- Layout 4 · Quote + Image ========================================== -->
<section class="slide dark">
<div class="chrome">
<div>身份反差 · The Twist</div>
<div>03 / 09</div>
</div>
<div class="frame grid-2-7-5" style="padding-top:6vh">
<div style="display:flex; flex-direction:column; justify-content:space-between; gap:3vh">
<div>
<div class="kicker">BUT</div>
<h2 class="h-xl" style="white-space:nowrap; font-size:7.2vw">
我不是程序员。
</h2>
<p class="lead" style="margin-top:3vh">
大学毕业之后再没写过一行生产代码。过去十年做的是 UI 设计 / AI 特效 / 自媒体内容。
</p>
</div>
<div class="callout">
“这东西在三年前,<br>
需要一个十人团队做一年。”
<div class="callout-src">— 一个观察者的判断</div>
</div>
</div>
<figure class="img-slot r-3x2" style="aspect-ratio:16/10; max-height:56vh">
<span class="plus">+</span>
<span class="label">Product Screenshot · CodePilot</span>
</figure>
</div>
<div class="foot">
<div>Page 03 · 我不是程序员</div>
<div>— · —</div>
</div>
</section>
<!-- Layout 6 · Pipeline =============================================== -->
<section class="slide light">
<div class="chrome">
<div>我的工作流 · Workflow</div>
<div>Act II · 04 / 09</div>
</div>
<div class="frame">
<div class="kicker">Pipeline · 流水线</div>
<h2 class="h-xl">两条流水线</h2>
<div class="pipeline-section">
<div class="pipeline-label">文本侧 · Text Pipeline</div>
<div class="pipeline">
<div class="step">
<div class="step-nb">01</div>
<div class="step-title">Draft</div>
<div class="step-desc">AI 帮我起草初稿</div>
</div>
<div class="step">
<div class="step-nb">02</div>
<div class="step-title">Polish</div>
<div class="step-desc">AI 润色去 AI 味</div>
</div>
<div class="step">
<div class="step-nb">03</div>
<div class="step-title">Morph</div>
<div class="step-desc">AI 变形成推特 / 小红书</div>
</div>
<div class="step">
<div class="step-nb">04</div>
<div class="step-title">Illustrate</div>
<div class="step-desc">AI 生成信息图</div>
</div>
<div class="step">
<div class="step-nb">05</div>
<div class="step-title">Distribute</div>
<div class="step-desc">一键分发 9 平台</div>
</div>
</div>
</div>
<div class="pipeline-section">
<div class="pipeline-label">视觉 · 视频侧 · Video Pipeline</div>
<div class="pipeline" data-cols="3">
<div class="step">
<div class="step-nb">06</div>
<div class="step-title">Cut</div>
<div class="step-desc">AI 剪辑 + 自动配字幕</div>
</div>
<div class="step">
<div class="step-nb">07</div>
<div class="step-title">Wrap</div>
<div class="step-desc">AI 包装 + 配 BGM</div>
</div>
<div class="step">
<div class="step-nb">08</div>
<div class="step-title">Cover</div>
<div class="step-desc">AI 生成封面图</div>
</div>
</div>
</div>
</div>
<div class="foot">
<div>Page 04 · 我的内容工厂</div>
<div>Workflow</div>
</div>
</section>
<!-- Layout 2 · Act Divider ============================================ -->
<section class="slide hero light">
<div class="chrome">
<div>第二幕 · 折叠</div>
<div>Act II · 05 / 09</div>
</div>
<div class="frame" style="display:grid; gap:6vh; align-content:center; min-height:80vh">
<div class="kicker">Act II</div>
<h1 class="h-hero" style="font-size:8.5vw">折叠</h1>
<p class="lead" style="max-width:55vw">
从 “一个人做内容” 到 “一个人是组织”。<br>
AI 不是工具,是岗位的折叠器。
</p>
</div>
<div class="foot">
<div>第二幕引子</div>
<div>— · —</div>
</div>
</section>
<!-- Layout 8 · Big Quote ============================================== -->
<section class="slide dark">
<div class="chrome">
<div>The Takeaway · 核心金句</div>
<div>06 / 09</div>
</div>
<div class="frame" style="display:grid; gap:5vh; align-content:center; min-height:80vh">
<div class="kicker">Quote · 金句</div>
<blockquote style="font-family:var(--serif-zh); font-weight:700; font-size:5.6vw; line-height:1.2; letter-spacing:-.01em; max-width:78vw">
“没有交接,<br>所有人都在构建。”
</blockquote>
<p class="lead" style="max-width:55vw; opacity:.65">
Without the handoff, everyone builds.<br>
And that makes all the difference.
</p>
<div class="meta-row">
<span>— Luke Wroblewski</span><span>·</span><span>2026.04.16</span>
</div>
</div>
<div class="foot">
<div>Page 06 · 金句</div>
<div>— · —</div>
</div>
</section>
<!-- Layout 7 · Hero Question ========================================== -->
<section class="slide hero dark">
<div class="chrome">
<div>留给你的问题</div>
<div>07 / 09</div>
</div>
<div class="frame" style="display:grid; gap:8vh; align-content:center; min-height:80vh">
<div class="kicker">The Question</div>
<h1 class="h-hero" style="font-size:7vw; line-height:1.15">
你的公司里,<br>
哪些岗位本来就<br>
不该由人来做?
</h1>
<p class="lead" style="max-width:50vw">
这不是技术问题,是架构问题。
</p>
</div>
<div class="foot">
<div>Page 07 · The Question</div>
<div>— · —</div>
</div>
</section>
<!-- Layout 9 · Before / After ========================================= -->
<section class="slide light">
<div class="chrome">
<div>旧 vs 新 · The Shift</div>
<div>08 / 09</div>
</div>
<div class="frame" style="padding-top:5vh">
<div class="kicker">Before / After · 范式转变</div>
<h2 class="h-xl" style="margin-bottom:4vh">从交接到共建</h2>
<div class="grid-2-6-6" style="gap:5vw 4vh">
<div style="padding:3vh 2vw; border-left:3px solid currentColor; opacity:.55">
<div class="kicker" style="opacity:.9">Before · 旧模式</div>
<h3 class="h-md" style="margin-top:2vh">设计 → 开发 → 交接</h3>
<ul style="margin-top:3vh; padding-left:1.2em; display:flex; flex-direction:column; gap:1.4vh; font-family:var(--sans-zh); font-size:max(14px,1.1vw); line-height:1.55">
<li>设计师在 Figma 做稿,反复对齐像素</li>
<li>开发盯着设计稿手动翻译</li>
<li>反复 PR 沟通,文档遗失在 Slack</li>
<li>非技术成员无法触碰代码</li>
</ul>
</div>
<div style="padding:3vh 2vw; border-left:3px solid currentColor">
<div class="kicker" style="opacity:.9">After · 新模式</div>
<h3 class="h-md" style="margin-top:2vh">同工具 · 并行 · 共建</h3>
<ul style="margin-top:3vh; padding-left:1.2em; display:flex; flex-direction:column; gap:1.4vh; font-family:var(--sans-zh); font-size:max(14px,1.1vw); line-height:1.55">
<li>三个角色同时在同一份 Intent 上工作</li>
<li>agents.md / SKILL.md 是共享上下文</li>
<li>代理处理对齐、冲突、动效</li>
<li>任何人都能安全贡献代码</li>
</ul>
</div>
</div>
</div>
<div class="foot">
<div>Page 08 · 范式转变</div>
<div>Before / After</div>
</div>
</section>
<!-- Layout 2 · Hero Close ============================================= -->
<section class="slide hero light">
<div class="chrome">
<div>End · 致谢</div>
<div>09 / 09</div>
</div>
<div class="frame" style="display:grid; gap:5vh; align-content:center; min-height:80vh">
<div class="kicker">Thanks for watching</div>
<h1 class="h-hero" style="font-size:9vw">谢谢。</h1>
<p class="lead" style="max-width:55vw">
Slides are a single HTML file —<br>
open in any browser, no build, no server.
</p>
<div class="meta-row">
<span>github.com/op7418/guizang-ppt-skill</span><span>·</span><span>MIT License</span>
</div>
</div>
<div class="foot">
<div>Made with magazine-web-ppt skill</div>
<div>— Fin —</div>
</div>
</section>
+643
View File
@@ -0,0 +1,643 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>[必填] 替换为 PPT 标题 · Deck Title</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,700&family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,500;0,8..60,600;1,8..60,400&family=IBM+Plex+Mono:wght@300;400;500;600&family=Noto+Serif+SC:wght@300;400;500;600;700;900&family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap" rel="stylesheet">
<style>
:root{
/* ============ 主题色(默认:🖋 墨水经典) ============
切换主题:从 references/themes.md 复制对应的 :root 块
整体替换这几行(--ink / --ink-rgb / --paper / --paper-rgb)
其他地方散落的 rgba() 都走 var(--ink-rgb) / var(--paper-rgb),无需逐处改 */
--ink:#0a0a0b;
--ink-rgb:10,10,11;
--paper:#f1efea;
--paper-rgb:241,239,234;
--paper-tint:#e8e5de;
--ink-tint:#18181a;
/* ============ 字体(跨主题固定) ============ */
--mono:"IBM Plex Mono",ui-monospace,monospace;
--serif-en:"Playfair Display","Source Serif 4",Georgia,serif;
--serif-body-en:"Source Serif 4",Georgia,serif;
--serif-zh:"Noto Serif SC",source-han-serif-sc,serif;
--sans-zh:"Noto Sans SC",source-han-sans-sc,sans-serif;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{width:100%;height:100%;overflow:hidden;background:var(--ink);color:var(--paper);font-family:var(--sans-zh);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
/* ============ WebGL 双背景 ============ */
canvas.bg{position:fixed;inset:0;width:100vw;height:100vh;z-index:0;display:block;transition:opacity 1.2s ease}
canvas#bg-light{opacity:0}
canvas#bg-dark{opacity:1}
body.light-bg canvas#bg-light{opacity:1}
body.light-bg canvas#bg-dark{opacity:0}
/* ============ Deck 容器 + 翻页 ============ */
/* width: NSLIDES * 100vw,会在 JS 里动态矫正 */
#deck{position:fixed;inset:0;width:10000vw;height:100vh;display:flex;flex-wrap:nowrap;transition:transform .9s cubic-bezier(.77,0,.175,1);z-index:10;will-change:transform}
.slide{width:100vw;height:100vh;flex:0 0 100vw;position:relative;padding:6vh 6vw 10vh 6vw;display:flex;flex-direction:column;overflow:hidden}
.slide.light{color:var(--ink)}
.slide.dark{color:var(--paper)}
/* 默认页:遮罩较厚,保证文字可读 */
.slide::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;transition:background .7s ease}
.slide.light::before{background:rgba(var(--paper-rgb),.78);backdrop-filter:blur(3px)}
.slide.dark::before{background:rgba(var(--ink-rgb),.78);backdrop-filter:blur(3px)}
/* Hero 页:遮罩大幅降低,让 WebGL 背景明显透出 */
.slide.hero.light::before{background:rgba(var(--paper-rgb),.16);backdrop-filter:none}
.slide.hero.dark::before{background:rgba(var(--ink-rgb),.12);backdrop-filter:none}
/* Hero 页顶底微弱渐隐,保证 chrome/foot 区域可读 */
.slide.hero::after{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none}
.slide.hero.light::after{background:linear-gradient(180deg,rgba(var(--paper-rgb),.28) 0%,rgba(var(--paper-rgb),0) 14%,rgba(var(--paper-rgb),0) 86%,rgba(var(--paper-rgb),.28) 100%)}
.slide.hero.dark::after{background:linear-gradient(180deg,rgba(var(--ink-rgb),.32) 0%,rgba(var(--ink-rgb),0) 14%,rgba(var(--ink-rgb),0) 86%,rgba(var(--ink-rgb),.32) 100%)}
/* ============ Magazine chrome:顶部 meta + 底部 foot ============ */
.chrome{display:flex;justify-content:space-between;align-items:flex-start;font-family:var(--mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;opacity:.7}
.chrome .left,.chrome .right{display:flex;gap:2.4em;align-items:center}
.chrome .sep{width:40px;height:1px;background:currentColor;opacity:.4}
.foot{margin-top:auto;display:flex;justify-content:space-between;align-items:flex-end;font-family:var(--mono);font-size:12px;letter-spacing:.14em;text-transform:uppercase;opacity:.55}
.foot .title{font-family:var(--serif-zh);font-weight:400;letter-spacing:.05em;text-transform:none;opacity:.75;font-size:13px}
.tag{display:inline-block;font-family:var(--mono);font-size:11px;letter-spacing:.24em;text-transform:uppercase;padding:6px 14px;border:1px solid currentColor;opacity:.85}
.rule{width:100%;height:1px;background:currentColor;opacity:.25;margin:3vh 0}
.rule.v{width:1px;height:100%;margin:0}
/* ============ 字体规则 ============
· 衬线(Noto Serif SC / Playfair):大标题、重点金句、数字
· 非衬线(Noto Sans SC):正文描述、body、补充说明
· 等宽(IBM Plex Mono):kicker、meta 小标签、foot 右侧
*/
.kicker{font-family:var(--mono);font-size:12px;letter-spacing:.3em;text-transform:uppercase;opacity:.6;margin-bottom:2.6vh}
.display{font-family:var(--serif-en);font-weight:700;font-size:11vw;line-height:.92;letter-spacing:-.025em}
.display-zh{font-family:var(--serif-zh);font-weight:700;font-size:7.8vw;line-height:1.04;letter-spacing:-.005em}
.h1-zh{font-family:var(--serif-zh);font-weight:700;font-size:4.6vw;line-height:1.12;letter-spacing:-.005em}
.h2-zh{font-family:var(--serif-zh);font-weight:600;font-size:3.2vw;line-height:1.2;letter-spacing:0}
.h3-zh{font-family:var(--serif-zh);font-weight:500;font-size:1.9vw;line-height:1.35}
.body-zh{font-family:var(--sans-zh);font-weight:400;font-size:max(15px,1.22vw);line-height:1.75;opacity:.82;letter-spacing:.01em}
.body-serif{font-family:var(--serif-zh);font-weight:400;font-size:max(15px,1.3vw);line-height:1.65;opacity:.88}
.lead{font-family:var(--serif-zh);font-weight:400;font-size:1.9vw;line-height:1.4;opacity:.85}
.meta{font-family:var(--mono);font-size:max(11px,.88vw);letter-spacing:.16em;text-transform:uppercase;opacity:.6}
.big-num{font-family:var(--serif-en);font-weight:800;font-size:10vw;line-height:.85;letter-spacing:-.03em;font-feature-settings:"tnum"}
.mid-num{font-family:var(--serif-en);font-weight:700;font-size:5.5vw;line-height:.88;letter-spacing:-.02em;font-feature-settings:"tnum"}
.ghost{font-family:var(--serif-en);font-weight:900;font-size:34vw;line-height:.8;opacity:.06;letter-spacing:-.04em;position:absolute;font-feature-settings:"tnum"}
em{font-style:italic;font-family:var(--serif-en)}
.en{font-family:var(--serif-en);font-style:italic;font-weight:500}
/* ============ 布局工具 ============ */
.col{display:flex;flex-direction:column;gap:2.4vh}
.row{display:flex;align-items:center;gap:3vw}
.grid-6{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(2,1fr);gap:4vw 6vw;flex:1;align-content:center;padding:2vh 0}
.grid-9{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:3vh 4vw;flex:1;align-content:center}
.grid-4{display:grid;grid-template-columns:repeat(2,1fr);grid-template-rows:repeat(2,1fr);gap:4vh 6vw;flex:1;align-content:center}
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:4vw;flex:1;align-content:center}
.split{display:grid;grid-template-columns:1fr 1fr;gap:4vw;flex:1;align-items:center}
.split-55{display:grid;grid-template-columns:55fr 45fr;gap:5vw;flex:1;align-items:stretch}
.fill{flex:1}
.center{align-items:center;justify-content:center;text-align:center}
.bottom-left{position:absolute;left:6vw;bottom:9vh;max-width:50vw}
.bottom-right{position:absolute;right:6vw;bottom:9vh;max-width:50vw;text-align:right}
.top-right{position:absolute;right:6vw;top:6vh;text-align:right}
/* ============ Stat(数字矩阵) ============ */
.stat{display:flex;flex-direction:column;gap:1vh;align-items:flex-start}
.stat .n{font-family:var(--serif-en);font-weight:800;font-size:8vw;line-height:.88;letter-spacing:-.03em;font-feature-settings:"tnum"}
.stat .l{font-family:var(--sans-zh);font-size:max(13px,1.05vw);opacity:.7;margin-top:1vh;font-weight:400;line-height:1.5}
.stat .m{font-family:var(--mono);font-size:10px;letter-spacing:.22em;text-transform:uppercase;opacity:.5;margin-bottom:.2vh}
/* ============ Callout(引用框) ============ */
.callout{padding:3vh 2.4vw;border-left:3px solid currentColor;position:relative;font-family:var(--serif-zh);font-size:max(15px,1.2vw);line-height:1.55;opacity:.92}
.slide.light .callout{background:rgba(var(--ink-rgb),.05)}
.slide.dark .callout{background:rgba(var(--paper-rgb),.06)}
.callout .cite{display:block;margin-top:1.6vh;font-family:var(--mono);font-size:11px;letter-spacing:.2em;text-transform:uppercase;opacity:.6}
.callout .q-big{font-family:var(--serif-zh);font-weight:600;font-size:max(17px,1.6vw);line-height:1.42}
/* ============ Platform(平台卡) ============ */
.plat{display:flex;flex-direction:column;justify-content:flex-end;padding:2vh 0;border-top:1px solid currentColor;border-color:rgba(127,127,127,.35)}
.plat .name{font-family:var(--serif-zh);font-weight:700;font-size:1.8vw;margin-bottom:.6vh}
.plat .nb{font-family:var(--serif-en);font-weight:700;font-size:3.2vw;letter-spacing:-.02em;line-height:1;font-feature-settings:"tnum"}
.plat .sub{font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;opacity:.55;margin-top:.6vh}
.plat .fill{font-family:var(--sans-zh);font-weight:300;font-size:2.4vw;opacity:.28;letter-spacing:-.01em;line-height:1}
/* ============ Rowline(表格行) ============ */
.rowline{display:grid;grid-template-columns:1fr 2fr 1fr;gap:2vw;padding:2.2vh 0;border-top:1px solid currentColor;align-items:center;border-color:rgba(127,127,127,.25)}
.rowline:last-child{border-bottom:1px solid currentColor;border-color:rgba(127,127,127,.25)}
.rowline .k{font-family:var(--serif-zh);font-weight:700;font-size:1.7vw}
.rowline .v{font-family:var(--sans-zh);font-weight:400;font-size:max(14px,1.2vw);opacity:.85;line-height:1.55}
.rowline .m{font-family:var(--mono);font-size:11px;letter-spacing:.2em;text-transform:uppercase;opacity:.6;justify-self:end}
/* ============ Pillar(支柱卡片) ============ */
.pillar{display:flex;flex-direction:column;gap:1.8vh}
.pillar .ic{font-family:var(--serif-en);font-style:italic;font-size:2.6vw;opacity:.45;font-weight:400}
.pillar .ic svg{width:2.8vw;height:2.8vw;stroke-width:1.2;opacity:.7}
.pillar .t{font-family:var(--serif-zh);font-weight:700;font-size:2.4vw;line-height:1.1}
.pillar .d{font-family:var(--sans-zh);font-weight:400;font-size:max(14px,1.1vw);opacity:.76;line-height:1.6}
/* ============ Signature / Highlight ============ */
.sign{font-family:var(--serif-en);font-style:italic;font-weight:500;font-size:2vw;opacity:.7}
.hi{position:relative;display:inline}
.slide.dark .hi::after{content:"";position:absolute;left:-.1em;right:-.1em;bottom:-.05em;height:.28em;background:rgba(var(--paper-rgb),.15);z-index:-1}
.slide.light .hi::after{content:"";position:absolute;left:-.1em;right:-.1em;bottom:-.05em;height:.28em;background:rgba(var(--ink-rgb),.08);z-index:-1}
/* ============ IconsLucide via CDN ============ */
.ico{width:1em;height:1em;display:inline-block;vertical-align:-.12em;stroke:currentColor;fill:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.ico-lg,.ico-md,.ico-sm{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round}
.ico-lg{width:2.6vw;height:2.6vw;stroke-width:1.2;display:inline-block}
.ico-md{width:1.8vw;height:1.8vw;stroke-width:1.3;display:inline-block;vertical-align:-.4em}
.ico-sm{width:1.1vw;height:1.1vw;stroke-width:1.4;display:inline-block;vertical-align:-.15em;opacity:.7}
/* ============ 图片占位(虚线框,提示设计师位置) ============ */
.img-slot{border:1.5px dashed rgba(127,127,127,.4);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:1vh;padding:2vh 2vw;font-family:var(--mono);font-size:10px;letter-spacing:.28em;text-transform:uppercase;opacity:.55;position:relative;aspect-ratio:16/9;width:100%;max-height:56vh;margin-inline:auto;box-sizing:border-box}
.img-slot::before{content:"";position:absolute;inset:8px;border:1px solid currentColor;opacity:.2}
.img-slot .plus{font-size:2vw;font-weight:300;opacity:.5;letter-spacing:0}
.img-slot .label{position:relative;z-index:2;text-align:center}
.img-slot.r-4x3{aspect-ratio:4/3}
.img-slot.r-3x2{aspect-ratio:3/2}
.img-slot.r-1x1{aspect-ratio:1/1}
/* ============ 图片实填框(关键:固定高度 + 只裁底部) ============
重要约束:高度用内联 height:Nvh 精确控制,不要用 aspect-ratio(会撑破布局)
object-position:top center 保证严禁裁剪顶部和左右,只裁剪底部
*/
.frame-img{overflow:hidden;position:relative;background:rgba(0,0,0,.04);box-sizing:border-box;width:100%;border-radius:4px}
.slide.dark .frame-img{background:rgba(255,255,255,.04);border-color:rgba(255,255,255,.12)}
.frame-img > img{width:100%;height:100%;object-fit:cover;object-position:top center;display:block}
.frame-cap{display:flex;justify-content:space-between;align-items:baseline;gap:1vw;margin-top:.8vh;font-family:var(--mono);font-size:10px;letter-spacing:.22em;text-transform:uppercase;opacity:.72}
.frame-cap .pf{font-family:var(--serif-zh);font-weight:600;font-size:max(13px,1vw);letter-spacing:.04em;text-transform:none;opacity:.94}
.frame-cap .nb{font-family:var(--serif-en);font-style:italic;font-size:max(15px,1.2vw);letter-spacing:.02em;text-transform:none;opacity:.88}
.frame-cap .idx{font-family:var(--mono);opacity:.5}
figure.tile{display:flex;flex-direction:column;margin:0;min-width:0}
figure.tile > .frame-img{flex:0 0 auto}
/* ============ 导航 ============ */
#nav{position:fixed;left:50%;bottom:2.6vh;transform:translateX(-50%);z-index:30;display:flex;gap:10px;padding:8px 14px;border-radius:999px;background:rgba(0,0,0,.18);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
#nav .dot{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,.3);cursor:pointer;transition:all .3s ease;border:0;padding:0}
#nav .dot:hover{background:rgba(255,255,255,.5);transform:scale(1.15)}
#nav .dot.active{background:rgba(255,255,255,.95);width:22px;border-radius:999px}
body.light-bg #nav{background:rgba(255,255,255,.25)}
body.light-bg #nav .dot{background:rgba(var(--ink-rgb),.25)}
body.light-bg #nav .dot.active{background:rgba(var(--ink-rgb),.9)}
#hint{position:fixed;bottom:3vh;right:3vw;z-index:30;font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;opacity:.4;mix-blend-mode:difference;color:#aaa}
/* ============================================================
============ LAYOUTS API · 面向 agent 的类(v2============
所有 layouts.md 中的骨架都基于下面这套命名。
如果你在 layouts.md 里看到某个类,它必须在下面有定义。
============================================================ */
/* ---------- .frame:每页主内容容器 ---------- */
.frame{flex:1;display:flex;flex-direction:column;min-height:0}
/* 当 .frame 同时加了 grid 类时,grid 的 display:grid 覆盖 flex */
.frame.grid-2-7-5,
.frame.grid-2-6-6,
.frame.grid-2-8-4,
.frame.grid-3-3,
.frame.grid-6{display:grid}
/* ---------- 标题层级(API 名称,衬线为主) ---------- */
.h-hero{
font-family:var(--serif-zh);
font-weight:900;
font-size:10vw;
line-height:.96;
letter-spacing:-.02em;
}
.h-xl{
font-family:var(--serif-zh);
font-weight:700;
font-size:6.2vw;
line-height:1.08;
letter-spacing:-.01em;
}
.h-sub{
font-family:var(--serif-zh);
font-weight:500;
font-size:3.1vw;
line-height:1.25;
letter-spacing:0;
opacity:.7;
}
.h-md{
font-family:var(--serif-zh);
font-weight:600;
font-size:2.3vw;
line-height:1.3;
}
/* 英文标题专用(Playfair 衬线) */
.h-hero-en,.h-xl-en{font-family:var(--serif-en);letter-spacing:-.025em}
/* ---------- lead 引语 ---------- */
.lead{
font-family:var(--serif-zh);
font-weight:400;
font-size:1.75vw;
line-height:1.5;
opacity:.86;
}
/* ---------- meta-row 底部元数据 ---------- */
.meta-row{
display:flex;
gap:1.2em;
align-items:baseline;
flex-wrap:wrap;
font-family:var(--mono);
font-size:max(12px,.92vw);
letter-spacing:.16em;
text-transform:uppercase;
opacity:.6;
}
/* ---------- stat-card(数据大字报用) ---------- */
.stat-card{
display:flex;
flex-direction:column;
gap:.8vh;
align-items:flex-start;
padding-top:1.6vh;
border-top:1px solid currentColor;
border-color:rgba(127,127,127,.3);
}
.stat-card .stat-label{
font-family:var(--mono);
font-size:max(10px,.78vw);
letter-spacing:.24em;
text-transform:uppercase;
opacity:.55;
}
.stat-card .stat-nb{
font-family:var(--serif-en);
font-weight:800;
font-size:5.8vw;
line-height:.9;
letter-spacing:-.03em;
font-feature-settings:"tnum";
margin-top:.4vh;
}
.stat-card .stat-nb .stat-unit{
font-family:var(--serif-zh);
font-weight:500;
font-size:.38em;
letter-spacing:0;
opacity:.72;
margin-left:.14em;
}
.stat-card .stat-note{
font-family:var(--sans-zh);
font-weight:400;
font-size:max(13px,1.05vw);
line-height:1.5;
opacity:.72;
margin-top:.6vh;
}
/* 当 stat-card 用于 grid-42x2),数字可以更大 */
.grid-4 .stat-card .stat-nb{font-size:7.5vw}
/* 当只有 3 个,字也可以稍大 */
.grid-3 .stat-card .stat-nb{font-size:6.8vw}
/* ---------- pipeline(流水线) ---------- */
.pipeline-section{
margin-top:4.4vh;
padding-top:2.8vh;
border-top:1px dashed rgba(127,127,127,.32);
}
.pipeline-section:first-of-type{
border-top:0;
padding-top:0;
margin-top:3vh;
}
.pipeline-label{
font-family:var(--mono);
font-size:max(11px,.85vw);
letter-spacing:.24em;
text-transform:uppercase;
opacity:.62;
margin-bottom:2.2vh;
}
.pipeline{
display:grid;
grid-template-columns:repeat(5,1fr);
gap:1.2vw;
}
.pipeline[data-cols="3"]{grid-template-columns:repeat(3,1fr)}
.pipeline[data-cols="4"]{grid-template-columns:repeat(4,1fr)}
.pipeline[data-cols="6"]{grid-template-columns:repeat(6,1fr)}
.step{
display:flex;
flex-direction:column;
gap:.8vh;
padding-top:1.4vh;
border-top:1px solid currentColor;
border-color:rgba(127,127,127,.35);
}
.step-nb{
font-family:var(--serif-en);
font-style:italic;
font-weight:500;
font-size:1.15vw;
opacity:.45;
}
.step-title{
font-family:var(--sans-zh);
font-weight:700;
font-size:1.55vw;
letter-spacing:.01em;
line-height:1.2;
}
.step-desc{
font-family:var(--sans-zh);
font-weight:400;
font-size:max(12px,.95vw);
line-height:1.45;
opacity:.72;
}
/* ---------- 网格(layouts.md 所用) ---------- */
/* 这些类独立挂到任何容器上都能生效,不依赖 .frame 复合选择器 */
.grid-2-7-5{display:grid;grid-template-columns:7fr 5fr;gap:3vw 4vh;align-items:start}
.grid-2-6-6{display:grid;grid-template-columns:1fr 1fr;gap:3vw 4vh;align-items:start}
.grid-2-8-4{display:grid;grid-template-columns:8fr 4fr;gap:3vw 4vh;align-items:start}
.grid-3-3{
display:grid;
grid-template-columns:repeat(3,1fr);
grid-auto-rows:minmax(0,1fr);
gap:2.4vh 2vw;
}
/* grid-6 已在旧样式里定义为 3x2,这里仅补 align */
/* ---------- 图片 frame-imglayouts.md 主命名) ---------- */
/* 在旧样式里已定义,这里补 img-cap 命名别名与增强 */
figure.frame-img{margin:0;display:flex;flex-direction:column;min-width:0}
.img-cap{
display:block;
margin-top:.8vh;
font-family:var(--mono);
font-size:max(10px,.8vw);
letter-spacing:.22em;
text-transform:uppercase;
opacity:.6;
}
/* callout src 命名别名 */
.callout-src{
display:block;
margin-top:1.6vh;
font-family:var(--mono);
font-size:11px;
letter-spacing:.2em;
text-transform:uppercase;
opacity:.6;
}
/* ---------- chrome & foot 补位(layouts.md 简单写法) ---------- */
.chrome{font-family:var(--mono);font-size:max(11px,.78vw);letter-spacing:.2em;text-transform:uppercase;opacity:.62}
.foot{font-family:var(--mono);font-size:max(11px,.78vw);letter-spacing:.18em;text-transform:uppercase;opacity:.5}
/* ---------- 响应式降级 ---------- */
@media (max-width:900px){
.display{font-size:16vw}
.display-zh{font-size:12vw}
.h1-zh{font-size:7vw}
.h-hero{font-size:14vw}
.h-xl{font-size:9vw}
.pipeline{grid-template-columns:repeat(2,1fr)}
.grid-2-7-5,.grid-2-6-6,.grid-2-8-4{grid-template-columns:1fr}
}
</style>
</head>
<body>
<canvas id="bg-dark" class="bg"></canvas>
<canvas id="bg-light" class="bg"></canvas>
<div id="hint">← → 翻页 · ESC 索引</div>
<div id="deck">
<!-- ============================================================
SLIDES 插入区 · 在此处填充所有 <section class="slide ..."> 页面
每页模板参考 references/page-patterns.md
页面组件参考 references/components.md
============================================================ -->
<!-- SLIDES_HERE -->
</div>
<div id="nav"></div>
<script>
/* =============== WebGL 双背景 ===============
深色页:Holographic Dispersion(全息色散 · 钛金暗流)—— 彩虹微扰、鼠标径向涟漪
浅色页:Spiral Vortex(旋转涡流 · 银色珍珠)—— domain-warp 流动、无中心
修改风格请参考 references/webgl-backgrounds.md
*/
const VS = `attribute vec2 position;void main(){gl_Position=vec4(position,0.0,1.0);}`;
const FS_DARK = `precision highp float;
uniform vec2 u_resolution;uniform float u_time;uniform vec2 u_mouse;
vec3 palette(float t,vec3 a,vec3 b,vec3 c,vec3 d){return a+b*cos(6.28318*(c*t+d));}
void main(){
vec2 uv=gl_FragCoord.xy/u_resolution.xy;
vec2 p=uv*2.0-1.0;p.x*=u_resolution.x/u_resolution.y;
vec2 m=u_mouse*2.0-1.0;m.x*=u_resolution.x/u_resolution.y;
float md=length(p-m);
float mr=sin(md*15.0-u_time*4.0)*exp(-md*3.0);p+=mr*0.08;
vec2 p0=p;
for(float i=1.0;i<4.0;i++){
p.x+=0.1/i*sin(i*3.0*p.y+u_time*0.4)+0.05;
p.y+=0.1/i*cos(i*2.0*p.x+u_time*0.3)-0.05;
}
float r=length(p);float ang=atan(p.y,p.x);
vec3 a=vec3(0.12,0.12,0.13);
vec3 b=vec3(0.03,0.04,0.05);
vec3 c=vec3(1.0,1.0,1.0);
vec3 d=vec3(0.1,0.2,0.4);
vec3 col=palette(r*1.5+p0.x*0.5+u_time*0.1,a,b,c,d);
float disp=sin(r*25.0-u_time*1.5+ang*2.0)*0.5+0.5;
col+=vec3(disp*0.015,disp*0.01,disp*0.02);
float hi=pow(sin(p.x*4.0+p.y*3.0+u_time)*0.5+0.5,8.0);
col+=hi*0.08;
vec3 base=vec3(0.05,0.05,0.06);
col=mix(base,col,0.85);
gl_FragColor=vec4(col,1.0);
}`;
const FS_LIGHT = `precision highp float;
uniform vec2 u_resolution;uniform float u_time;uniform vec2 u_mouse;
float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
float noise(vec2 p){
vec2 i=floor(p),f=fract(p);
float a=hash(i),b=hash(i+vec2(1,0));
float c=hash(i+vec2(0,1)),d=hash(i+vec2(1,1));
vec2 u=f*f*(3.0-2.0*f);
return mix(a,b,u.x)+(c-a)*u.y*(1.0-u.x)+(d-b)*u.x*u.y;
}
float fbm(vec2 p){
float v=0.0,a=0.5;
mat2 m=mat2(0.80,0.60,-0.60,0.80);
for(int i=0;i<5;i++){v+=a*noise(p);p=m*p*2.02;a*=0.5;}
return v;
}
void main(){
vec2 uv=gl_FragCoord.xy/u_resolution.xy;
vec2 p=uv;p.x*=u_resolution.x/u_resolution.y;
vec2 m=u_mouse;m.x*=u_resolution.x/u_resolution.y;
vec2 md=p-m;float dl=length(md);
p+=normalize(md+vec2(0.0001))*exp(-dl*5.0)*0.03;
vec2 q=vec2(fbm(p*1.8+u_time*0.07),fbm(p*1.8+vec2(5.2,1.3)+u_time*0.06));
vec2 r=vec2(fbm(p*2.0+q*1.3+vec2(1.7,9.2)+u_time*0.05),
fbm(p*2.0+q*1.3+vec2(8.3,2.8)+u_time*0.04));
float f=fbm(p*2.2+r*1.5);
vec3 silverDark=vec3(0.86,0.85,0.84);
vec3 paper=vec3(0.955,0.945,0.925);
vec3 col=mix(silverDark,paper,f);
float ph=r.x*2.2+u_time*0.35;
col+=vec3(0.78,0.62,0.92)*sin(ph)*0.055;
col+=vec3(0.55,0.72,0.95)*sin(ph*0.8+2.0)*0.05;
float hl=smoothstep(0.48,0.92,f);
col+=hl*0.06;
gl_FragColor=vec4(col,1.0);
}`;
const mouse={x:0.5,y:0.5};
addEventListener('mousemove',e=>{mouse.x=e.clientX/innerWidth;mouse.y=e.clientY/innerHeight});
function bootGL(canvasId, fsSrc){
const canvas=document.getElementById(canvasId);
const gl=canvas.getContext('webgl',{alpha:false,antialias:true});
if(!gl) return ()=>false;
const mk=(t,s)=>{const sh=gl.createShader(t);gl.shaderSource(sh,s);gl.compileShader(sh);return sh};
const prog=gl.createProgram();
gl.attachShader(prog,mk(gl.VERTEX_SHADER,VS));
gl.attachShader(prog,mk(gl.FRAGMENT_SHADER,fsSrc));
gl.linkProgram(prog);gl.useProgram(prog);
const buf=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buf);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);
const pos=gl.getAttribLocation(prog,'position');
gl.enableVertexAttribArray(pos);gl.vertexAttribPointer(pos,2,gl.FLOAT,false,0,0);
const lRes=gl.getUniformLocation(prog,'u_resolution');
const lT=gl.getUniformLocation(prog,'u_time');
const lM=gl.getUniformLocation(prog,'u_mouse');
const resize=()=>{
const d=Math.min(window.devicePixelRatio||1,2);
canvas.width=innerWidth*d;canvas.height=innerHeight*d;
gl.viewport(0,0,canvas.width,canvas.height);
};
addEventListener('resize',resize);resize();
return (tSec)=>{
gl.uniform2f(lRes,canvas.width,canvas.height);
gl.uniform1f(lT,tSec);
gl.uniform2f(lM,mouse.x,1-mouse.y);
gl.drawArrays(gl.TRIANGLES,0,6);
return true;
};
}
const drawDark=bootGL('bg-dark',FS_DARK);
const drawLight=bootGL('bg-light',FS_LIGHT);
const t0=Date.now();
(function loop(){
const t=(Date.now()-t0)/1000;
drawDark(t);drawLight(t);
requestAnimationFrame(loop);
})();
// =============== 导航(翻页 / 圆点 / 键盘 / 滚轮 / 触屏) ===============
const deck=document.getElementById('deck');
const slides=deck.querySelectorAll('.slide');
const nav=document.getElementById('nav');
let idx=0,total=slides.length,lock=false;
// 关键:矫正 deck 宽度为 total * 100vw,否则翻页会错位
deck.style.width=(total*100)+'vw';
slides.forEach((s,i)=>{
const b=document.createElement('button');
b.className='dot';b.dataset.i=i;b.setAttribute('aria-label','Page '+(i+1));
b.onclick=()=>go(i);
nav.appendChild(b);
});
function go(n){
if(lock)return;
idx=Math.max(0,Math.min(total-1,n));
deck.style.transform=`translateX(${-idx*100}vw)`;
nav.querySelectorAll('.dot').forEach((d,i)=>d.classList.toggle('active',i===idx));
/* 主题切换:优先读 data-theme,其次从 classlight/dark)推断 */
const el=slides[idx];
const th=el.dataset.theme || (el.classList.contains('light')?'light':(el.classList.contains('dark')?'dark':'dark'));
document.body.classList.toggle('light-bg',th==='light');
lock=true;setTimeout(()=>lock=false,700);
}
/* =============== ESC 索引视图 =============== */
let overviewOn=false;
const ov=document.createElement('div');
ov.id='overview';
ov.style.cssText='position:fixed;inset:0;z-index:100;background:rgba(var(--ink-rgb),.92);backdrop-filter:blur(12px);display:none;overflow-y:auto;padding:4vh 4vw';
document.body.appendChild(ov);
function buildOverview(){
ov.innerHTML='';
const grid=document.createElement('div');
grid.style.cssText='display:grid;grid-template-columns:repeat(4,1fr);gap:2vh 1.6vw;max-width:90vw;margin:0 auto';
slides.forEach((s,i)=>{
const card=document.createElement('div');
card.style.cssText='cursor:pointer;border-radius:6px;overflow:hidden;border:2px solid '+(i===idx?'rgba(var(--paper-rgb),.8)':'rgba(var(--paper-rgb),.15)')+';transition:border-color .2s';
card.onmouseenter=()=>card.style.borderColor='rgba(var(--paper-rgb),.6)';
card.onmouseleave=()=>card.style.borderColor=i===idx?'rgba(var(--paper-rgb),.8)':'rgba(var(--paper-rgb),.15)';
const wrap=document.createElement('div');
wrap.style.cssText='width:100%;aspect-ratio:16/9;overflow:hidden;position:relative;pointer-events:none;background:'+(s.classList.contains('light')?'var(--paper)':'var(--ink)');
const clone=s.cloneNode(true);
clone.style.cssText='width:100vw;height:100vh;transform:scale('+(1/4.5)+');transform-origin:top left;position:absolute;top:0;left:0;pointer-events:none';
wrap.appendChild(clone);
const label=document.createElement('div');
label.style.cssText='padding:6px 10px;font-family:var(--mono);font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--paper);opacity:.7';
label.textContent=(i+1)+' / '+total;
card.appendChild(wrap);
card.appendChild(label);
card.onclick=()=>{toggleOverview();go(i)};
grid.appendChild(card);
});
ov.appendChild(grid);
}
function toggleOverview(){
overviewOn=!overviewOn;
if(overviewOn){buildOverview();ov.style.display='block';}
else{ov.style.display='none';}
}
addEventListener('keydown',e=>{
if(e.key==='Escape'){e.preventDefault();toggleOverview();return;}
if(overviewOn)return;
if(e.key==='ArrowRight'||e.key==='PageDown'||e.key===' '||e.key==='ArrowDown')go(idx+1);
if(e.key==='ArrowLeft'||e.key==='PageUp'||e.key==='ArrowUp')go(idx-1);
if(e.key==='Home')go(0);
if(e.key==='End')go(total-1);
});
let wheelTO=null,wheelAcc=0;
addEventListener('wheel',e=>{
wheelAcc+=e.deltaY+e.deltaX;
if(Math.abs(wheelAcc)>50){go(idx+(wheelAcc>0?1:-1));wheelAcc=0;}
clearTimeout(wheelTO);wheelTO=setTimeout(()=>wheelAcc=0,150);
},{passive:true});
let tx=0,ty=0;
addEventListener('touchstart',e=>{tx=e.touches[0].clientX;ty=e.touches[0].clientY},{passive:true});
addEventListener('touchend',e=>{
const dx=(e.changedTouches[0].clientX-tx);
const dy=(e.changedTouches[0].clientY-ty);
if(Math.abs(dx)>50&&Math.abs(dx)>Math.abs(dy))go(idx+(dx<0?1:-1));
},{passive:true});
go(0);
</script>
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
<script>lucide.createIcons();</script>
</body>
</html>
+265
View File
@@ -0,0 +1,265 @@
# 质量检查清单(Checklist
这个清单来自"一人公司"分享 PPT 的真实迭代过程。每一条都是踩过坑之后总结的,按重要性排序。
生成 PPT 前,先通读一遍;生成后,逐项自检。
---
## 🔴 P0 · 一定不能犯的错
### 0. 生成前必须通过的类名校验(最重要)
**现象**:直接把 layouts.md 的骨架粘到新 HTML,结果样式全部丢失——大标题变成非衬线、数据大字报字体小得像正文、pipeline 多页糊成一坨、图片堆到浏览器底部。
**根因**:如果 `template.html``<style>` 里没有这些类的定义,浏览器就 fallback 到默认样式。
**做法**
- **生成 PPT 前,必须先 `Read` `assets/template.html`**,确认 layouts.md 里用到的类都已定义
- 最常见遗漏的类:`h-hero / h-xl / h-sub / h-md / lead / meta-row / stat-card / stat-label / stat-nb / stat-unit / stat-note / pipeline-section / pipeline-label / pipeline / step / step-nb / step-title / step-desc / grid-2-7-5 / grid-2-6-6 / grid-2-8-4 / grid-3-3 / frame / img-cap / callout-src`
- 如果某个类确实缺了,**在 template.html 的 `<style>` 里补上**,不要在每页 inline 重写
- 生成后打开浏览器,如果看到"大标题是非衬线"或"pipeline 步骤挤在一行",几乎 100% 是这个问题
### 1. 不要用 emoji 作图标
**现象**:在中式杂志风格里用 emoji(🎯 💡 ✅)会立刻破坏格调。
**做法**:用 Lucide 图标库,CDN 方式引用:
```html
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
...
<i data-lucide="target" class="ico-md"></i>
...
<script>lucide.createIcons();</script>
```
常用图标名:`target / palette / search-check / compass / share-2 / crown / check-circle / x-circle / plus / arrow-right / grid-2x2 / network`
### 2. 图片只允许裁底部,左右和顶部绝对不能切
**现象**:用 `aspect-ratio` 撑图,网格会在父容器不足时堆叠或切掉图片关键信息(比如截图上部的标题栏)。
**做法**:图片容器用**固定 height + overflow hidden**,图片走 `object-fit:cover + object-position:top`
```html
<figure class="frame-img" style="height:26vh">
<img src="screenshot.png">
</figure>
```
CSS 里 `.frame-img img` 已经预设 `object-position:top`,只裁底。
**绝不用这种写法**(会在网格中撑破容器):
```html
<!-- 坏例 -->
<figure class="frame-img" style="aspect-ratio: 16/9">...</figure>
```
**例外**:单张主视觉(非网格内)可以用 `aspect-ratio + max-height`,因为父容器会兜底。
### 2b. 亮页面配暗 WebGL = 灰蒙蒙(主题切换没生效)
**现象**:所有 light 页面背景都像蒙了一层灰,甚至 hero light 也灰。
**根因**:JS 根据 slide 的主题切换两张 canvas 的 opacity。如果整个 deck 开场是 hero dark,而没有任何机制能把 bg 切到 light,body 永远不加 `light-bg` 类,`canvas#bg-dark` 一直在上面。
**做法**:
- 模板里 `go()` 函数已改为从 `classList` 推断主题(`light` / `dark`),所以 **slide 必须明确带 `light` 或 `dark` 类**。不要漏写,更不要用其他自定义主题名
- hero 页用 `hero light` / `hero dark`,正文页用 `light` / `dark`。只写 `hero` 不带主题色是坏的
- 一个 deck 里必须至少有一个 **非 hero 的 light 页**,确保 body 有机会加 `light-bg`
### 2b-2. 整个 deck 全是 light,没有节奏
**现象**:除封面 `hero dark` 外,其余所有页面默认写 `light`——视觉平淡,没有呼吸感,白花花一片。
**根因**:layouts.md 的骨架默认全写 `light`,如果只是粘贴骨架不调整主题,就会全亮。
**做法**:
- **生成前画"主题节奏表"**:每一页写清 `hero dark` / `hero light` / `light` / `dark` 中的哪一个,对齐后再写代码
- **硬规则**:连续 3 页以上同主题 = 不允许;8 页以上必须有 ≥1 `hero dark` + ≥1 `hero light`;不能全是 `light` 正文页——必须有 `dark` 正文页
- **按布局选主题**(详见 layouts.md 开头"主题节奏规划"):
- 左文右图(Layout 4)、大引用(Layout 8)、图文混排(Layout 10)→ **`light` / `dark` 交替**
- 大字报、图片网格、Pipeline、对比页 → `light`(截图/数字/流程需要亮底)
- 封面、问题页 → `hero dark`
- 章节幕封 → `hero dark``hero light` 交替
- **生成后自检**:`grep 'class="slide' index.html`,目视确认节奏有交错
### 2c. chrome 和 kicker 不要写同一句话
**现象**:左上角 `.chrome` 写"Design First · 设计先行",同一页里 `.kicker` 又写"Phase 01 · 设计阶段"——同义翻译,AI 味浓。
**做法**:
- **chrome = 杂志页眉 / 导航标签**:跨多页可相同(如 "Act II · Workflow"、"Data · Result"、"lukew.com · 2026.04")
- **kicker = 本页独一份的引导句**:短、有钩子、是大标题的"小前缀"(如 "BUT"、"一个人,做了什么。"、"The Question")
- 一个描述栏目,一个描述这一页——绝不互相翻译
### 3. 大标题字号不能超过屏宽 / 单字数
**现象**:中文大标题字号设太大(比如 13vw),结果每行只容 1 个字,强制换行非常难看。
**做法**
- `h-hero`(最大):10vw,**且标题长度 ≤ 5 字**
- `h-xl`(次大):6vw-7vw
- 长标题用 `<br>` 手工断行,不要依赖自动换行
- 必要时加 `white-space:nowrap`
**示例**`我不是程序员。`6 字)用 `h-xl` 7.2vw + nowrap,一行排完。
### 4. 字体分工:标题衬线、正文非衬线
**做法**
- 大标题、重点 quote、数字大字 → **衬线字体**Noto Serif SC + Playfair Display + Source Serif
- 正文、描述、pipeline 步骤名 → **非衬线字体**Noto Sans SC + Inter
- 元数据、代码、标签 → **等宽字体**IBM Plex Mono + JetBrains Mono
所有字体用 Google Fonts CDN 引入,模板里已预设。
### 4b. 图片不要用 `align-self:end` 贴底
**现象**:左文右图布局里,为了让右列图片和左列 callout 底部对齐,在 `<figure>` 上加 `align-self:end`。结果:
- 如果父容器不是 grid(比如类名没定义),`align-self` 完全失效,图片掉到文档流最下面被浏览器底栏遮挡
- 即使是 grid,图片会在 cell 里贴底,低分屏上仍然被 `.foot``#nav` 圆点遮挡
**做法**:
- 图文混排**必须用 `.frame.grid-2-7-5`**(或 `.grid-2-6-6`/`.grid-2-8-4`)
- 右列 `<figure class="frame-img">`**标准比例 16/10 或 4/3 + max-height:56vh**,自然贴顶即可
- 要让左列 callout 看起来"贴底",给**左列**加 flex column + `justify-content:space-between`,不要动右列
### 4c. 图片不要用原图奇葩比例
**现象**:`aspect-ratio: 2592/1798` 这种从原图复制的比例,在不同屏幕下撑出奇怪的空白或溢出。
**做法**:无论原图什么比例,占位器固定用标准比例 **16/10 / 4/3 / 3/2 / 1/1 / 16/9**。图片自动 `object-fit:cover + object-position:top`,顶部不裁,底部裁掉一点无伤大雅。
### 5. 不要给图片加厚边框 / 阴影
**现象**:为了"高级感"加了强阴影或黑框,瞬间变成商务 PPT。
**做法**:最多 1-4px 的微圆角 + **极淡的底噪**(已在模板里)。不要加 `box-shadow`,不要加 `border`(除非 1px 极淡的灰)。
---
## 🟡 P1 · 排版节奏
### 6. Hero 页和非 hero 页要交替
**推荐节奏**25-30 页):
```
Hero Cover → Act Divider (hero) → 3-4 pages non-hero → Act Divider (hero)
→ 4-5 pages non-hero → Hero Question → ... → Hero Close
```
连续 2 页以上 hero 会让人疲劳,连续 4 页以上 non-hero 会让节奏死。
### 7. 大字报页和密集页要交替
大字报(big numbers / hero question)和密集页(pipeline / image grid)交替出现,听众眼睛才不累。
### 8. 同一概念的英文/中文用法要统一
**现象**:一会儿写 "Skills",一会儿写 "技能",一会儿写 "薄承载厚技能",全篇不一致。
**做法**
- 术语优先用**英文单词**Skills / Harness / Pipeline / Workflow),这些都是圈内熟悉词
- **别硬翻译**,硬翻译反而生硬
- 整个 deck 里同一个词 1 个写法
### 9. 底部 chrome 的页码要一致
`XX / 总页数` 的格式(比如 `05 / 27`)。**不要在右上角加动态页码**(会和 `.chrome` 重复)。
---
## 🟢 P2 · 视觉打磨
### 10. WebGL 背景的遮罩透明度
**dark hero**:遮罩 12-15%WebGL 明显透出)
**light hero**:遮罩 16-20%(WebGL 隐约可见,不抢字)
**普通 light/dark 页**:遮罩 92-95%(几乎不透)
如果页面文字非常少(hero question),遮罩可以再薄些;如果正文密集,必须加厚遮罩确保可读。
### 11. Light hero 的 shader 不能有强中心点
**现象**Spiral Vortex、径向涟漪在 light 主题下太显眼,像 Windows 98 屏保。
**做法**light hero 用 FBM 域扭曲驱动的无中心流动,底色保持银/纸色(接近 #F0F0F0 / #FBF8F3),彩虹偏色 subtle(0.05 以下)。
### 12. Dark hero 允许更多视觉冲击
Dark hero 可以用 Holographic Dispersion(钛金色散)等带中心结构的 shader,因为黑底能容纳更多视觉信息。
### 13. 左文右图的对齐
- 左列的文字组 `justify-content:space-between`:标题贴顶,引用框贴底
- 右列图片 `align-self:end`:和左列的底部元素对齐
- 网格整体 `align-items:start`(不是 `center` / `end`
### 14. 图片的微弱圆角
所有 `.frame-img``.frame-img img` 都加 `border-radius:4px`,视觉上"柔和"但不软。**不要超过 8px**,否则像消费 app UI。
---
## 🔵 P3 · 操作细节
### 15. 图片路径用相对路径
图片放在 `images/` 文件夹下,HTML 里用相对路径 `images/xxx.png`,不要用绝对路径。
### 16. 页码在 `.chrome` 里写死
JS 会动态算总页数并扩展底部翻页圆点,但 `.chrome` 里的 `XX / N` 是写死的。加页/删页时要手工改 N。
### 17. 翻页导航要保留
模板默认支持:← → / 滚轮 / 触屏滑动 / 底部圆点 / Home·End。不要删 JS 里的导航逻辑。
### 18. 不要用 `height:100vh` 硬设,用 `min-height:80vh`
`100vh` 会让内容刚好卡满屏幕,但浏览器工具栏、标签栏会吃掉一部分高度,导致内容溢出。用 `min-height:80vh + align-content:center` 更稳。
---
## 🧪 最终自检清单
生成完 PPT 后,逐项对照这个清单(勾一下):
```
预检(生成前)
□ 已读过 template.html 的 <style>,确认所需类都存在
□ 已决定每页用哪个 Layout(1-10)
□ 已画出"主题节奏表":每页明确 hero dark / hero light / light / dark
□ 节奏表满足硬规则:无连续 3 页同主题 / 有 ≥1 hero dark + ≥1 hero light(8 页以上) / 至少有 1 个 dark 正文页
□ `<title>` 已改为实际 deck 标题(grep "[必填]" 应无结果)
内容
□ 每一幕的页数比例合理(不会头重脚轻)
□ 没有使用 emoji 作图标
□ Skills / Harness 等术语用法统一
□ 每页的 kicker + 标题 + 正文 三级信息清晰
排版
□ 所有大标题没有出现 1 字 1 行的换行
□ 图片网格用 height:Nvh 而非 aspect-ratio
□ 图片只裁底部,顶部和左右完整
□ 衬线/非衬线字体分工符合模板
□ Pipeline 多组之间有明显分隔
视觉
□ hero 页和 non-hero 页交替
□ WebGL 背景在 hero 页可见
□ 图片有微弱圆角
□ 没有沉重的阴影和边框
交互
□ ← → 翻页正常
□ 底部圆点数量与总页数匹配
□ chrome 里的页码和实际页号一致
□ ESC 键触发索引视图(如果保留)
```
全勾完,才是合格的 PPT。
+363
View File
@@ -0,0 +1,363 @@
# 组件参考 · Components
这是 `magazine-web-ppt` skill 的组件手册。template.html 已经定义好了所有样式,这里只写"这个组件长什么样、怎么用"。
## 目录
- [基础 Slide 外壳](#基础-slide-外壳)
- [字体 Typography](#字体-typography)
- [Chrome & Foot](#chrome--foot)
- [Callout 引用框](#callout-引用框)
- [Stat 数字矩阵](#stat-数字矩阵)
- [Platform 平台卡](#platform-平台卡)
- [Rowline 表格行](#rowline-表格行)
- [Pillar 支柱卡](#pillar-支柱卡)
- [Tag & Kicker](#tag--kicker)
- [Figure 图片框](#figure-图片框)
- [Icons 图标](#icons-图标)
- [Ghost 巨型背景字](#ghost-巨型背景字)
- [Highlight 荧光标记](#highlight-荧光标记)
---
## 基础 Slide 外壳
每一页都是一个 `<section class="slide ...">`。必须包含 `data-theme` 属性(`light``dark`),JS 翻页时会根据这个属性切换背景。
```html
<section class="slide light" data-theme="light"> <!-- 浅色页 -->
<section class="slide dark" data-theme="dark"> <!-- 深色页 -->
<section class="slide light hero" data-theme="light"> <!-- Hero 页:浅色 + 薄遮罩透出 WebGL -->
<section class="slide dark hero" data-theme="dark"> <!-- Hero 页:深色 + 薄遮罩 -->
```
**light vs dark 的使用:交替使用**,每 2-3 页切换一次主题,避免连续超过 3 页同色。翻页时 WebGL 背景会自动在两个 shader 之间渐变过渡。
**hero 类的使用**:只给视觉主导的页面加(封面、金句页、章节过渡、结尾)。加 `hero` 后遮罩降到 12-16%,WebGL 背景会大幅透出,所以不要在 hero 页上放太多文字。
---
## 字体 Typography
字体分工是本模板最重要的规则,严禁混用。
| Class | 用途 | 字体 |
|---|---|---|
| `.display` | 超大号英文(Hero 页) | Playfair Display 700, 11vw |
| `.display-zh` | 超大号中文标题 | Noto Serif SC 700, 7.8vw |
| `.h1-zh` | 页面主标题 | Noto Serif SC 700, 4.6vw |
| `.h2-zh` | 副标题 | Noto Serif SC 600, 3.2vw |
| `.h3-zh` | 流水线步骤标题 | Noto Serif SC 500, 1.9vw |
| `.lead` | 引导段(比 body 大) | Noto Serif SC 400, 1.9vw |
| `.body-zh` | **正文/描述(非衬线)** | Noto Sans SC 400, 1.22vw |
| `.body-serif` | 正文(衬线) | Noto Serif SC 400, 1.3vw |
| `.kicker` | 小节提示(标题上方) | IBM Plex Mono, 12px uppercase |
| `.meta` | 元信息标签 | IBM Plex Mono, 0.88vw uppercase |
| `.big-num` | 巨型数字 | Playfair Display 800, 10vw |
| `.mid-num` | 中号数字 | Playfair Display 700, 5.5vw |
**核心规则**
- **衬线**`serif-zh` / `serif-en`):标题、重点金句、数字 —— 用于"视觉重音"
- **非衬线**`sans-zh`):正文描述、大段阅读内容 —— 用于"信息密度"
- **等宽**`mono`):kicker、meta、foot 的英文标签 —— 用于"装饰节奏"
**强调技巧**
- `<em class="en">英文词</em>` —— 把英文词渲染成 Playfair Display 斜体(很好看)
- `<em style="opacity:.65">短语</em>` —— 让标题后半段淡出,制造节奏
---
## Chrome & Foot
每一页的顶部和底部的元信息条。几乎所有页都应该有。
```html
<div class="chrome">
<div class="left">
<span>第一幕 · 硬数据</span>
<span class="sep"></span>
<span>Act I</span>
</div>
<div class="right"><span>02 / 27</span></div>
</div>
<!-- ... 页面主体 ... -->
<div class="foot">
<div class="title">项目名 · CodePilot | github.com/codepilot</div>
<div>Act I · Dev Numbers</div>
</div>
```
**规则**
- `chrome.right` 总是放页码 `NN / TOTAL` TOTAL 为总页数)
- `foot.title` 是中文说明,`foot.right` 是英文 act 标记
- chrome 和 foot 共同构成杂志感的"页眉页脚"
---
## Callout 引用框
展示金句 / 关键观点 / 他人引言。
```html
<div class="callout" style="max-width:80vw">
<div class="q-big">"这东西在三年前,<br>需要一个十人团队做一年。"</div>
<span class="cite">— 一个观察者的判断</span>
</div>
```
变体:
- 不带 cite:去掉 `<span class="cite">` 即可
- 带英文金句:`<em class="en">"Thin Harness, Fat Skills."</em>`
- 在 hero 页使用:外层加 `style="position:relative;z-index:2"`(避免被背景遮罩盖住)
---
## Stat 数字矩阵
展示数据指标,常与 `.grid-6` / `.grid-4` 配合。
```html
<div class="grid-6">
<div class="stat">
<span class="m">Duration</span>
<span class="n">64<em style="font-size:.4em;opacity:.5;font-style:normal"> 天</em></span>
<span class="l">从 0 到现在</span>
</div>
<!-- ... 更多 stat ... -->
</div>
```
三段式结构:`.m` 等宽小标签 → `.n` 巨型数字 → `.l` 描述说明。数字后的单位用 `<em>` 缩小到 0.4emopacity 0.5。
**常用布局容器**
- `.grid-6` — 3×2 网格(最常用,6 个 stat)
- `.grid-4` — 2×2 网格(4 个 stat
- `.grid-3` — 3 等分单行(3 个 stat / pillar
---
## Platform 平台卡
展示社交平台 / 渠道 + 粉丝数。
```html
<div class="plat">
<div class="sub">Weibo</div>
<div class="name">微博</div>
<div class="nb">289K</div>
</div>
```
可选第四行(补充说明):
```html
<div class="body-zh" style="font-size:max(11px,.8vw);opacity:.5;margin-top:.6vh">
含小绿书同步
</div>
```
**"Also On" 变体**(补充平台):
```html
<div class="plat" style="border-top-style:dashed;opacity:.72">
<div class="sub">Also On</div>
<div class="body-zh" style="font-weight:600;margin-top:.8vh">
B 站 · 知乎
</div>
</div>
```
---
## Rowline 表格行
列表式内容,每行一个条目。
```html
<div class="rowline">
<div class="k">CLAUDE.md</div>
<div class="v">你该怎么做事 —— 行为规则 + 工作偏好 + 禁止事项</div>
<div class="m">EMPLOYEE · HANDBOOK</div>
</div>
```
三列结构:`.k` 衬线关键词 · `.v` 正文描述 · `.m` 等宽标签(右对齐)。第一个和最后一个 rowline 自动加上下边框。
**变体:2 列**`style="grid-template-columns:1fr 3fr"` 去掉 `.m` 列。
---
## Pillar 支柱卡
三支柱结构,常用于"概念并列"类型页面。
```html
<div class="grid-3">
<div class="pillar">
<div class="ic">01</div>
<div class="t">三层<br>文档体系</div>
<div class="d">CLAUDE.md<br>+ 项目知识库<br>+ 护栏文件</div>
</div>
<!-- ... 更多 pillar ... -->
</div>
```
**带图标的 pillar(用于强调性页面)**:
```html
<div class="pillar" style="padding:4vh 2vw;border:1px solid currentColor;border-color:rgba(10,10,11,.2)">
<div class="ic"><i data-lucide="compass" class="ico-lg"></i></div>
<div class="t">判断力</div>
<div class="d">决策和方向的权威。<br>取舍、品味、方向感。</div>
</div>
```
`.ic` 可以是序号(`01 / 02 / 03``A. / B. / C.`),也可以是 Lucide 图标。
---
## Tag & Kicker
**Kicker** 是标题上方的小提示文字(等宽、全大写、小字号):
```html
<div class="kicker">过去 64 天 · 开发篇</div>
<div class="h1-zh">一个人,做了什么。</div>
```
**Tag** 是独立的标签胶囊(带边框):
```html
<div style="display:flex;gap:1.6vw;flex-wrap:wrap">
<div class="tag">早上 10 点起床</div>
<div class="tag">周二 / 四下午健身</div>
<div class="tag">晚上照样看剧 · 玩游戏</div>
</div>
```
---
## Figure 图片框
**这是本模板最容易踩坑的组件,务必遵守以下规则**。
### 基础结构
```html
<figure class="tile">
<div class="frame-img" style="height:26vh">
<img src="图片素材/xxx.png" alt="说明">
</div>
<figcaption class="frame-cap">
<span class="pf">推特 · Twitter</span>
<span class="nb">137K</span>
</figcaption>
</figure>
```
### 关键约束(血泪经验,不要违反)
1. **必须用 `height:Nvh` 固定高度**,不要用 `aspect-ratio`
- 原因:用 aspect-ratio 在网格里会撑破父容器,导致图片堆叠。
- 推荐尺寸:`height:18vh` (紧凑条形) / `22vh` (标准网格) / `26vh` (突出展示) / `28vh` (大图)。
2. **`object-position:top center`(已在 CSS 里设好)**,只允许裁掉底部。
- 严禁裁剪左右和顶部 —— 这是图片的核心身份信息区。
3. **网格里多张图时,用内联 grid 而不是 `grid-3`**
```html
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1vh 1.2vw">
<figure class="tile">...</figure>
<figure class="tile">...</figure>
<figure class="tile">...</figure>
</div>
```
4. **图片与布局其他部分对齐**figure 单独加 `align-self:end` 让图片贴底。
### Frame Caption 变体
```html
<!-- 标准:左 figure 名,右数字 -->
<figcaption class="frame-cap">
<span class="pf">推特 · Twitter</span>
<span class="nb">137K</span>
</figcaption>
<!-- 带编号 -->
<figcaption class="frame-cap">
<span class="idx">01</span>
<span class="pf">AI 润色</span>
<span>Polish</span>
</figcaption>
```
### 图片占位(设计阶段占位符)
图片还没有就位时,用虚线框占位:
```html
<div class="img-slot r-4x3"> <!-- r-4x3 / r-16x9(default) / r-3x2 / r-1x1 -->
<span class="plus">+</span>
<span class="label">GitHub 截图位置</span>
</div>
```
---
## Icons 图标
**严禁使用 emoji**。用 Lucide via CDNtemplate.html 已引入)。
```html
<i data-lucide="compass" class="ico-lg"></i> <!-- 大图标(pillar 用) -->
<i data-lucide="target" class="ico-md"></i> <!-- 中图标(列表项用) -->
<i data-lucide="check-circle" class="ico-sm"></i> <!-- 小图标(inline 用) -->
```
**常用 Lucide 图标名**(按含义分组):
- 判断类:`compass`, `target`, `crosshair`, `search-check`
- 关系类:`share-2`, `users`, `network`, `link`, `handshake`
- 品牌类:`crown`, `gem`, `award`, `star`, `badge-check`
- 流程类:`workflow`, `route`, `arrow-right-left`, `repeat`
- 数据类:`grid-2x2`, `bar-chart-3`, `trending-up`, `activity`
- 审美类:`palette`, `brush`, `eye`, `sparkles`
- 对错类:`check-circle`, `x-circle`, `check`, `x`
- 方向类:`arrow-right`, `arrow-up-right`, `corner-down-right`
**图标与文字 inline 组合**
```html
<div class="h3-zh" style="display:flex;align-items:center;gap:.8em">
<i data-lucide="target" class="ico-md"></i>
判断 — 什么值得写
</div>
```
---
## Ghost 巨型背景字
用作"装饰性背景字",极低透明度,营造杂志感。
```html
<div class="ghost" style="right:-6vw;top:-8vh">BUT</div>
<div class="ghost" style="left:-8vw;bottom:-18vh;font-style:italic">Harness</div>
```
- 字号 34vwopacity 0.06
- 常用定位:`right:-6vw;top:-8vh`(右上超出)/ `left:-8vw;bottom:-18vh`(左下超出)
- 内容:英文单词或数字(章节序号 01/02/03、关键词 BUT/NOW/HERE
**注意**:使用 ghost 的页面里,其他内容要加 `position:relative;z-index:2` 避免被压到下面。
---
## Highlight 荧光标记
行内短语的"荧光笔"效果:
```html
<span class="hi">不是</span>
<span class="hi">一次性爆发</span>
```
在文字底部生成一条半透明高亮条。深色主题用亮条,浅色主题用暗条(CSS 已处理)。
**适合场景**:只对关键 1-3 个词使用,不要大面积用。
+630
View File
@@ -0,0 +1,630 @@
# 页面布局库(Layouts
本文档收录 10 种最常用的页面布局骨架。每种都是一个完整可粘贴的 `<section class="slide ...">...</section>` 代码块,直接替换文案/图片即可使用。
---
## ⚠️ 生成前必读(Pre-flight
### A. 类名必须来自 template.html
layouts.md 使用的所有类(`h-hero` / `h-xl` / `h-sub` / `h-md` / `lead` / `meta-row` / `stat-card` / `stat-label` / `stat-nb` / `stat-unit` / `stat-note` / `pipeline-section` / `pipeline-label` / `pipeline` / `step` / `step-nb` / `step-title` / `step-desc` / `grid-2-7-5` / `grid-2-6-6` / `grid-2-8-4` / `grid-3-3` / `grid-6` / `grid-3` / `grid-4` / `frame` / `frame-img` / `img-cap` / `callout` / `callout-src` / `kicker`)都在 `assets/template.html``<style>` 块里预定义。
**不要发明新类名**。如果必须自定义,用 `style="..."` inline 写。生成前若不确定某个类是否存在,grep template.html 确认。
### B. 图片比例规范(非常重要)
**永远用标准比例**,不要用原图 `aspect-ratio: 2592/1798` 这种奇葩比例:
| 场景 | 推荐比例 | 写法 |
|------|---------|------|
| 左文右图 主图 | 16:10 或 4:3 | `aspect-ratio:16/10; max-height:54vh` |
| 图片网格(多图对比) | 统一 | **固定 `height:26vh`,不用 aspect-ratio** |
| 左小图 + 右文字 | 1:1 或 3:2 | `aspect-ratio:1/1; max-width:40vw` |
| 全屏主视觉 | 16:9 | `aspect-ratio:16/9; max-height:64vh` |
| 图文混排小插图 | 3:2 | `aspect-ratio:3/2; max-width:30vw` |
图片必须包在 `<figure class="frame-img">` 里,里面的 `<img>` 会自动 `object-fit:cover + object-position:top center`,只裁底部,不裁顶/左/右。
### C. 图片定位准则(避免图片堆到页面最底部、被浏览器工具栏遮挡)
**错误做法**(已踩坑,不要再犯):
- 在非 grid 容器里用 `align-self:end``align-self` 在 flex/grid 之外完全无效,图片会掉到文档流末尾堆底
- 用 `position:absolute + bottom:0` 把图"固定"到底:会被底部 `.foot``#nav` 圆点遮挡
- 单张图片只写 `height:N vh` 不限 `max-height`:在低分屏会撑出视口
**正确做法**
- 图文混排**必须用 `.frame.grid-2-7-5`**(或 `.grid-2-6-6` / `.grid-2-8-4`)的 grid 结构
- grid 容器默认 `align-items:start`(已在 template 中设置),图片自然贴到 cell 顶端
- 如果需要"图片底对齐左列 callout"**左列用 flex column + `justify-content:space-between`**(让 callout 自己贴左列底),**右列 figure 直接保持 align-items:start 即可**,不要加 `align-self:end`
- 所有 grid 父容器建议加 inline `style="padding-top:6vh"`,给标题区留呼吸空间
### D. 主题色与主题节奏
- 主题色从 `references/themes.md` 的 5 套预设里选一套,不允许自定义 hex 值
- 主题节奏(每页用 light / dark / hero light / hero dark 哪一个)在下文"主题节奏规划"一节有硬规则,生成前必读
- 两件事都要在挑布局之前决定,避免返工
---
## 0. 基础结构(所有 slide 都一样)
```html
<section class="slide [light|dark|hero light|hero dark]">
<div class="chrome">
<div>上下文标签 · 子标签</div>
<div>ACT · 页号 / 总页数</div>
</div>
<!-- 主内容 -->
<div class="foot">
<div>页码说明 · Page Description</div>
<div>— · —</div>
</div>
</section>
```
- 非 hero 页建议加 `light``dark` 主题;hero 页加 `hero light``hero dark`(参与 WebGL 主题插值)
- `chrome``foot` 是可选但推荐保留的上下左右四角元数据
- **hero 页用于章节封面/开场/收束/转场**,非 hero 页用于正文
### ⚠️ chrome 和 kicker 不要写同一句话
这是最常见的内容重复问题。两者在语义上完全不同的维度:
| 位置 | 角色 | 内容性质 | 例子 |
|------|------|---------|------|
| `.chrome` 左上 | **杂志页眉 / 导航元数据** | 稳定的"栏目名"或"章节分类",跨多页可以相同 | "Act II · Workflow" / "Data · Result" / "lukew.com · 2026.04" |
| `.chrome` 右上 | **页号 + 幕号** | 固定格式 | "Act II · 15 / 25" |
| `.kicker` | **这一页独一份的引导句** | 是大标题的"小前缀",像杂志大标题上方的一行话,每页都应不同 | "BUT" / "一个人,做了什么。" / "Phase 01 · 设计阶段" |
**反例**(已踩坑):chrome 写"设计先行 · Design First"kicker 又写"Phase 01 · 设计阶段"——意思重复,读者一眼就觉得 AI 生成的。
**正确做法**chrome 是**栏目标签**(稳定、跨页可复用),kicker 是**本页钩子**(短句、有戏剧性),两者互为补充,不互相翻译。
### ⚠️ 主题节奏规划(必读 · 生成前必做)
**核心机制**:每页 `<section>` 必须带 `light` / `dark` / `hero light` / `hero dark` 之一。JS 根据 class 推断主题,决定 body 加不加 `light-bg`,从而切换暗/亮两张 WebGL canvas 哪张在前。不带主题或写自定义名 = fallback 出错。
#### 按布局的主题默认值
| Layout | 默认主题 | 原因 |
|---|---|---|
| 1. 开场封面 | `hero dark` | 开场仪式感,暗底强冲击 |
| 2. 章节幕封 | `hero dark``hero light` **必须交替** | 呼吸节奏 |
| 3. 大字报(数据) | `light` | 数字需纸白底;多幕连发时可偶插 `dark` |
| 4. 左文右图 | **`light` / `dark` 交替** | 正文节奏主力 |
| 5. 图片网格 | `light` | 截图需亮底 |
| 6. Pipeline | `light` | 流程图需清晰 |
| 7. 问题页 | `hero dark` | 强视觉冲击默认 |
| 8. 大引用 | **`dark` 优先**,偶用 `light` | 金句仪式感靠暗底 |
| 9. 对比页 | `light` | 双列需清晰 |
| 10. 图文混排 | **`light` / `dark` 交替** | 节奏 |
#### 节奏硬规则(生成后 grep 自检)
- ❌ **禁止**连续 3 页以上相同主题(包括 light 堆叠和 dark 堆叠)
- ❌ **禁止**8 页以上的 deck 没有至少 1 个 `hero dark` + 1 个 `hero light`
- ❌ **禁止**整个 deck 只有 `light` 正文页没有任何 `dark` 正文页——会显得平淡、没呼吸
- ✅ **推荐**每 3-4 页插入 1 个 hero(封面/幕封/问题/大引用)
#### 8 页节奏模板(可直接套用)
| 页 | 主题 | 布局 | 备注 |
|---|---|---|---|
| 1 | `hero dark` | 封面 | 开场 |
| 2 | `light` | 大字报 | 数据抛出 |
| 3 | `dark` | 左文右图 | 对比/故事 |
| 4 | `light` | Pipeline | 流程 |
| 5 | `hero light` | 章节幕封 | 呼吸 |
| 6 | `dark` | 左文右图 or 大引用 | |
| 7 | `hero dark` | 问题页 | 悬念收束 |
| 8 | `light` | 大引用/结尾 | 收尾 |
**先画这张表对齐,再动手写 slide**。跳过规划直接粘骨架 = 全是 light。
---
## Layout 1: 开场封面(Hero Cover
```html
<section class="slide hero dark">
<div class="chrome">
<div>A Talk · 2026.04.22</div>
<div>Vol.01</div>
</div>
<div class="frame" style="display:grid; gap:4vh; align-content:center; min-height:80vh">
<div class="kicker">私享会 · 李继刚</div>
<h1 class="h-hero">一人公司</h1>
<h2 class="h-sub">被 AI 折叠的组织</h2>
<p class="lead" style="max-width:60vw">
一个 AI 创作者 —— 在 64 天里做了 11 万行代码、在 9 个平台上持续输出,生活节奏几乎没有被改变。
</p>
<div class="meta-row">
<span>歸藏 Guizang</span><span>·</span><span>独立创作者 / CodePilot 作者</span>
</div>
</div>
<div class="foot">
<div>一场关于 AI · 组织 · 个体的分享</div>
<div>— 2026 —</div>
</div>
</section>
```
**要点**
- 用 `hero dark` 让 WebGL 背景在大部分区域透出
- `h-hero` 是最大字号(10vw),这里作标题主视觉
- 用 `min-height:80vh + align-content:center` 让内容整体垂直居中
- 不需要 `.chrome` 里写页码,封面页自成一体
---
## Layout 2: 章节幕封(Act Divider
```html
<section class="slide hero light">
<div class="chrome">
<div>第一幕 · 硬数据</div>
<div>Act I · 01 / 25</div>
</div>
<div class="frame" style="display:grid; gap:6vh; align-content:center; min-height:80vh">
<div class="kicker">Act I</div>
<h1 class="h-hero" style="font-size:8.5vw">硬数据</h1>
<p class="lead" style="max-width:55vw">
先看数字,再谈方法。
</p>
</div>
<div class="foot">
<div>第一幕引子</div>
<div>— · —</div>
</div>
</section>
```
**要点**
- 极简,只需要 kicker + 大标题 + 一行引语
- 两个幕的封面可以交替 `hero light` / `hero dark`,制造节奏
- `h-hero` 字号可以从 10vw 调到 8.5vw 适配长短
---
## Layout 3: 数据大字报(Big Numbers Grid
```html
<section class="slide light">
<div class="chrome">
<div>过去 64 天 · 开发篇</div>
<div>Act I / Dev · 02 / 25</div>
</div>
<div class="frame" style="padding-top:6vh">
<div class="kicker">一个人,做了什么。</div>
<h2 class="h-xl">过去 64 天</h2>
<p class="lead" style="margin-bottom:5vh">从 0 到开源 CodePilot。</p>
<div class="grid-6" style="margin-top:6vh">
<div class="stat-card">
<div class="stat-label">Duration</div>
<div class="stat-nb">64 <span class="stat-unit">天</span></div>
<div class="stat-note">从 0 到现在</div>
</div>
<div class="stat-card">
<div class="stat-label">Lines of Code</div>
<div class="stat-nb">110K+</div>
<div class="stat-note">一行行写到 11 万+</div>
</div>
<div class="stat-card">
<div class="stat-label">GitHub Stars</div>
<div class="stat-nb">5,166</div>
<div class="stat-note">一个开源仓库</div>
</div>
<div class="stat-card">
<div class="stat-label">Downloads</div>
<div class="stat-nb">41K+</div>
<div class="stat-note">装到了几万台电脑里</div>
</div>
<div class="stat-card">
<div class="stat-label">AI Providers</div>
<div class="stat-nb">19</div>
<div class="stat-note">跨平台接入</div>
</div>
<div class="stat-card">
<div class="stat-label">Commits</div>
<div class="stat-nb">608+</div>
<div class="stat-note">没有协作者</div>
</div>
</div>
</div>
<div class="foot">
<div>项目 · CodePilot | github.com/codepilot</div>
<div>Act I · Dev Numbers</div>
</div>
</section>
```
**要点**
- 3×2 或 4×2 网格最稳(见 `.grid-6`
- 每个 `stat-card` 结构固定:label(英文小字)→ nb(大字数字)→ note(注释)
- 数字建议 2-3 位字符(太长会溢出),用 K / M 简写
- 留 5vh 以上的上方缓冲,让标题区先抢眼球
---
## Layout 4: 左文右图(Quote + Image
```html
<section class="slide light">
<div class="chrome">
<div>身份反差 · The Twist</div>
<div>03 / 25</div>
</div>
<div class="frame grid-2-7-5" style="padding-top:6vh">
<!-- 左列:标题 + 正文 + calloutflex column 让 callout 贴列底 -->
<div style="display:flex; flex-direction:column; justify-content:space-between; gap:3vh">
<div>
<div class="kicker">BUT</div>
<h2 class="h-xl" style="white-space:nowrap; font-size:7.2vw">
我不是程序员。
</h2>
<p class="lead" style="margin-top:3vh">
大学毕业之后再也没写过一行代码。过去十年做的是 UI 设计和 AI 特效。
</p>
</div>
<div class="callout">
"这东西在三年前,<br>
需要一个十人团队做一年。"
<div class="callout-src">— 一个观察者的判断</div>
</div>
</div>
<!-- 右列:图片用标准 16/10 比例 + max-height,不要 align-self:end -->
<figure class="frame-img" style="aspect-ratio:16/10; max-height:56vh">
<img src="images/codepilot.png" alt="CodePilot 产品截图">
<figcaption class="img-cap">CodePilot · 产品截图</figcaption>
</figure>
</div>
<div class="foot">
<div>Page 03 · 我不是程序员</div>
<div>— · —</div>
</div>
</section>
```
**要点**
- 用 `grid-2-7-5`(左 7 份、右 5 份),`align-items:start` 已在 template 预设
- **左列**用 flex column + `justify-content:space-between`:标题贴顶,callout 自然贴底
- **右列图片** **不要加 `align-self:end`**。会让图片滑到 cell 底部,低分屏下被浏览器工具栏遮挡
- 图片必须用 **标准比例 16/10 或 4/3 + `max-height:56vh`**,不要用原图奇葩比例(`2592/1798` 这种)
---
## Layout 5: 图片网格(多图对比)
```html
<section class="slide light">
<div class="chrome">
<div>平台粉丝实证</div>
<div>Act I / Ops · 05 / 27</div>
</div>
<div class="frame" style="padding-top:5vh">
<div class="kicker">Proof · 粉丝实证</div>
<h2 class="h-xl">10 个平台 · 6 张截图</h2>
<div class="grid-3-3" style="margin-top:4vh">
<figure class="frame-img" style="height:26vh">
<img src="images/weibo.png" alt="微博 289K">
<figcaption class="img-cap">微博 · 289K</figcaption>
</figure>
<figure class="frame-img" style="height:26vh">
<img src="images/twitter.png" alt="推特 137K">
<figcaption class="img-cap">推特 · 137K</figcaption>
</figure>
<figure class="frame-img" style="height:26vh">
<img src="images/wechat.png" alt="公众号 96K">
<figcaption class="img-cap">公众号 · 96K</figcaption>
</figure>
<figure class="frame-img" style="height:26vh">
<img src="images/jike.png" alt="即刻 26K">
<figcaption class="img-cap">即刻 · 26K</figcaption>
</figure>
<figure class="frame-img" style="height:26vh">
<img src="images/xhs.png" alt="小红书 19K">
<figcaption class="img-cap">小红书 · 19K</figcaption>
</figure>
<figure class="frame-img" style="height:26vh">
<img src="images/douyin.png" alt="抖音 10K">
<figcaption class="img-cap">抖音 · 10K</figcaption>
</figure>
</div>
</div>
<div class="foot">
<div>截图时间 · 2026.04</div>
<div>Page 05 · 粉丝实证</div>
</div>
</section>
```
**要点**
- 关键:每个 `frame-img` 必须写死 `height:NNvh`(不要用 `aspect-ratio`),否则网格会撑破
- 图片会自动 `object-fit:cover + object-position:top`,只裁底部
- 用 `.grid-3-3`3×2)或 `.grid-3`3×1)承载
---
## Layout 6: 两列流水线(Pipeline
```html
<section class="slide light">
<div class="chrome">
<div>我的工作流 · Workflow</div>
<div>Act II · 15 / 27</div>
</div>
<div class="frame">
<div class="kicker">Pipeline · 流水线</div>
<h2 class="h-xl">两条流水线</h2>
<!-- 第一组:文本侧 -->
<div class="pipeline-section">
<div class="pipeline-label">文本侧 · Text Pipeline</div>
<div class="pipeline">
<div class="step">
<div class="step-nb">01</div>
<div class="step-title">Draft</div>
<div class="step-desc">AI 帮我起草初稿</div>
</div>
<div class="step">
<div class="step-nb">02</div>
<div class="step-title">Polish</div>
<div class="step-desc">AI 润色去 AI 味</div>
</div>
<div class="step">
<div class="step-nb">03</div>
<div class="step-title">Morph</div>
<div class="step-desc">AI 变形成推特 / 小红书</div>
</div>
<div class="step">
<div class="step-nb">04</div>
<div class="step-title">Illustrate</div>
<div class="step-desc">AI 生成信息图</div>
</div>
<div class="step">
<div class="step-nb">05</div>
<div class="step-title">Distribute</div>
<div class="step-desc">一键分发 9 平台</div>
</div>
</div>
</div>
<!-- 第二组:视频侧 -->
<div class="pipeline-section">
<div class="pipeline-label">视觉 · 视频侧 · Video Pipeline</div>
<div class="pipeline">
<div class="step">
<div class="step-nb">06</div>
<div class="step-title">Cut</div>
<div class="step-desc">AI 帮我剪辑</div>
</div>
<div class="step">
<div class="step-nb">07</div>
<div class="step-title">Wrap</div>
<div class="step-desc">AI 帮我包装</div>
</div>
<div class="step">
<div class="step-nb">08</div>
<div class="step-title">Cover</div>
<div class="step-desc">AI 生成封面</div>
</div>
</div>
</div>
</div>
<div class="foot">
<div>Page 15 · 我的内容工厂</div>
<div>Workflow</div>
</div>
</section>
```
**要点**
- 用 `.pipeline-section` 分组 + `.pipeline-label` 作组标题
- 两组之间用 3.6vh 的间距 + 顶部细分隔线(已在 CSS 中预设)
- 每个 step 是固定的 nb → title → desc 结构
- 步骤数不限但单行最好 ≤5 个,否则换到第二 pipeline
---
## Layout 7: 悬念收束 / 问题页(Hero Question
```html
<section class="slide hero dark">
<div class="chrome">
<div>留给你的问题</div>
<div>24 / 27</div>
</div>
<div class="frame" style="display:grid; gap:8vh; align-content:center; min-height:80vh">
<div class="kicker">The Question</div>
<h1 class="h-hero" style="font-size:7vw; line-height:1.15">
你的公司里,<br>
哪些岗位本来就<br>
不该由人来做?
</h1>
<p class="lead" style="max-width:50vw">
这个问题,不是技术问题,是架构问题。
</p>
</div>
<div class="foot">
<div>Page 24 · The Question</div>
<div>— · —</div>
</div>
</section>
```
**要点**
- Hero 页留白越多越好,只放一个问题
- `h-hero` 字号视长度调整(7vw 适合 3 行,10vw 适合 1 行)
- 用 `<br>` 手工断行,确保断点在语义处
- 尾巴可以再给一行 `lead` 作为点破
---
## Layout 8: 大引用页(Big Quote · 衬线金句)
```html
<section class="slide light">
<div class="chrome">
<div>The Takeaway · 核心金句</div>
<div>18 / 25</div>
</div>
<div class="frame" style="display:grid; gap:5vh; align-content:center; min-height:80vh">
<div class="kicker">Quote · 金句</div>
<blockquote style="font-family:var(--serif-zh); font-weight:700; font-size:5.8vw; line-height:1.2; letter-spacing:-.01em; max-width:72vw">
"没有交接,<br>所有人都在构建。"
</blockquote>
<p class="lead" style="max-width:55vw; opacity:.65">
Without the handoff, everyone builds.<br>
And that makes all the difference.
</p>
<div class="meta-row">
<span>— Luke Wroblewski</span><span>·</span><span>2026.04.16</span>
</div>
</div>
<div class="foot">
<div>Page 18 · 金句</div>
<div>— · —</div>
</div>
</section>
```
**要点**
- 整页留白,只放一个大引用 + 出处
- `<blockquote>` 用 inline style 单独放大(5-6vw,不要用 `h-hero`(那是页面主标题的命名)
- 下面跟随英文原文(lead · opacity:.65)制造层级
- 配 `meta-row` 写出处 · 日期
---
## Layout 9: 并列对比(A vs B · 旧 vs 新)
```html
<section class="slide light">
<div class="chrome">
<div>旧 vs 新 · The Shift</div>
<div>12 / 25</div>
</div>
<div class="frame" style="padding-top:5vh">
<div class="kicker">Before / After · 范式转变</div>
<h2 class="h-xl" style="margin-bottom:4vh">从交接到共建</h2>
<div class="grid-2-6-6" style="gap:5vw 4vh">
<!-- 左列:旧 -->
<div style="padding:3vh 2vw; border-left:3px solid currentColor; opacity:.55">
<div class="kicker" style="opacity:.9">Before · 旧模式</div>
<h3 class="h-md" style="margin-top:2vh">设计 → 开发 → 交接</h3>
<ul style="margin-top:3vh; padding-left:1.2em; display:flex; flex-direction:column; gap:1.4vh; font-family:var(--sans-zh); font-size:max(14px,1.1vw); line-height:1.55">
<li>设计师在 Figma 做稿</li>
<li>开发者盯着文件翻译像素</li>
<li>反复 PR 沟通对齐</li>
<li>非技术人员无法触碰代码</li>
</ul>
</div>
<!-- 右列:新 -->
<div style="padding:3vh 2vw; border-left:3px solid currentColor">
<div class="kicker" style="opacity:.9">After · 新模式</div>
<h3 class="h-md" style="margin-top:2vh">同工具 · 并行 · 共建</h3>
<ul style="margin-top:3vh; padding-left:1.2em; display:flex; flex-direction:column; gap:1.4vh; font-family:var(--sans-zh); font-size:max(14px,1.1vw); line-height:1.55">
<li>三个角色同时在 Intent 工作</li>
<li>agents.md 作为共享上下文</li>
<li>代理处理对齐 / 冲突 / 动画</li>
<li>任何人都能安全贡献代码</li>
</ul>
</div>
</div>
</div>
<div class="foot">
<div>Page 12 · 范式转变</div>
<div>Before / After</div>
</div>
</section>
```
**要点**
- 用 `.grid-2-6-6`1:1)左右分半
- 左列 `opacity:.55` 做"旧"的视觉弱化,右列满亮度做"新"的突出
- 两列都用 `border-left:3px solid` + `padding-left` 做引用块感
- 每列结构统一:`kicker``h-md``<ul>` 要点,节奏一致
---
## Layout 10: 图文混排(Lead Image + Side Text
```html
<section class="slide light">
<div class="chrome">
<div>Design First · 设计先行</div>
<div>08 / 16</div>
</div>
<div class="frame grid-2-8-4" style="padding-top:6vh">
<!-- 左列:大段正文 + 引用 -->
<div>
<div class="kicker">Phase 01 · 设计阶段</div>
<h2 class="h-xl" style="margin-top:1vh; margin-bottom:3vh">设计先行 · 2 周</h2>
<p class="lead" style="margin-bottom:3vh">
在 Figma 中完成视觉探索与设计系统,网格 / 排版 / 颜色变量 / 可复用组件,桌面和移动端稿件几轮反馈迭代。
</p>
<p style="font-family:var(--sans-zh); font-size:max(14px,1.15vw); line-height:1.75; opacity:.78; margin-bottom:2.4vh">
两周之内,视觉风格、粗略结构、方向性内容全部稳定。这是扎实的传统设计流程——在这里还没什么新鲜事。
</p>
<div class="callout" style="margin-top:3vh">
"This phase was pretty standard.<br>Just a solid Web design process."
<div class="callout-src">— Luke Wroblewski</div>
</div>
</div>
<!-- 右列:辅助图 · 竖版或方形 -->
<figure class="frame-img" style="aspect-ratio:3/4; max-height:60vh">
<img src="images/figma.png" alt="Figma design system">
<figcaption class="img-cap">Figma · Design System</figcaption>
</figure>
</div>
<div class="foot">
<div>Page 08 · Design First</div>
<div>约 2 周</div>
</div>
</section>
```
**要点**
- `.grid-2-8-4`(8:4) 让正文占主导,图片作辅助
- 左列包含多种信息层级:kicker → 大标题 → lead → 正文段落 → callout(引用)
- 右列图片用 **竖版 3:4** 或方形 1:1,避免和左列文本竞争注意力
- 这种布局适合**页面信息量偏大**的场景(不像 Layout 4 只有一句金句)
---
## 附录:常用网格模板
| 类名 | 配比 | 用途 |
|---|---|---|
| `.grid-2-6-6` | 6:61:1 | 对半分 |
| `.grid-2-7-5` | 7:5 | 文字为主 + 辅助图 |
| `.grid-2-8-4` | 8:4(2:1 | 大段文字 + 小图/数据 |
| `.grid-3` | 1:1:1 | 3 项并列(案例/截图) |
| `.grid-3-3` | 3×2 | 6 图矩阵 |
| `.grid-6` | 3×2 | 6 个数据卡片 |
所有网格都预留 `gap: 3vw 4vh`(水平 3vw、竖直 4vh),可以单独覆写。
---
## 页面节奏建议
一场 25-30 页的分享,推荐以下节奏:
1. **Hero Cover**(第 1 页)
2. **Act Divider**(第一幕开场,hero light 或 hero dark
3. **Big Numbers**(抛硬数据制造冲击)
4. **Quote + Image**(讲身份反差/挂钩)
5. **Image Grid**(证据支撑)
6. **Hero Question**(幕收束,留悬念)
7. ... 第二幕、第三幕同样节奏 ...
8. **Hero Close**(最后一页,问题或致谢)
hero 页与 non-hero 页应该 **2-3 : 1 比例交错**,不要连续超过 3 页 non-hero,也不要连续超过 2 页 hero。
+195
View File
@@ -0,0 +1,195 @@
# 杂志风方向(Magazine Directions
5 个**预设方向**,每个方向都把"用哪套主题色 / 哪些 layout / 多少 slide / 怎么写 chrome 文案"打包好,避免你在 6 问澄清里给出 5 个不相关的选项。
> 灵感来源:[alchaincyf/huashu-design](https://github.com/alchaincyf/huashu-design) 的 "20 design philosophies × 5 streams" — 我们把它压缩到 5 个 magazine-flavored 的方向,每个都对应到 `themes.md` 的某一套 + `layouts.md` 的某些组合。
---
## 何时用这份文档
在 SKILL.md `Step 1 · 需求澄清` 的开头:**先让用户在这 5 个方向里挑一个**,再去问主题色 / 时长 / 受众 / 大纲。流程是:
```
1. 用户讲一句"想做个分享 PPT"
2. 你(agent)介绍 5 个方向(拷贝下面的 1-line summary
3. 用户挑一个方向(或说"不知道, 你推荐"
4. 你按所选方向回答了"主题色"和"slide 数量"两个问题, 再问剩下的 4 个
```
**硬规则**:方向只能从下面 5 个里选,不能混搭。混搭 = 走 huashu-design 验证过的失败路径(品牌资产协议 v1)。如果用户对 5 个都不满意,委婉劝他选最接近的,然后允许在 `chrome` / `kicker` 里轻微定制语气,**绝不调色**。
---
## 1. Monocle Editorial · 国际杂志风 ✦ 默认推荐
**关键词**:克制、知识感、跨国、有 *taste*
| 配方 | 选择 |
|---|---|
| 主题色 | 🖋 墨水经典 |
| 推荐 slide 数 | 1824 页(60% non-hero / 40% hero) |
| 主力 layouts | **1 封面 / 2 章节幕 / 4 左文右图 / 8 大引用 / 10 图文混排** |
| Chrome 文案 | `Vol.04 · Spring 2026` / `Act II · 12 / 24` / `lukew.com · 2026.04` |
| Kicker 风格 | 短英文 + 中点:`THE TWIST` / `BUT` / `DEC.` |
| Foot 文案 | `Page 12 · 一种新的工作方式` |
**适合**:商业发布、行业内部讲话、产品宣发、个人品牌沉淀分享。**默认就选这个**,跑不出大错。
**反例**:技术深度报告(密度太低),表格数据很多的 ops 复盘(没有合适的 layout)。
**视觉锚点***Monocle* / *Apricot Magazine* / *A Book Apart* / *Apartamento*
---
## 2. WIRED Tech · 数据 + 工程
**关键词**:硬数据、流水线、对比、未来感
| 配方 | 选择 |
|---|---|
| 主题色 | 🌊 靛蓝瓷 |
| 推荐 slide 数 | 1418 页(轻巧、数据密) |
| 主力 layouts | **1 封面 / 3 数据大字报 / 6 Pipeline / 7 问题页 / 9 Before/After** |
| Chrome 文案 | `Q2 / 2026 · Field Report` / `Data · 03` / `Eng Notes` |
| Kicker 风格 | 全大写 + 数字:`38× FASTER` / `RUNTIME 04` / `CASE 02` |
| Foot 文案 | `Page 03 · benchmark` / `methodology footnote` |
**适合**:技术发布会、研究分享、benchmark 报告、工程团队对内沟通、AI 产品 demo day。
**反例**:人文类金句分享(太冷)、艺术品牌(不够温度)。
**视觉锚点***WIRED* 长文版 / *MIT Technology Review* / *The Pudding* / *Stripe Press*
**特殊建议**:每个 stat-card 的 `stat-label` 用英文等宽(这是 WIRED 风的核心),数字别加千分位逗号(不够工程),用 `K` / `M` / `×` 简写。
---
## 3. Kinfolk Slow · 慢生活 / 人文
**关键词**:留白、衬线、温度、私享会
| 配方 | 选择 |
|---|---|
| 主题色 | 🍂 牛皮纸 |
| 推荐 slide 数 | 9–12 页(慢、放空、低密度) |
| 主力 layouts | **1 封面 / 4 左文右图 / 8 大引用 / 10 图文混排 / 2 章节幕** |
| Chrome 文案 | `Vol.07 · Autumn` / `一封信 · 03` / `Notes from Kyoto` |
| Kicker 风格 | 中文短语 + 标点:"给一个朋友。" / "晚秋。" / "Letter Three" |
| Foot 文案 | `Page 03 · Letter Three` / `2026 · Spring Issue` |
**适合**:私享会、读书分享、人物访谈复盘、生活方式品牌、个人随笔。
**反例**:产品发布(太慢)、技术分享(太软)、严肃数据(信息密度不够)。
**视觉锚点***Kinfolk* / *The Gentlewoman* / *Cereal* / *Drift Magazine*
**特殊建议**
- **故意把 slide 数压到 10 页以下**——Kinfolk 的核心是"少即是多",不要塞满
- 大量使用 Layout 8(大引用)和 Layout 10(图文混排)
- 不要用 Layout 3(数据大字报)——和气质冲突
- `<title>` 文字、章节名、kicker 全部用衬线 + 中文短句
---
## 4. Domus Architectural · 建筑 / 空间感
**关键词**:尺度、几何、不对称、克制的炫耀
| 配方 | 选择 |
|---|---|
| 主题色 | 🌙 沙丘 |
| 推荐 slide 数 | 1218 页(中密度,强视觉) |
| 主力 layouts | **1 封面 / 2 章节幕 / 5 图片网格 / 9 Before/After / 10 图文混排** |
| Chrome 文案 | `Spazio 09 · Project File` / `Plan · 03` / `Fig.4` |
| Kicker 风格 | 数字 + 类别:`PROJECT 04` / `SECTION B` / `FIGURE 12` |
| Foot 文案 | `Page 09 · West Wing` / `1:200 scale` |
**适合**:设计 / 建筑案例分享、产品设计 review、品牌视觉发布、画廊式 portfolio 展示。
**反例**:金句分享(太硬)、技术 deep dive(不擅长流水线)。
**视觉锚点***Domus* / *Apartamento* / *Mark Magazine* / *Pin-Up*
**特殊建议**
- **每个 hero 页都要"留 60% 空"** — 不要塞满,建筑感来自呼吸
- 大量使用 Layout 5(图片网格)但**只放 4 张大图**,不要放 6 张小图
- `chrome` 文案保持冷峻,全用英文 + 数字
---
## 5. Lab / Reference · 学术 + 工艺手册
**关键词**:克制、有图有表、可复现、工程师爱看
| 配方 | 选择 |
|---|---|
| 主题色 | 🌿 森林墨 |
| 推荐 slide 数 | 1624 页(密度高、有图表) |
| 主力 layouts | **1 封面 / 2 章节幕 / 3 数据大字报 / 6 Pipeline / 9 Before/After** |
| Chrome 文案 | `Field Notes · Vol.II` / `Section 3.2 · Method` / `Reference 04` |
| Kicker 风格 | 编号:`§ 3.2` / `Ref. 04` / `Method 01` |
| Foot 文案 | `Page 12 · 3.2 Calibration` / `appendix A` |
**适合**:学术分享、内部研究复盘、可持续 / 自然主题、长期产品复盘、有方法论的工艺型分享(咖啡 / 香水 / 茶)。
**反例**:商业发布(太冷静)、营销活动(不够 catchy)。
**视觉锚点***National Geographic*(旧版)/ *Hand-Eye Magazine* / *Nautilus* / *MIT Press* book layouts。
**特殊建议**
- 大量 `meta-row` 标注来源、方法、引用
- 比其他方向**更频繁地用 `<figcaption class="img-cap">`** 给每张图标编号
- `kicker` 用 § 章节编号,不用感叹句
---
## 推荐速查(如果用户描述了一个意图,你应该选哪个)
| 用户说的话 | 推荐方向 |
|---|---|
| "通用分享" / "不知道选啥" | **1. Monocle** |
| "一人公司 / AI 折叠 / 创业 demo day" | **1. Monocle**(默认)或 **2. WIRED**(如果偏技术) |
| "AI / benchmark / 模型评测" | **2. WIRED** |
| "产品发布会 / 工程团队分享" | **2. WIRED** |
| "读书分享 / 人物访谈 / 一个人的故事" | **3. Kinfolk** |
| "私享会 / 朋友间分享 / 周末闲聊式" | **3. Kinfolk** |
| "设计案例 / 品牌发布 / portfolio 展示" | **4. Domus** |
| "建筑 / 空间 / 装置" | **4. Domus** |
| "学术 / 研究 / 方法论 / 教程" | **5. Lab** |
| "可持续 / 环保 / 自然主题" | **5. Lab** |
---
## 决策记录(生成前必做)
挑完方向后,**在项目目录下生成或更新 `项目记录.md`**(或 `大纲-v1.md`),第一行写清:
```markdown
# [演讲标题] · 项目记录
- 方向(Direction):**Monocle Editorial** from `references/styles.md`
- 主题色(Theme):🖋 墨水经典
- 受众:内部团队(产品 + 设计)
- 时长:25 min · 约 18 slides
- Chrome 风格:Vol.04 / Act II / 12 of 18
- Kicker 风格:短英文 + 中点
```
后续迭代每次调整方向都更新这一节。**不要中途换方向**——5 个方向之间的"语气"差异比想象的大,混着写就会撕裂。
---
## ❌ 不要做的事
- ❌ 把 5 个方向的 layout 选择混着用(例如 Monocle 配 Layout 6 Pipeline 多页 + Kinfolk 风的 chrome)—— 杂乱
- ❌ 自己造第 6 个方向("我想做'科技 + 文艺'风")—— 委婉劝他选最近的,告诉他混搭历史失败率超高
- ❌ 中途换方向,例如做到第 8 页突然觉得"换 Kinfolk 更好"——前 7 页就废了,要么全推倒重来,要么坚持原方向到底
- ❌ 在不属于该方向的 layout 上花时间(例如 Kinfolk 写 4 页 Layout 6 Pipeline)—— 信号是用错方向了
## ✅ 应当做的事
- ✅ 只在 5 个方向里挑,挑完用方向去回答其他 5 个澄清问题
- ✅ 在 `项目记录.md` 第一行明确方向,全程不变
- ✅ 让 chrome / kicker / foot 三个文字位为方向"代言"——它们承担了一半的方向辨识度
- ✅ 如果不确定,**默认选 Monocle Editorial**——它是 5 个方向里失败概率最低的兜底
+122
View File
@@ -0,0 +1,122 @@
# 主题色预设(Themes
5 套精心调配的主题色板,保证"电子杂志 × 电子墨水"的美学不垮。**不允许用户自定义颜色——色彩搭配错了画面瞬间变丑**,只从以下预设中挑选。
---
## 使用方法
1. 问用户选哪套(或基于内容推荐一套)
2. 打开 `assets/template.html``<style>`
3. 找到开头的 `:root{`
4. **整体替换**标有"主题色"注释的那几行 `--ink` / `--ink-rgb` / `--paper` / `--paper-rgb` / `--paper-tint` / `--ink-tint`
5. 其他 CSS 都走 `var(--...)`,无需任何其他改动
---
## 🖋 墨水经典 (Monocle 默认)
**适合**:通用分享、商业发布、科技产品、任何场景都安全的默认选择。
**调性**:纯墨黑 + 暖米白,杂志感最强,Monocle / Apricot / A Book Apart 风。
```css
--ink:#0a0a0b;
--ink-rgb:10,10,11;
--paper:#f1efea;
--paper-rgb:241,239,234;
--paper-tint:#e8e5de;
--ink-tint:#18181a;
```
---
## 🌊 靛蓝瓷 (Indigo Porcelain)
**适合**:科技/研究/数据分享、工程师文化、深度内容、技术发布会。
**调性**:深靛蓝 + 瓷白,冷静、理性、有深度,像学术期刊或蓝印花瓷器。
```css
--ink:#0a1f3d;
--ink-rgb:10,31,61;
--paper:#f1f3f5;
--paper-rgb:241,243,245;
--paper-tint:#e4e8ec;
--ink-tint:#152a4a;
```
---
## 🌿 森林墨 (Forest Ink)
**适合**:自然/可持续/文化/非虚构内容、户外品牌、环保主题。
**调性**:深森林绿 + 象牙,沉稳、有呼吸感,像旧版《国家地理》。
```css
--ink:#1a2e1f;
--ink-rgb:26,46,31;
--paper:#f5f1e8;
--paper-rgb:245,241,232;
--paper-tint:#ece7da;
--ink-tint:#253d2c;
```
---
## 🍂 牛皮纸 (Kraft Paper)
**适合**:怀旧/人文/阅读/历史/文学分享、独立杂志、手作品牌。
**调性**:深棕 + 暖米,像牛皮信封或老笔记本,温暖、有年代感。
```css
--ink:#2a1e13;
--ink-rgb:42,30,19;
--paper:#eedfc7;
--paper-rgb:238,223,199;
--paper-tint:#e0d0b6;
--ink-tint:#3a2a1d;
```
---
## 🌙 沙丘 (Dune)
**适合**:艺术/设计/创意/时尚分享、画廊手册、审美优先的私享会。
**调性**:炭灰 + 沙色,克制、高级、中性,像沙漠黄昏或建筑设计图册。
```css
--ink:#1f1a14;
--ink-rgb:31,26,20;
--paper:#f0e6d2;
--paper-rgb:240,230,210;
--paper-tint:#e3d7bf;
--ink-tint:#2d2620;
```
---
## 推荐选择参考
| 如果是... | 推荐主题 |
|---|---|
| 不知道选啥 / 第一次用 | 🖋 墨水经典 |
| AI / 技术 / 产品发布 | 🌊 靛蓝瓷 |
| 内容 / 行业观察 / 文化 | 🌿 森林墨 |
| 书评 / 生活方式 / 人文 | 🍂 牛皮纸 |
| 设计 / 艺术 / 品牌 | 🌙 沙丘 |
---
## 切换原则
- **一份 deck 只用一套主题**,不要中途换色
- WebGL shader 的默认主色(钛金色散 / 银色流动)适配所有 5 套(经测试可接受)
- `currentColor` 驱动的 border / icon 会跟随 section 的 text color 自动适配,无需额外调整
- 选定主题后,`<title>` 文字和 `chrome` 文案可以强化该主题的语义(例如牛皮纸配"Vol.03 · 秋"这种)
## ❌ 不要做的事
- ❌ **不允许混搭**(例如 ink 取墨水经典的,paper 取沙丘的)——会彻底违和
- ❌ **不允许用户随便给一个 hex 值**——需委婉拒绝并展示 5 套预设让选
- ❌ **不要直接修改 template.html 其他地方的颜色**——所有散落 rgba 都走 var,改 :root 一处即可
选定主题后在 skill 对话中告诉用户:"用 🖋 墨水经典 / 🌊 靛蓝瓷 ..."并在 deck 项目记录里备注,方便后续迭代时保持一致。