ワンクリックで
frontend-slides
生成零依赖、动画丰富的 HTML 演示文稿。支持从零创建、Markdown 文件转换、PPT 转换。当用户说「做 slides」「做演示」「做汇报」「PPT 转 web」「把文档做成演示」「产品展示」时使用。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
生成零依赖、动画丰富的 HTML 演示文稿。支持从零创建、Markdown 文件转换、PPT 转换。当用户说「做 slides」「做演示」「做汇报」「PPT 转 web」「把文档做成演示」「产品展示」时使用。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | frontend-slides |
| description | 生成零依赖、动画丰富的 HTML 演示文稿。支持从零创建、Markdown 文件转换、PPT 转换。当用户说「做 slides」「做演示」「做汇报」「PPT 转 web」「把文档做成演示」「产品展示」时使用。 |
生成零依赖、自包含的 HTML 演示文稿,在浏览器中直接运行。默认中文排版优化,支持中英混排。
lang="zh-CN",中文字体 + 排版规范每页 slide 必须完全可见,无需滚动。
| Slide 类型 | 最大内容(中文) |
|---|---|
| 标题页 | 1 标题 + 1 副标题 + 可选标语 |
| 内容页 | 1 标题 + 3-5 条要点(每条最多 2 行,18-25 字/行) |
| 特性网格 | 1 标题 + 最多 6 张卡片(2x3 或 3x2) |
| 代码页 | 1 标题 + 最多 8-10 行代码 |
| 引用页 | 1 段引用(最多 3 行)+ 出处 |
| 图片页 | 1 标题 + 1 张图片(max-height: 60vh) |
| 数据卡片页 | 1 标题 + 最多 4 个大数字卡片 |
| 对比展示页 | 1 标题 + 左右两列对比 |
| 产品大图页 | 1 张全屏产品图 + 底部标题 |
| 规格参数页 | 左侧图片 + 右侧最多 6 行参数 |
| 流程展示页 | 1 标题 + 最多 4 个步骤节点 |
内容超出上限 → 拆分成多页,禁止缩小字号或允许滚动。
每个演示文稿必须包含以下基础样式:
html, body {
height: 100%;
overflow-x: hidden;
}
html {
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
}
.slide {
width: 100vw;
height: 100vh;
height: 100dvh;
overflow: hidden;
scroll-snap-align: start;
display: flex;
flex-direction: column;
position: relative;
}
.slide-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
max-width: min(100%, 1200px);
padding: var(--slide-padding);
}
/* 默认:笔记本屏幕 / 一般显示器 */
:root {
--title-size: clamp(1.5rem, 5vw, 4rem);
--h2-size: clamp(1.25rem, 3.5vw, 2.5rem);
--h3-size: clamp(1rem, 2.5vw, 1.75rem);
--body-size: clamp(0.75rem, 1.5vw, 1.125rem);
--small-size: clamp(0.65rem, 1vw, 0.875rem);
--slide-padding: clamp(1rem, 4vw, 4rem);
--content-gap: clamp(0.5rem, 2vw, 2rem);
--element-gap: clamp(0.25rem, 1vw, 1rem);
}
/* 投屏模式:大屏幕投影仪 / 会议室电视 — 在 <html> 加 class="projection" 启用 */
.projection:root {
--title-size: clamp(2.8rem, 6.5vw, 5rem);
--h2-size: clamp(1.8rem, 4.2vw, 3rem);
--h3-size: clamp(1.3rem, 2.8vw, 1.9rem);
--body-size: clamp(1.05rem, 1.8vw, 1.35rem);
--small-size: clamp(0.88rem, 1.3vw, 1.1rem);
--slide-padding: clamp(2.5rem, 6vw, 6rem);
--content-gap: clamp(1.2rem, 3vw, 2.5rem);
--element-gap: clamp(0.3rem, 1vw, 0.8rem);
}
.card, .container, .content-box {
max-width: min(90vw, 1000px);
max-height: min(80vh, 700px);
}
img, .image-container {
max-width: 100%;
max-height: min(50vh, 400px);
object-fit: contain;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
gap: clamp(0.5rem, 1.5vw, 1rem);
}
@media (max-height: 700px) {
:root {
--slide-padding: clamp(0.75rem, 3vw, 2rem);
--content-gap: clamp(0.4rem, 1.5vw, 1rem);
--title-size: clamp(1.25rem, 4.5vw, 2.5rem);
--h2-size: clamp(1rem, 3vw, 1.75rem);
}
}
@media (max-height: 600px) {
:root {
--slide-padding: clamp(0.5rem, 2.5vw, 1.5rem);
--content-gap: clamp(0.3rem, 1vw, 0.75rem);
--title-size: clamp(1.1rem, 4vw, 2rem);
--body-size: clamp(0.7rem, 1.2vw, 0.95rem);
}
.nav-dots, .keyboard-hint, .decorative { display: none; }
}
@media (max-height: 500px) {
:root {
--slide-padding: clamp(0.4rem, 2vw, 1rem);
--title-size: clamp(1rem, 3.5vw, 1.5rem);
--h2-size: clamp(0.9rem, 2.5vw, 1.25rem);
--body-size: clamp(0.65rem, 1vw, 0.85rem);
}
}
@media (max-width: 600px) {
:root { --title-size: clamp(1.25rem, 7vw, 2.5rem); }
.grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.2s !important;
}
html { scroll-behavior: auto; }
}
不能直接取反 CSS 函数:-clamp() / -min() / -max() 会被浏览器静默忽略。必须用 calc(-1 * clamp(...)) 包裹。
当演示文稿用于投影仪或大屏幕时:
<html> 标签添加 class="projection" — 自动切换到更大的 clamp 参数集--text-secondary 不应浅于 #3d3835,--text-muted 不应浅于 #6b6560,否则投影后难以阅读max-height: 100%; overflow: hidden 在 .slide-content 上 — 内容增多后极易导致顶部或底部被裁切(如标签、标题被遮挡),如确有溢出问题应通过拆页解决max-width 限制 — .slide-content 建议加 max-width: min(100%, 1200px) 防止大屏幕上内容拉得太宽难以阅读所有演示文稿默认遵守以下排版规则:
<html lang="zh-CN">
中文字体通过 Google Fonts 加载。每种风格预设都定义了对应的中文字体对,格式为:
--font-display: 'Noto Sans SC', sans-serif; /* 中文标题 */
--font-body: 'Noto Sans SC', sans-serif; /* 中文正文 */
--font-display-en: 'Clash Display', sans-serif; /* 英文/数字标题 */
--font-body-en: 'Satoshi', sans-serif; /* 英文/数字正文 */
在 CSS 中使用 font-family 时,英文字体写在中文字体前面(浏览器会对每个字符 fallback):
h1 { font-family: var(--font-display-en), var(--font-display); }
body { font-family: var(--font-body-en), var(--font-body); }
:root {
--line-height-zh: 1.8; /* 中文行高(英文用 1.5) */
--letter-spacing-zh: 0.02em;
}
body {
line-height: var(--line-height-zh);
letter-spacing: var(--letter-spacing-zh);
font-feature-settings: "halt"; /* CJK 标点挤压 */
text-align: justify;
word-break: break-all;
overflow-wrap: break-word;
}
/* 标题不两端对齐 */
h1, h2, h3, h4, h5, h6 {
text-align: left;
letter-spacing: 0.04em;
line-height: 1.4;
}
在生成内容时,中文和英文/数字之间自动加半角空格(pangu spacing)。模板中可加 JS 自动处理:
function addPanguSpacing(text) {
return text
.replace(/([\u4e00-\u9fa5])([A-Za-z0-9])/g, '$1 $2')
.replace(/([A-Za-z0-9])([\u4e00-\u9fa5])/g, '$1 $2');
}
以下组件在实际项目中高频使用,可直接复用。
右上角显示当前页码,用斜体衬线字体增加编辑感。
<span class="slide-number">02</span>
.slide-number {
position: absolute;
top: clamp(1.2rem, 3vw, 2.2rem);
right: clamp(1.8rem, 5vw, 4rem);
font-family: var(--font-display-en);
font-size: clamp(0.85rem, 1.4vw, 1.1rem);
color: var(--text-muted);
font-weight: 400;
font-style: italic;
}
slide 顶部的分类标签(如"发现一""核心洞察"),用小型大写字母风格。
<div class="tag">发现一</div>
.tag {
display: inline-block;
font-size: var(--body-size);
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.2em;
font-weight: 600;
margin-bottom: clamp(0.5rem, 1.5vw, 1rem);
font-family: var(--font-body-en), var(--font-body);
}
适合品类密度、市场占比等数据展示。纯 CSS 实现,无 JS 依赖,动画由 .visible class 触发。
<div class="css-bar-chart">
<div class="bar-row reveal">
<div class="bar-label">
<span class="bar-cat">酸奶 / 奶昔</span>
<span class="bar-brands">品牌 A · 品牌 B · 品牌 C…</span>
</div>
<div class="bar-track">
<div class="bar-value" style="--w:100%;--delay:0.1s"></div>
</div>
<span class="bar-num">7+</span>
</div>
<!-- 更多行... -->
</div>
.css-bar-chart { display: flex; flex-direction: column; gap: clamp(0.5rem,1.2vw,1rem); }
.bar-row {
display: grid;
grid-template-columns: minmax(120px, 30%) 1fr auto;
align-items: center;
gap: clamp(0.5rem, 1.5vw, 1.2rem);
}
.bar-cat { font-weight: 700; font-size: var(--body-size); display: block; }
.bar-brands { font-size: var(--small-size); color: var(--text-muted); display: block; }
.bar-track {
height: clamp(0.6rem, 1.2vw, 0.9rem);
background: var(--border-light, #eae6e0);
border-radius: 99px;
overflow: hidden;
}
.bar-value {
height: 100%; width: 0;
background: var(--accent);
border-radius: 99px;
transition: width 1s var(--ease-out-expo);
transition-delay: var(--delay, 0s);
}
.slide.visible .bar-value { width: var(--w); }
.bar-num {
font-family: var(--font-display-en);
font-weight: 700;
font-size: var(--h3-size);
color: var(--text-secondary);
min-width: 2.5em;
text-align: right;
}
适合价格带分析、时间段对比等场景。
<div class="price-range-chart">
<div class="range-row reveal">
<span class="range-brand">品牌 A</span>
<div class="range-bar-wrap">
<div class="range-bar" style="left:10%;width:30%"></div>
</div>
<span class="range-price">¥15-28</span>
</div>
<div class="range-row reveal" style="background:rgba(200,146,46,0.1)">
<span class="range-brand" style="color:#c8922e;font-weight:700">空白机会</span>
<div class="range-bar-wrap">
<div class="range-bar gap-bar" style="left:25%;width:20%"></div>
</div>
<span class="range-price highlight">¥20-35</span>
</div>
</div>
.price-range-chart { margin-top: var(--content-gap); }
.range-row {
display: grid;
grid-template-columns: minmax(80px, 18%) 1fr auto;
align-items: center;
gap: clamp(0.5rem, 1.5vw, 1.2rem);
padding: clamp(0.4rem, 0.8vw, 0.7rem) clamp(0.8rem, 1.5vw, 1.2rem);
border-bottom: 1px solid var(--border-light, #eae6e0);
}
.range-brand { font-size: var(--body-size); color: var(--text-secondary); font-weight: 500; }
.range-bar-wrap {
position: relative; height: clamp(0.5rem, 1vw, 0.7rem);
background: #cdc8c0; border-radius: 99px;
}
.range-bar {
position: absolute; top: 0; height: 100%;
background: var(--accent); border-radius: 99px;
opacity: 0; transition: opacity 0.6s ease;
}
.range-bar.gap-bar {
background: #c8922e;
animation: pulse-bar 2s ease-in-out infinite;
}
@keyframes pulse-bar { 0%,100% { opacity: 0.7; } 50% { opacity: 1; } }
.slide.visible .range-bar { opacity: 1; }
.range-price { font-family: var(--font-display-en); font-size: var(--body-size); min-width: 5em; text-align: right; }
.range-price.highlight { color: #c8922e; font-weight: 700; }
适合展示 3-4 个关键数字指标。
<div class="data-cards">
<div class="data-card reveal">
<div class="number">17</div>
<div class="label">走访门店</div>
</div>
<!-- 更多卡片... -->
</div>
.data-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
gap: var(--content-gap);
}
.data-card {
background: var(--bg-card, #fff);
border: 1px solid var(--border, #e0dbd4);
padding: clamp(1rem, 2.5vw, 2rem);
text-align: center;
display: flex; flex-direction: column;
justify-content: center; align-items: center;
}
.data-card .number {
font-family: var(--font-display-en);
font-size: var(--big-number, clamp(2.5rem, 7vw, 4.5rem));
font-weight: 700;
line-height: 1;
color: var(--accent);
}
.data-card .label {
font-size: var(--body-size);
color: var(--text-secondary);
margin-top: var(--element-gap);
}
替代传统的"谢谢观看",用一句有态度的话收尾。注意多行文本不要用 <br> 控制换行(在不同视口尺寸下 <br> 表现不一致),用 <span style="display:block"> 强制每行独立。
<section class="slide end-slide">
<div class="slide-content">
<div class="end-rule reveal"></div>
<h2 class="reveal-blur">
<span style="display:block">不是从零开始。</span>
<span style="display:block">是在已有的网络和经验上,</span>
<span style="display:block">做一次真正的升级。</span>
</h2>
</div>
</section>
.end-slide {
background: var(--bg-primary);
justify-content: center;
}
.end-slide .slide-content {
max-width: min(90%, 900px);
text-align: center;
}
.end-rule {
width: clamp(2rem, 5vw, 4rem);
height: 2px;
background: var(--accent);
margin: 0 auto clamp(1.5rem, 4vw, 3rem);
}
.end-slide h2 {
font-size: var(--h2-size);
font-weight: 600;
line-height: 1.6;
color: var(--text-secondary);
}
禁止使用 Emoji 图标。Emoji(🎯🔥📊 等)在专业演示中显得幼稚和廉价,无论什么主题都不要用。
替代方案:使用内联 SVG 图标。好处是可以自定义颜色、大小,且不依赖外部文件。
<!-- ✗ 不要这样 -->
<span>🔥 热门品类</span>
<!-- ✓ 用 inline SVG -->
<span class="icon-hot">
<svg viewBox="0 0 16 16" width="1em" height="1em" fill="currentColor">
<path d="M8 16c3.314 0 6-2.686 6-6 0-1.5-.5-3.5-2-5-.643-.543-1-1.5-1.5-3-.5 1.5-1 2-2.5 3C6.5 6.5 6 8 6 9c0 .5-.5 1-1 1s-1-.5-1-1c0-1 .5-2 1-3-1.5 1-3 3.5-3 5.5C2 13.314 4.686 16 8 16z"/>
</svg>
热门品类
</span>
对于简洁的分隔或装饰,也用 SVG 线条代替 em dash — 或其他 Unicode 装饰符。
判断用户意图:
| 模式 | 触发条件 | 下一步 |
|---|---|---|
| A:新建演示 | 从零创建 slides | → Phase 1(内容梳理) |
| B:PPT 转换 | 有 .ppt/.pptx 文件 | → Phase 4(PPT 提取) |
| C:增强现有 | 有现成 HTML 演示要改进 | → 读取文件后增强,遵循版本管理 |
| D:Markdown 转换 | 有 .md 文件要转成演示 | → Phase 5(Markdown 提取) |
| E:替换图片 | 「图片准备好了」「替换图片」 | → 扫描 images/ 文件夹,替换占位符 |
| F:回滚版本 | 「回到 v2」「用上一版」 | → 从 versions/ 复制对应版本 |
| G:修改迭代 | 「改一下第 3 页」「更新数据」 | → 先备份当前版本,再修改 |
通过提问了解内容:
问题 1:用途
问题 1.5:展示方式
<html> 加 class="projection" 启用大字号参数集,并加深辅助文字颜色问题 2:页数
问题 3:内容状态
如果用户有内容,让他分享。
先问用户如何选择风格:
选项 A:引导发现(推荐)
选项 B:直接选择
| 预设 | 氛围 | 适合 |
|---|---|---|
| Bold Signal | 自信、高冲击力 | 路演、主旨演讲 |
| Electric Studio | 干净、专业 | 机构展示 |
| Creative Voltage | 有活力、复古现代 | 创意提案 |
| Dark Botanical | 优雅、精致 | 高端品牌 |
| Notebook Tabs | 编辑风、有序 | 报告、评测 |
| Pastel Geometry | 友好、亲切 | 产品概览 |
| Split Pastel | 活泼、现代 | 创意机构 |
| Vintage Editorial | 有态度、个性 | 个人品牌 |
| Neon Cyber | 未来感、科技 | 科技初创 |
| Terminal Green | 开发者风 | 开发工具、API |
| Swiss Modern | 极简、精确 | 企业、数据 |
| Paper & Ink | 文学、沉思 | 叙事 |
| Warm Retail | 暖调、零售 | 食品/零售行业、渠道展示 |
| Data Executive | 数据驱动、高管 | 数据汇报、进度同步 |
| Clean Brief | 极简、高密度 | 内部对齐、方案说明 |
| Product Showcase | 产品聚焦 | 产品展示、打样对比 |
| Neo-Brutalist Editorial | 新野兽主义、工业编辑 | 投资路演、硬核数据展示 |
完整风格定义见 STYLE_PRESETS.md。
问题:你希望观众看到后的感受?
| 氛围 | 推荐风格 |
|---|---|
| 专业/可信 | Bold Signal, Electric Studio, Data Executive, Clean Brief |
| 兴奋/前沿 | Creative Voltage, Neon Cyber, Split Pastel |
| 平静/专注 | Notebook Tabs, Paper & Ink, Swiss Modern |
| 有感染力 | Dark Botanical, Vintage Editorial, Warm Retail |
基于氛围选择生成 3 个预览 HTML 文件,每个只包含一页标题 slide,展示排版、配色、动画。
预览文件保存到项目的 slide-previews/ 子目录中:
{project-slug}/
└── slide-previews/
├── style-a.html
├── style-b.html
└── style-c.html
展示后问用户选哪个,或是否要混搭元素。
每个演示项目使用统一的输出目录。根据来源不同,目录位置如下:
| 来源 | 输出目录 |
|---|---|
| OpenClaw agent | ~/presentations/{project-slug}/ |
| Cursor 本地 | 当前工作目录下 ./presentations/{project-slug}/ |
| 用户指定路径 | 用户指定的路径 |
{project-slug} 由演示标题自动生成:中文转拼音缩写或用户自定义短名。例如:「七星产品介绍」→ qx-product-intro,「Q3 数据汇报」→ q3-data-report。
presentations/
└── {project-slug}/
├── {project-slug}.html # 当前版本(自包含演示文稿)
├── images/ # 图片素材文件夹
│ ├── 01.png # 按序号命名,对应 slide 中的占位符
│ ├── 02.png
│ ├── 03.jpg
│ └── ...
├── versions/ # 版本备份
│ ├── v1_{project-slug}.html # 第一版
│ ├── v2_{project-slug}.html # 第二版
│ └── ...
├── CHANGELOG.md # 变更记录
└── slide-previews/ # 风格预览(生成后可删除)
├── style-a.html
├── style-b.html
└── style-c.html
{project-slug}.html(始终是最新版本)images/{序号}.{ext},序号从 01 开始,两位数字补零versions/v{N}_{project-slug}.htmlslide-previews/style-{a|b|c}.htmlimages/、versions/){project-slug}.htmlversions/v1_{project-slug}.htmlCHANGELOG.md,写入首条记录用户要求修改现有演示时:
{project-slug}.html 复制到 versions/v{N}_{project-slug}.html(N 为递增版本号)CHANGELOG.md 追加变更记录# 变更记录
## v2 — 2026-02-26
- 替换第 3 页产品图
- 修改第 5 页数据为最新季度
## v1 — 2026-02-26
- 初始生成
- 风格:Warm Retail
- 页数:12
用户说「回到上一版」或「用 v2 的版本」时,从 versions/ 中复制对应版本覆盖当前文件,并在 CHANGELOG 中记录回滚操作。
生成演示时,如果用户尚未提供图片素材,使用占位色块代替。
每个需要图片的位置生成一个带编号的占位色块:
#E5E5E5(浅灰),与所有风格兼容01 开始,全局递增(跨 slide 统一编号)<div class="img-placeholder" data-img-id="01">
<span class="placeholder-id">01</span>
<span class="placeholder-hint">建议 1920×1080</span>
<span class="placeholder-desc">产品正面图</span>
</div>
.img-placeholder {
background: #E5E5E5;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
border-radius: 8px;
min-height: 200px;
position: relative;
}
.placeholder-id {
font-size: clamp(2rem, 5vw, 4rem);
font-weight: 900;
color: #BDBDBD;
font-family: 'Courier New', monospace;
}
.placeholder-hint {
font-size: var(--small-size);
color: #9E9E9E;
letter-spacing: 0.05em;
}
.placeholder-desc {
font-size: var(--small-size);
color: #757575;
}
| Slide 类型 | 占位符尺寸 | 建议分辨率 |
|---|---|---|
| 产品大图页(product-hero) | width: 100%; height: 100% | 1920×1080 |
| 规格参数页(spec-card) | max-height: min(70vh, 500px) | 800×800 |
| 对比展示页(comparison) | 两个 height: min(40vh, 300px) | 800×600 |
| 内容页配图 | max-height: min(50vh, 400px) | 1200×675 |
| 流程展示页图标 | width: clamp(3rem, 6vw, 5rem) | 200×200 |
生成 HTML 时,同时创建 images/ 文件夹,并生成一个 README.md:
# 图片素材
将图片文件放入此文件夹,按序号命名即可自动替换演示中的占位符。
| 序号 | 所在页 | 用途 | 建议分辨率 | 格式 |
|-----|-------|-----|----------|-----|
| 01 | Slide 2 | 产品正面图 | 1920×1080 | PNG/JPG |
| 02 | Slide 3 | 规格参数配图 | 800×800 | PNG |
| 03 | Slide 5 | 对比图(左) | 800×600 | PNG/JPG |
| 04 | Slide 5 | 对比图(右) | 800×600 | PNG/JPG |
| ...
命名示例:01.png、02.jpg、03.webp
支持格式:PNG、JPG、JPEG、WebP、SVG
HTML 中引用图片时统一使用相对路径 images/{序号}.{ext}:
<!-- 占位模式(无图片时) -->
<div class="img-placeholder" data-img-id="01">
<span class="placeholder-id">01</span>
<span class="placeholder-hint">建议 1920×1080</span>
<span class="placeholder-desc">产品正面图</span>
</div>
<!-- 替换后(用户放入图片后,重新生成或手动替换) -->
<img src="images/01.png" alt="产品正面图">
当用户说「图片准备好了」或「替换图片」时:
images/ 文件夹,获取已有图片文件列表data-img-id,将占位符替换为 <img> 标签基于 Phase 1 的内容 + Phase 2 的风格,生成完整演示文稿。
<img> 引用;未提供 → 使用占位色块{project-slug}.htmlversions/v1_{project-slug}.html{project-slug}/
├── {project-slug}.html
├── images/
│ └── README.md # 图片清单(序号→用途→分辨率)
├── versions/
│ └── v1_{project-slug}.html
├── CHANGELOG.md
└── slide-previews/ # 风格选择阶段生成,交付时可清理
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>演示标题</title>
<!-- 字体:中文 + 英文 -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap">
<style>
:root {
/* 颜色 */
--bg-primary: #0a0f1c;
--text-primary: #ffffff;
--accent: #00ffcc;
/* 字体 */
--font-display: 'Noto Sans SC', sans-serif;
--font-body: 'Noto Sans SC', sans-serif;
/* 排版 - 所有尺寸用 clamp() */
--title-size: clamp(2rem, 6vw, 5rem);
--body-size: clamp(0.75rem, 1.2vw, 1rem);
--slide-padding: clamp(1.5rem, 4vw, 4rem);
--content-gap: clamp(1rem, 2vw, 2rem);
/* 中文排版 */
--line-height-zh: 1.8;
--letter-spacing-zh: 0.02em;
/* 动画 */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--duration-normal: 0.6s;
}
/* 基础样式 + viewport fitting + 中文排版... */
</style>
</head>
<body>
<div class="progress-bar"></div>
<nav class="nav-dots"></nav>
<section class="slide title-slide">
<h1 class="reveal">演示标题</h1>
<p class="reveal">副标题</p>
</section>
<!-- 更多 slides... -->
<script>
class SlidePresentation {
// 键盘导航、触摸/滑动、鼠标滚轮、进度条、导航点
}
new SlidePresentation();
</script>
</body>
</html>
.visible class 驱动 CSS 动画PageDown / PageUp 键码。setupKeyboard() 中必须监听这两个键:
if (['ArrowDown','ArrowRight',' ','PageDown'].includes(e.key)) { this.next(); }
if (['ArrowUp','ArrowLeft','PageUp'].includes(e.key)) { this.prev(); }
同时建议支持 Home 跳首页、End 跳末页。F 键切换全屏的逻辑,且不要在 UI 中添加任何全屏提示文字:
if (e.key.toLowerCase() === 'f') {
if (!document.fullscreenElement) document.documentElement.requestFullscreen().catch(()=>{});
else document.exitFullscreen();
}
section、nav、header)prefers-reduced-motion)clamp(),不用固定像素值用 Python + python-pptx 提取所有文本、图片、备注。
from pptx import Presentation
import os
def extract_pptx(file_path, output_dir):
prs = Presentation(file_path)
slides_data = []
assets_dir = os.path.join(output_dir, 'assets')
os.makedirs(assets_dir, exist_ok=True)
for slide_num, slide in enumerate(prs.slides):
slide_data = {
'number': slide_num + 1,
'title': '',
'content': [],
'images': [],
'notes': ''
}
for shape in slide.shapes:
if shape.has_text_frame:
if shape == slide.shapes.title:
slide_data['title'] = shape.text
else:
slide_data['content'].append({'type': 'text', 'content': shape.text})
if shape.shape_type == 13:
image = shape.image
image_name = f"slide{slide_num+1}_img{len(slide_data['images'])+1}.{image.ext}"
image_path = os.path.join(assets_dir, image_name)
with open(image_path, 'wb') as f:
f.write(image.blob)
slide_data['images'].append({'path': f"assets/{image_name}"})
if slide.has_notes_slide:
slide_data['notes'] = slide.notes_slide.notes_text_frame.text
slides_data.append(slide_data)
return slides_data
展示提取结果让用户确认 → Phase 2 选风格 → 生成 HTML。
当用户提供 .md 文件路径时使用此流程。
读取 .md 文件,按以下规则解析为 slide 结构:
| Markdown 元素 | 映射为 |
|---|---|
# 一级标题 | 标题页(如果是文档第一个标题) |
## 二级标题 | 新 slide 分页点 |
### 三级标题 | 页内子标题 |
| 无序/有序列表 | 要点列表 |
| 表格 | 数据表格 slide 或内嵌表格 |
| 代码块 | 代码 slide |
> 引用块 | 引用 slide |
 图片 | 图片 slide 或页内图片 |
--- 分隔线 | 忽略(不作为分页依据) |
解析完成后,对每页 slide 检查内容密度:
向用户展示解析结果:
从 Markdown 中提取到以下 slide 结构:
**Slide 1(标题页):** [文档标题]
**Slide 2:** [二级标题] — 3 条要点
**Slide 3:** [二级标题] — 1 张表格 + 2 条要点
**Slide 4:** [二级标题] — 4 条要点(拆分自原始 8 条)
**Slide 5:** [二级标题] — 4 条要点(续)
...
共 X 页。是否需要调整?
→ Phase 2(风格选择)
→ Phase 3(生成演示文稿)。Markdown 中的图片处理:
 中的图片 → 复制到 images/ 文件夹并按序号重命名images/README.md 中标注原始路径以下 slide 类型专为产品展示场景设计,可在任何风格中使用:
全屏产品图 + 底部半透明标题栏。有图片时用 <img>,无图片时用占位色块。
<!-- 有图片时 -->
<section class="slide product-hero">
<img src="images/01.png" class="product-hero-img" alt="产品名称">
<div class="product-hero-caption reveal">
<h2>产品名称</h2>
<p>一句话描述</p>
</div>
</section>
<!-- 无图片时(占位模式) -->
<section class="slide product-hero">
<div class="img-placeholder product-hero-img" data-img-id="01">
<span class="placeholder-id">01</span>
<span class="placeholder-hint">建议 1920×1080</span>
<span class="placeholder-desc">产品主图</span>
</div>
<div class="product-hero-caption reveal">
<h2>产品名称</h2>
<p>一句话描述</p>
</div>
</section>
.product-hero { padding: 0; }
.product-hero-img {
width: 100%; height: 100%;
object-fit: cover; position: absolute;
top: 0; left: 0;
}
.product-hero-caption {
position: absolute; bottom: 0; left: 0; right: 0;
padding: var(--slide-padding);
background: linear-gradient(transparent, rgba(0,0,0,0.7));
color: white;
}
左侧图片 + 右侧参数列表。图片位置支持占位符。
<section class="slide spec-card">
<div class="spec-card-layout">
<div class="spec-card-image reveal">
<!-- 有图片时 -->
<img src="images/02.png" alt="产品">
<!-- 无图片时 -->
<!-- <div class="img-placeholder" data-img-id="02"><span class="placeholder-id">02</span><span class="placeholder-hint">建议 800×800</span></div> -->
</div>
<div class="spec-card-info">
<h2 class="reveal">产品名称</h2>
<dl class="spec-list">
<div class="spec-item reveal"><dt>规格</dt><dd>值</dd></div>
<!-- 最多 6 行 -->
</dl>
</div>
</div>
</section>
.spec-card-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--content-gap);
height: 100%;
align-items: center;
}
.spec-card-image img {
max-height: min(70vh, 500px);
width: 100%; object-fit: contain;
}
.spec-list { display: flex; flex-direction: column; gap: var(--element-gap); }
.spec-item { display: flex; justify-content: space-between; }
.spec-item dt { font-weight: 500; color: var(--text-secondary); }
.spec-item dd { font-weight: 700; }
@media (max-width: 768px) {
.spec-card-layout { grid-template-columns: 1fr; }
}
左右并排对比,适合「现有品 vs 新方案」「前 vs 后」。
<section class="slide comparison">
<h2 class="reveal">对比标题</h2>
<div class="comparison-layout">
<div class="comparison-side reveal">
<div class="comparison-label">现有方案</div>
<img src="images/03.png" alt="现有">
<!-- 或占位:<div class="img-placeholder" data-img-id="03"><span class="placeholder-id">03</span><span class="placeholder-hint">建议 800×600</span></div> -->
<ul><li>特点 1</li><li>特点 2</li></ul>
</div>
<div class="comparison-divider"></div>
<div class="comparison-side reveal">
<div class="comparison-label highlight">新方案</div>
<img src="images/04.png" alt="新方案">
<!-- 或占位:<div class="img-placeholder" data-img-id="04"><span class="placeholder-id">04</span><span class="placeholder-hint">建议 800×600</span></div> -->
<ul><li>改进 1</li><li>改进 2</li></ul>
</div>
</div>
</section>
横向时间线,适合「概念 → 打样 → 成品」流程。
<section class="slide process-flow">
<h2 class="reveal">产品开发流程</h2>
<div class="process-timeline">
<div class="process-step reveal">
<div class="process-icon">1</div>
<h3>概念</h3>
<p>说明</p>
</div>
<div class="process-connector"></div>
<!-- 最多 4 个步骤 -->
</div>
</section>
.process-timeline {
display: flex; align-items: center;
justify-content: center; gap: clamp(0.5rem, 2vw, 2rem);
flex-wrap: nowrap;
}
.process-step {
flex: 1; text-align: center;
max-width: min(200px, 22vw);
}
.process-icon {
width: clamp(2rem, 4vw, 3.5rem);
height: clamp(2rem, 4vw, 3.5rem);
border-radius: 50%;
background: var(--accent);
color: var(--bg-primary);
display: flex; align-items: center; justify-content: center;
font-weight: 700; margin: 0 auto var(--element-gap);
font-size: var(--body-size);
}
.process-connector {
width: clamp(1rem, 3vw, 3rem);
height: 2px;
background: var(--accent);
flex-shrink: 0;
}
@media (max-width: 600px) {
.process-timeline { flex-direction: column; }
.process-connector { width: 2px; height: clamp(0.5rem, 2vh, 1.5rem); }
}
slide-previews/ 目录,或询问用户是否保留)versions/v1_{project-slug}.html)images/README.md 已生成(列出所有占位符清单)演示文稿已完成!
项目目录:{project-slug}/
文件:{project-slug}.html
风格:{风格名}
页数:{数量}
版本:v1
图片状态:
- 已使用图片:{N} 张
- 占位符(待替换):{M} 个 → 查看 images/README.md 获取清单
将图片放入 images/ 文件夹并按序号命名(01.png、02.jpg...),
然后说「替换图片」即可自动替换占位符。
操作方式:
- 方向键(← →)或空格键翻页
- 鼠标滚轮 / 触摸滑动也可以
- 右侧圆点可跳转到指定页
- 支持无线翻页笔(PageDown/PageUp)
- 浏览器全屏:F11(Windows/Linux)或 Ctrl+Cmd+F(macOS),或直接按 `F` 键
版本管理:
- 修改时自动备份上一版到 versions/ 文件夹
- 说「回到 v{N}」可回滚到任意历史版本
- 查看 CHANGELOG.md 了解所有变更记录
自定义方式:
- 颜色:修改 :root 中的 CSS 变量
- 字体:修改 Google Fonts 链接
- 动画:修改 .reveal 的过渡时间
需要调整吗?
当用户要求修改现有演示时,输出格式:
已更新演示文稿!
文件:{project-slug}.html(v{N})
上一版备份:versions/v{N-1}_{project-slug}.html
本次修改:
- {变更 1}
- {变更 2}
需要继续调整吗?
/* 淡入 + 上滑(最常用) */
.reveal {
opacity: 0; transform: translateY(30px);
transition: opacity 0.6s var(--ease-out-expo), transform 0.6s var(--ease-out-expo);
}
.slide.visible .reveal { opacity: 1; transform: translateY(0); }
/* 子元素依次延迟 */
.reveal:nth-child(1) { transition-delay: 0.1s; }
.reveal:nth-child(2) { transition-delay: 0.2s; }
.reveal:nth-child(3) { transition-delay: 0.3s; }
.reveal:nth-child(4) { transition-delay: 0.4s; }
/* 缩放入场 */
.reveal-scale { opacity: 0; transform: scale(0.9); }
/* 模糊入场 */
.reveal-blur { opacity: 0; filter: blur(10px); }
/* 渐变网格 */
.gradient-bg {
background:
radial-gradient(ellipse at 20% 80%, rgba(120,0,255,0.3) 0%, transparent 50%),
radial-gradient(ellipse at 80% 20%, rgba(0,255,200,0.2) 0%, transparent 50%),
var(--bg-primary);
}
/* 噪点纹理 */
.noise-bg { background-image: url("data:image/svg+xml,..."); }
/* 网格图案 */
.grid-bg {
background-image:
linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 50px 50px;
}
| 氛围 | 动画特征 |
|---|---|
| 影院感/大气 | 慢淡入(1-1.5s)、大缩放过渡、暗背景 + 聚光效果 |
| 科技感/未来 | 霓虹发光、粒子系统、网格图案、等宽字体 |
| 活泼/友好 | 弹性缓动、大圆角、明亮/马卡龙色 |
| 专业/企业 | 快速微动(200-300ms)、无衬线字体、数据可视化 |
| 平静/极简 | 极慢动效、大留白、衬线字体 |
| 编辑/杂志 | 强排版层次、引用块、图文交错、网格破格布局 |
| 问题 | 检查 |
|---|---|
| 字体不加载 | 检查 Google Fonts URL,确认字体名匹配 |
| 动画不触发 | 确认 Intersection Observer 正在运行,.visible class 是否被添加 |
| Scroll snap 失效 | 确认 html 有 scroll-snap-type,每个 slide 有 scroll-snap-align |
| 移动端问题 | 768px 以下禁用重效果,测试触摸事件 |
| 性能问题 | 谨慎使用 will-change,优先用 transform 和 opacity 动画 |
| 中文字体闪烁 | 添加 font-display: swap 到 Google Fonts URL |
| 内容被裁切 | 不要在 .slide-content 上使用 max-height: 100%; overflow: hidden。字体变大后极易导致顶部标签或底部内容被遮挡。通过拆页解决溢出。 |
| 投屏字太小 | 检查 clamp 上限值。投屏建议 title ≥ 2.8rem / body ≥ 1.05rem。可在 <html> 上加 class="projection" 启用投屏参数集。 |
| 投屏颜色太浅 | 投影仪对比度低于显示器。--text-secondary 不应浅于 #3d3835,--text-muted 不应浅于 #6b6560。 |
| 大屏内容太宽 | .slide-content 加 max-width: min(100%, 1200px) 防止拉伸。 |
| 元素重叠 | 不要使用绝对的百分比或固定像素,使用 clamp() 和 grid-template-columns 的自适应分配(如 minmax() 或 clamp())防止文字和图表重叠。表格数据单元格(td)应加上 white-space: nowrap 防止换行。 |
<br> 换行不一致 | 不同视口宽度下 <br> 的位置语义会变。用 <span style="display:block">每行文本</span> 强制精确换行。 |
| 翻页笔无反应 | 确认 JS 监听了 PageDown / PageUp(不只是方向键)。 |