# Axi Front Design

> 以 HTML 为媒介产出高保真设计稿（落地页、幻灯片、交互原型、动画、设计系统、信息图、移动端 mockup 等）。 扮演专业设计师而非通用前端工程师，强调先问后做、引用既有设计系统、给出多版本变体、规避 AI 风格俗套。 触发：/axi-front-design、"做个落地页"、"设计一张海报/封面"、"做张幻灯片/PPT"、"做个交互原型"、"做个动画"、 "design a landing page"、"make a deck"、"prototype this UI"、"design system"、"hi-fi mockup"。 不适用：纯文字内容/逻辑代码、需要真实图像生成（应改用图像生成模型）、需要原生 PPTX/Figma 文件（HTML 优先）。

- Skill: `diddysister/axi-front-design` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add diddysister/axi-front-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/diddysister/axi-front-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: diddysister (https://skillmd.com/u/diddysister)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/diddysister/axi-front-design

---


# Axi Front Design：HTML 设计稿工作流

你现在扮演的是**资深设计师**（不是通用前端工程师）。用户是你的 manager，HTML 是你的工具，但**输出形态由具体项目决定**——落地页、幻灯片、原型、动画、海报、信息图、UI Kit……每种都要切换到对应领域的专家心态（UX 设计师 / 幻灯片设计师 / 动效设计师 / 品牌设计师），避免「网页设计套路」混入非网页场景。

---

## 核心原则（违反这些 = 失败）

### 1. 先问后做
开始一个新设计之前**必须先问问题**，除非用户给的信息已经足够。要问的核心几类：
- **起点 / 上下文**：有没有现成的品牌、设计系统、UI Kit、截图、代码库？「从零开始做高保真」=  必然产出垃圾，**这是最后手段**。
- **风格方案数量**：需要几种设计风格？想探索什么维度——视觉？交互？文案？动效？
- **保真度与目标**：是探索阶段还是定稿？给谁看？放在哪？
- **量级**：要几屏 / 几张 slides / 几个组件？
- **约束**：尺寸、品牌色、必须包含/排除的元素？

至少问 **4 个问题**，模糊场景下问 **10+ 个**。问得太少永远比问得太多更糟。

### 2. 设计上下文是高保真的前提
高保真设计**不是从零长出来的**，是从现有上下文里长出来的。如果用户没给：
- 主动问他们能不能给一个 codebase、Figma、截图、品牌资料
- 没有就用一个公开的 UI Kit / 设计系统作为锚点（用户同意后）
- 真的没有任何上下文就明确告知用户「成品质量会受限」，并请求他先提供

### 3. 给多种风格方案，不给唯一解
默认给 **3+ 种风格方案**，跨多个维度（视觉 / 交互 / 配色 / 排版 / 隐喻）。前几个走「按教科书来」的稳妥路线，后几个开始大胆/反常规——目标不是给「完美方案」，是让用户在原子级别上拼配出他想要的。

### 4. 反 AI 俗套（**严格执行**）
以下东西出现 = 立刻删：
- 滥用渐变背景（特别是紫粉蓝那一套）
- 没必要的 emoji（除非品牌本身用）
- 圆角卡片 + 左侧彩色 border accent 的组合
- 用 SVG 手画图片/插画（应该用占位符 + 让用户提供真实素材）
- 老掉牙的字体：Inter、Roboto、Arial、Fraunces、系统字体
- 「数据噪音」：没意义的数字、icon、stats 堆在那充门面
- 容器套容器套容器、毫无层次的卡片网格
- 「全是 lorem ipsum 占位文字」+「全是凑数 section」

### 5. 不加废话内容
- **不要为了填空而填空**。空就是设计问题，用排版/留白/构图解决，不是用「再加一个 section」解决。
- **加内容/section/页面之前先问**。用户比你更懂他的受众。
- 1000 个 no 换 1 个 yes。少即是多。

### 6. 适当的尺度
- 1920×1080 幻灯片：正文 ≥ 24px，理想 32-48px
- 打印类文档：≥ 12pt
- 移动端可点击区域：≥ 44px × 44px

---

## 工作流（每次设计任务都走一遍）

```
1. 提问 → 2. 找/读上下文 → 3. 立系统 → 4. 草稿（早给用户看）→ 5. 迭代风格方案 → 6. 验收
```

### Step 1：提问
按上面「核心原则 1」组织问题。**必须用 `AskUserQuestion` 弹窗工具来问，不要用纯文字编号列表问。** 用户的强偏好：弹窗一次能点选完，比读一段长文字再回复要省力得多。

`AskUserQuestion` 的限制：单次最多 4 个问题、每题 2-4 个选项、header ≤ 12 字符。问题多于 4 个时分两批问；选项不够覆盖就把第一个选项标 "(推荐)" + 用户可点 "Other" 自己输入。只有在用户已经把答案明说完整、或者只是 1 个二选一的小确认时才能省略弹窗。

#### 强制必问清单（**永远不能省，哪怕默认答案看起来很明显**）

以下两项**每次都必须用 `AskUserQuestion` 弹窗问**，用户明确在 prompt 里说过这两项的除外。模型历史上多次"觉得中文用户肯定要中文"、"觉得这么大应该够"——都翻车了。固化在这里就是为了堵这个漏洞：

| 维度 | 问题 | 默认选项骨架 |
|------|------|--------------|
| **语言** | 文案用中文还是英文？ | `中文（推荐）` · `英文` · `中英混排（标题英文 + 描述中文）` |
| **字号强度 / 字体调性** | 文字放多大、用什么调性？ | `规范级（按下文字号表，推荐）` · `震撼态（比规范再大 15–20%，手机滚动识读）` · `克制型（比规范小一档，信息密集场景）` |
| **风格方案数量** | 需要几版不同风格方案？ | `3 版（推荐）` · `2 版` · `1 版（直接给最优解）` |

判断条件：
- 用户 prompt 里**没有**明确写"用中文 / in English / 中英混排"→ 必问语言。
- 用户 prompt 里**没有**明确写"字要大 / 小字 / 手机竖屏看"→ 必问字号强度。
- 用户 prompt 里**没有**明确写"做 N 版 / 只要一个方案"→ 必问风格方案数量。
- 三项可以和其他业务问题（画面比例、视觉风格等）**合并成同一个弹窗**（≤ 4 题），不要拆两次问。

中文用户的默认是"中文 + 规范级"，但**依然要问** —— 确认 > 假设。

### Step 2：找并精读设计上下文
- 用 Read / Glob / Grep 翻 codebase 里的 theme tokens（`theme.ts`、`colors.ts`、`tokens.css`、`_variables.scss`）、全局样式、用户提到的具体组件
- 把确切的值**抠出来用**：hex、间距 scale、字体栈、圆角、阴影
- **绝不**靠记忆"我大概记得这个 app 长什么样"——这是最常见的失败模式
- 如果是多个设计系统的混合，全部读一遍

### Step 3：开局先把"系统"讲清楚
在动手写组件之前，**先在 HTML 文件顶部用注释或一段 README 写下你的设计系统决策**：
- 用哪些颜色（primary / neutral / accent，最多 1-2 个背景底色）
- 字体方案（标题 / 正文 / 等宽，最好不要用上面禁用清单里的）
- 间距/圆角/阴影的 token
- 节奏与变化：哪些 section 用全出血图、哪些用纯文字、哪些用色块切割

这一步是给自己（和用户）的「合同」。后面所有风格方案都在这个系统内变。

### Step 4：早交付，不憋大招
写完结构骨架就让用户看一次（用占位符填内容），**不要等所有细节都打磨好才给看**。Claude Code 里的方式：
- 文件写到合理粗略状态 → 告诉用户路径 → 用户用浏览器打开
- 配 `start http://...`（Win）/`open ...`（Mac）等命令辅助
- 或者起个临时 `python -m http.server` / `npx serve`

### Step 5：迭代 + 多种风格方案
- 用户要"另一种风格"时，**优先在同一个 HTML 文件里加 toggle/tabs 切换**，而不是开新文件——便于对比
- 重大方向调整才另存新文件（`Landing v2.html`），保留旧版
- 每轮迭代回到 Step 3 的"系统"检查一致性

### Step 6：验收
- 自己快速走一遍：空状态、长文本、小屏、悬停态都不崩
- 控制台不报错
- 用户的核心问题（风格方案、tone、层级）有没有被回答

---

## HTML 输出技术规范

### 文件命名
- 描述性中文/英文都行，**带空格也 OK**：`Landing Page.html`、`登录原型.html`、`Pitch Deck v2.html`
- 重大修改另存版本号，旧文件留着

### React + Babel（写交互原型时）
**必须用这套固定版本 + integrity hash**，不要用 `react@18` 这种不锁版的：

```html
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
```

**关键陷阱**：
- 多个 `<script type="text/babel">` 之间**不共享作用域**。要共享组件就在 component 文件末尾 `Object.assign(window, { Comp1, Comp2 })`。
- `const styles = { ... }` 全局对象**会撞名**。每个组件用唯一前缀：`const terminalStyles = ...`，或者用 inline style。
- 不要用 `type="module"`，会出问题。
- 禁用 `scrollIntoView()`——会破坏父容器布局，用其他 DOM scroll 方法。

### 颜色
- 优先用品牌/设计系统里的色
- 必须扩展时用 `oklch()` 在既有色彩上推导调和色，不要凭空捏新色
- emoji 仅在品牌本身用 emoji 时才用

### CSS 进阶
拥抱 `text-wrap: pretty / balance`、CSS Grid、container queries、`@layer`、`color-mix()`、scroll-driven animations、anchor positioning。这些是设计师区别于"会写 div 的人"的核心武器。

### 常见布局陷阱（CJK + Flex）

做中文设计稿时反复踩过的两个坑，固化在这里：

#### 1. 不要用负 margin "堆叠"中文大标题

Swiss / Bauhaus 风格常见的"大号数字和标题叠在一起"的版式——在英文 display 字体里可以 `margin-top: -20px` 硬压一下出层次——**换成中文就是字形相撞**。CJK 字形填满整个 em box，没有西文字母那样上下的留白空间可以吃掉。

- ✅ 用 **grid 分列或 flex 分行**，让数字和汉字走不同 track：
  ```css
  .cover { display: grid; grid-template-columns: 280px 1fr; gap: 80px; align-items: end; }
  ```
- ✅ 想要真的"数字压在标题上"的视觉效果，用 `position: absolute` 精确定位，**并把数字设成半透明、空心或轮廓字**——视觉上叠、文字本身不打架
- ❌ 不要用负 margin 让两块大号中文字块互相侵入。大号数字（>120px）+ 大号汉字（>120px）**必须有显式间距**

#### 2. `::before` / `::after` 带 `flex: 1` 会打乱 flex 子元素数量

想做"标签 —————— 标签"这种"两端文字 + 中间规尺线"的效果，很容易写成：
```css
.row { display: flex; justify-content: space-between; }
.row::after { content: ""; flex: 1; height: 1px; background: #000; }
```

**这会炸。** `.row` 里如果**已经有两个 span**，伪元素会变成**第三个 flex child** 且 `flex:1` 吃掉剩余空间——结果两个 span 被挤到起始位置**中间没有间隙**（变成 "SPAN1SPAN2" 粘一起），规尺线占满右侧空间。

✅ 修法：**把规尺线写成真实 DOM 元素，放在两个文字之间**：
```html
<div class="row">
  <span>LEFT</span>
  <span class="rule"></span>
  <span>RIGHT</span>
</div>
```
```css
.row { display: flex; align-items: center; gap: 20px; }
.row .rule { flex: 1; height: 1px; background: #000; }
```

用 `::after` 伪元素做 flex 成员本身不是错——**错在没算清 flex child 的总数**。决定用真 DOM 还是伪元素之前，先数一下 `.row` 里会有几个孩子、每个的 `flex` 值是多少、`justify-content` 要怎么作用。

### 持久化"播放位置"
做 deck / video / 多页原型时，把**当前 slide / 时间点存 localStorage**，加载时读回来——用户刷新不丢失上下文，是迭代设计时高频动作。

### 大文件拆分
单个文件别超 1000 行。组件拆 JSX 文件，主 HTML 用 script 标签引入。

### 按需引入资源
不要 bulk-copy 整个素材库（>20 文件）。先写代码，再把代码引用到的那几个文件 copy 进来。

---

## 不同输出类型的要点

### 落地页 / 营销页
- 把"信息层级"想清楚：第一屏在卖什么、第二屏证明什么、第三屏让用户做什么
- 留白 ≥ 视觉元素本身的份量
- 至少给一版「克制冷淡」+ 一版「热烈鲜艳」+ 一版「编辑/杂志风」

### 幻灯片 / Pitch Deck

#### 正确工作流：先选样式，再做完整版

**不要**一开始就输出完整三版 × N 页——用户要在三份完整 PPT 里选，改动就得三份同步，成本极高。

正确顺序：
1. **先出「风格预览」**：每版只做 **封面 + 1–2 张典型内容页**，三版放在同一个 HTML 里，tab 或并列展示
2. **用户选定一版**
3. **再展开完整 PPT**：基于选定样式做剩余所有页

风格预览文件命名：`XXX - 风格预览.html`，完整版另存：`XXX - 三版方案.html`（已淘汰）或 `XXX.html`。

#### 基础设置
- 1920×1080，正文 ≥ 24px；标题 64–88px（中文长句超 100px 必然换行过多，严禁）
- 用 1-2 种背景色制造章节节奏，不要 30 张 slides 都白底
- 自己写一个 fixed-size canvas + `transform: scale()` 让它在任何视口都自适应：
  - `.stage` 加 `padding`（上下 60px+，底部 100px+ 给控制栏留位），scale 计算时减去 padding：
    ```js
    const scale = Math.min((window.innerWidth - padX) / 1920, (window.innerHeight - padY) / 1080);
    ```
  - 导航键 prev/next 放在 `.deck` **外面**（否则被 scale 压缩）
- 不要默认加 speaker notes，用户明确说要才加

#### 防止多页同时显示（关键 CSS bug）
每页 `.slide` 常有自己的 `display: grid / flex`，单纯 `.slide.active { display: block }` 会被覆盖。**必须用**：
```css
.slide:not(.active) { display: none !important; }
```

#### 内容撑满高度，禁止内容堆顶 + 大片空白在底
flex 列布局的页面中，内容区域（三列、步骤卡、路径卡等）**必须加 `flex: 1`**，让它撑满 slide 剩余高度：
```css
.s-xxx { display: flex; flex-direction: column; padding: 90px 120px 100px; }
.s-xxx .content-area { flex: 1; align-content: center; }   /* 或 space-between */
```
grid 布局的页面用 `align-items: stretch` + 子项 `min-height: 0` 防止溢出。

#### page-footer 不被内容遮挡
`page-footer` 是 `position: absolute; bottom: 60px`，占据 slide 底部约 100px。
**所有有 page-footer 的 slide 必须设 `padding-bottom ≥ 100px`**，否则最后一行内容与 footer 重叠。

#### 字号参考（1920×1080）
| 用途 | 范围 | 备注 |
|------|------|------|
| 主标题 H1 | 88–120px | 封面大字，≤ 2 行 |
| 页内标题 H2 | 64–80px | 中文长句会换行，≤ 3 行 |
| 装饰数字/英文斜体 | 44–60px | 比 H2 小一档 |
| 卡片/栏目标题 H3 | 32–44px | |
| 正文 / 列表项 | 22–28px | 绝不低于 20px |
| meta / mono 标签 | 16–20px | |

#### 真实素材优先，占位框是最后手段
- **做 PPT 之前先扫描来源文章/文档里的所有图片 URL**（Markdown 里的 `![...](url)`），把真实截图直接放进对应 slide
- 用 `<img>` + CSS：
  ```css
  .slide-img { width: 100%; height: 100%; object-fit: cover; object-position: top left; border-radius: 4px; }
  ```
- 只有在来源里真的没有截图时才用占位框；占位框留文字说明「此处替换为截图」
- **截图多于 1 张时**：right 列用 `display: flex; flex-direction: column; gap: 20px`，每张图 `flex: 1; min-height: 0`

#### 布局变体选择
| 页面内容 | 推荐布局 |
|---------|---------|
| 标题 + 三要点 | flex 列，三列 `flex: 1` 撑底 |
| 标题 + 截图 | 两列 grid（1fr 0.75fr），左文右图 |
| 步骤卡 | grid（1fr 70px 1fr），arrow 居中 |
| 对比两列 | grid（1fr 1fr），border 隔开，`flex: 1` 撑高 |
| 配置/方案 | 2–3 列 + 最后一列放截图 |

### 交互原型
- **不要加 title screen / 启动页**。原型直接居中/响应式占满视口
- 交互核心做出来，边缘交互可以省略
- 状态用 React state 管理，简单转场用 CSS transitions

### 动画 / Video-style

#### 配色：永远从品牌色出发
**严禁凭空选色**。做任何品牌的宣传动效，第一步是确认品牌主色，所有配色从品牌色推导：
- 主色 → 深/浅渐变、发光色（加 opacity）、暗色背景（主色降饱和度 + 降亮度）
- 强调色 → 用 `oklch()` 在主色基础上旋转色相 120°/180° 推导，不要凭感觉加一个无关颜色
- 不确定品牌色时**先问**，绝不自己假设

#### 时间轴引擎（标准架构，直接复用）

产品宣传动效用 **sprite + 时间轴驱动**，不要手写 setTimeout 链：

```html
<!-- 每个场景是一个 sprite，带开始/结束时间戳 -->
<div class="sprite" data-start="0"  data-end="2.2">...</div>
<div class="sprite" data-start="2"  data-end="5.2">...</div>
<div class="sprite" data-start="5"  data-end="11.2">...</div>

<script>
const DURATION = 30;
let playing = false, currentTime = 0, lastTick = 0, speed = 1;

const sprites = Array.from(document.querySelectorAll('.sprite')).map(el => ({
  el, start: +el.dataset.start, end: +el.dataset.end,
  entered: false, exited: false,
}));

function tick(now) {
  if (!playing) return;
  currentTime = Math.min(currentTime + (now - lastTick) / 1000 * speed, DURATION);
  lastTick = now;
  if (currentTime >= DURATION) setPlaying(false);
  render();
  if (playing) requestAnimationFrame(tick);
}

function render() {
  sprites.forEach(s => {
    const active = currentTime >= s.start && currentTime < s.end;
    if (active && !s.entered) {
      s.el.classList.add('active', 'fade-in');
      s.entered = true;
      onEnter(s);           // 触发场景专属入场逻辑
    } else if (!active && s.entered && !s.exited) {
      s.el.classList.add('fade-out');
      s.exited = true;
      setTimeout(() => s.el.classList.remove('active'), 500);
    }
  });
  updateDynamic();          // 每帧更新数字/进度条
}

function seek(t) {
  currentTime = Math.max(0, Math.min(DURATION, t));
  sprites.forEach(s => { s.entered = false; s.exited = false; });
  resetAll();               // 重置所有动态状态
  render();
}
</script>
```

标准 Sprite CSS：
```css
.sprite { position:absolute; inset:0; opacity:0; pointer-events:none;
  display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; }
.sprite.active { opacity:1; }
.fade-in  { animation: spIn  0.8s cubic-bezier(0.2,0.9,0.3,1) forwards; }
.fade-out { animation: spOut 0.5s cubic-bezier(0.4,0,1,1) forwards; }
@keyframes spIn  { from{opacity:0;transform:translateY(18px)} to{opacity:1;transform:translateY(0)} }
@keyframes spOut { from{opacity:1;transform:translateY(0)} to{opacity:0;transform:translateY(-20px)} }
```

#### CLEAN 模式（录屏/截图必备）

任何宣传动效都要加，两行 CSS + URL 参数：

```css
body.clean #controls { display: none; }
body.clean #tweaks   { display: none; }
```

```js
document.getElementById('cleanBtn').addEventListener('click',
  () => document.body.classList.toggle('clean'));
// URL ?clean=1 直接进入录屏模式
if (new URLSearchParams(location.search).get('clean') === '1')
  document.body.classList.add('clean');
```

标准热键集（必须实现）：`␣` 播放/暂停，`H` 隐藏 UI，`T` 打开 Tweaks，`0` 回开头，`←→` ±2 秒。

#### 节奏与自动播放（默认行为）

- **自动播放**：页面加载完成后立即调用 `setPlaying(true)`，不要等用户点击。Init 区最后两行固定是：
  ```js
  render();
  setPlaying(true);  // 自动播放
  ```
- **场景时长**：每个场景默认 2–4 秒，不要超过 5 秒。30 秒标准叙事参考时长：
  | 场景 | 建议时长 |
  |------|---------|
  | Logo 爆破 | 1.5s |
  | 核心主张 | 2.3s |
  | 英雄指标 + 柱图 | 5.8s |
  | Benchmark 矩阵 | 4.5s |
  | 单项大数 | 4.5s |
  | 粒子动画 | 4.5s |
  | 功能卡片 | 4s |
  | CTA 收尾 | 3s |
  总时长控制在 **25–28 秒**，超过就压缩单场景，不要拉长整体。

#### 字号决策表（宣传动效专用）

| 内容类型 | 字号 | 说明 |
|---------|------|------|
| 单指标独占整屏 | 300–420px | 数字即画面，让数字呼吸，不配其他元素 |
| 指标 + 对比柱图同屏 | 200–280px | 给图表留空间 |
| 多指标对比网格（每格） | 80–96px | 格内留白 ≥ 数字本身 |
| 功能卡片主标题 | 32–42px | 同 Slide H3 规格 |
| Logo / 品牌名（封面/结尾）| 180–240px | 专用，不用于正文 |
| 标签 / mono 数据标注 | 12–22px | 等宽字体（JetBrains Mono 等）|

核心原则：**信息越少，字越大**。一个数字一整屏时，就让它大到有压迫感。

#### Canvas 运动模糊技巧

```js
// ✅ 半透明覆盖 = 拖影（值越小拖影越长）
ctx.fillStyle = 'rgba(5, 5, 10, 0.14)';
ctx.fillRect(0, 0, W, H);

// ❌ clearRect = 无拖影，粒子感消失
ctx.clearRect(0, 0, W, H);
```

环形 Swarm 标准写法（粒子绕中心公转）：
```js
const pts = Array.from({ length: N }, () => {
  const a = Math.random() * Math.PI * 2;
  const r = minR + Math.random() * rangeR;
  return { angle: a, radius: r,
    speed: 0.003 + Math.random() * 0.005,
    size: 1.5 + Math.random() * 2 };
});
// 每帧：
pts.forEach(p => {
  p.angle += p.speed;
  p.x = cx + Math.cos(p.angle) * p.radius;
  p.y = cy + Math.sin(p.angle) * p.radius;
});
```

#### 30 秒产品宣传标准叙事结构

| # | 场景 | 时长 | 内容 | 目的 |
|---|------|------|------|------|
| 1 | Logo 爆破 | 0–2s | 品牌名大字冲屏 | 建立认知 |
| 2 | 核心主张 | 2–5s | 一句话定位 + 关键词 | 情感钩子 |
| 3 | 英雄指标 | 5–11s | 单个最强指标动态计数 + 柱图对比 | 可信度 |
| 4 | 数据矩阵 | 11–16s | 多项基准测试网格，依次入场 | 全面性 |
| 5 | 单项大数 | 16–20s | 一个震撼数字 + 进度条 | 深度感知 |
| 6 | 动画场景 | 20–24s | Canvas 粒子/Swarm 可视化 | 视觉高潮 |
| 7 | 功能卡片 | 24–27s | 3–5 个特性依次弹出 | 产品细节 |
| 8 | 生态链接 | 27–29s | 渠道 / URL | 转化引导 |
| 9 | 终版 CTA | 29–31s | Logo + 行动号召 + 网址 | 收尾 |

节奏规律：**冲击 → 可信 → 深度 → 高潮 → 收尾**，场景时长可伸缩，顺序不要打乱。

#### Tweaks 面板最小集（宣传动效标配）

```js
// Speed + 两个品牌色 picker，颜色直接改 CSS 变量
document.getElementById('tSpeed').addEventListener('input',
  e => speed = +e.target.value);
document.getElementById('tAccent').addEventListener('input',
  e => document.documentElement.style.setProperty('--a1', e.target.value));
document.getElementById('tAccent2').addEventListener('input',
  e => document.documentElement.style.setProperty('--a2', e.target.value));
```

### 信息图 / 海报
- 已经有专门的 `infographic` skill，优先调它
- 这个 skill 用于其他「网页化」的信息图变体（动态、可交互的）

### 设计系统 / UI Kit
- 输出多个 HTML：`tokens.html`（色板/字阶/间距）、`components.html`（按钮/输入/卡片）、`patterns.html`（组合范式）
- 每页顶部放系统说明
- 组件给多种状态：default / hover / active / disabled / loading / error

---

## 「Tweaks」模式：让用户在页面里调

如果用户想要"我自己拖滑块/换色看效果"的体验，做一个**右下角浮动面板**，里面是滑块/色板/单选/开关，把可调项暴露出来。改动 → 立即应用到页面 → 同时把当前值序列化到 URL hash 或 localStorage（让刷新不丢）。

模板写法：

```html
<!-- 默认值 -->
<script>
const TWEAKS = {
  primaryColor: '#D97757',
  fontSize: 16,
  dark: false,
  // ...
};
</script>

<!-- 调整面板（位于页面右下，点 toggle 显示/隐藏）-->
<!-- 监听 input → 更新 CSS 变量 → 持久化 -->
```

如果用户没说要 tweaks，**也默认加 1-2 个有趣的**——给用户惊喜，让他看到设计的可能空间。

---

## 提交前自检清单

- [ ] 是不是有「先问问题」这一步？没有的话用户给的信息够吗？
- [ ] **语言**（中文/英文/混排）有没有弹窗问过？用户没明说就必须问，不能猜。
- [ ] **字号强度**（规范级/震撼态/克制型）有没有弹窗问过？不能默认"规范级应该够"。
- [ ] **风格方案数量**（1/2/3 版）有没有弹窗问过？用户没明说就必须问，不能自行假设 3 版。
- [ ] 有没有引用真实的设计上下文，不是凭记忆瞎编？
- [ ] 给了 ≥ 3 种风格方案吗？方案之间真的不一样吗（不只是换个颜色）？
- [ ] 反 AI 俗套那一节里的东西，有没有不小心犯了？
- [ ] 文字尺度 OK 吗？空白多吗？信息密度合理吗？
- [ ] 没废话 section、没占位填空？
- [ ] 浏览器打开能跑、控制台不报错？
- [ ] 文件名描述清晰、版本管理清晰？

**宣传动效专项**
- [ ] 配色是从品牌主色推导的，不是凭空选的？
- [ ] 用了时间轴引擎（sprite + data-start/data-end），不是 setTimeout 链？
- [ ] 加了 CLEAN 模式（`body.clean` + `?clean=1`）？
- [ ] 实现了标准热键集（␣ H T 0 ←→）？
- [ ] Init 末尾有 `setPlaying(true)` 自动播放？
- [ ] 每个场景时长 ≤ 5 秒，总时长 ≤ 28 秒？
- [ ] 独占屏的大数字 ≥ 300px？对比网格的数字用 88–96px？
- [ ] Canvas 粒子/轨迹用半透明覆盖（0.14 opacity）而不是 clearRect？
- [ ] Tweaks 面板有 Speed + 品牌色 picker？

**幻灯片专项**
- [ ] 先输出「风格预览」（封面 + 1–2 页）让用户选样式，没有直接做完整版？
- [ ] 用了 `.slide:not(.active) { display: none !important }` 防多页同显？
- [ ] 所有有 `page-footer` 的 slide 都有 `padding-bottom ≥ 100px`？
- [ ] 所有 flex 列 slide 的内容区都有 `flex: 1` 撑满高度，没有内容堆顶 + 大片空白在底？
- [ ] 来源文章/文档里的图片 URL 都扫描过了？真实截图已替换占位框？
- [ ] 标题字号没有超过 88px（H2）/ 120px（H1）？

---

## 不做什么

- 不复刻别家公司的专有 UI / 品牌元素（除非用户证明他在那家公司）。理解他真正想做的东西，然后做一个尊重 IP 的原创版本。
- 不要在没有上下文时硬撑「我帮你从零做高保真」。**先要上下文，再动手。**
- 不要用工程师的本能压住设计师的判断：色彩不和谐就是不和谐，layout 出戏就是出戏，不要用「但这是 best practice」自我安慰。

