关于 frontend-slides
此技能已弃用,面向非设计人员,用于创建可在浏览器中播放的动画 HTML 演示文稿。它引导用户选择视觉风格,并生成每页适配视口的独立幻灯片。
主要功能
- 01单文件 HTML,内含 CSS 和 JavaScript
- 02通过视觉预览选择演示风格
- 03使用预设设置幻灯片样式
- 04幻灯片适配视口且不在页内滚动
- 05转换 PowerPoint 并改进 HTML 演示文稿
适用场景
- 01制作浏览器播放的产品推介演示
- 02将粗略笔记整理成演示文稿
- 03将 PowerPoint 文件转换为 HTML 幻灯片
- 04改进现有 HTML 演示文稿
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gooseworks-ai/ goose-skills --skill frontend-slides安装后在 Claude Code 中输入/frontend-slides确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdskill.meta.jsonSTYLE_PRESETS.md详细介绍
已弃用: 此技能已由
goose-graphics取代。请参阅skills/composites/goose-graphics/(使用npx goose-skills install goose-graphics安装)。幻灯片格式是新版技能支持的七种格式之一,并支持 36 种样式预设、图片来源选取和 PNG 导出。此技能将保留一个发布周期,之后移除。
前端幻灯片技能
创建零依赖、富有动画效果的 HTML 演示文稿,完全在浏览器中运行。此技能通过视觉探索(“展示,而非描述”)帮助非设计师找到自己偏好的美学风格,然后生成达到生产质量的幻灯片。
核心理念
- 零依赖 — 使用内联 CSS/JS 的单个 HTML 文件。不需要 npm 或构建工具。
- 展示,而非描述 — 人们只有亲眼看到,才知道自己想要什么。生成视觉预览,而非抽象选项。
- 独特设计 — 避免千篇一律的“AI 垃圾风”审美。每份演示文稿都应显得经过量身打造。
- 生产质量 — 代码应有完善注释、易于访问且性能良好。
- 适配视口(重要) — 每张幻灯片都必须恰好适配视口。幻灯片内绝不能滚动。这一点不可妥协。
重要:适配视口要求
本节对所有演示文稿均为强制要求。每张幻灯片在任何屏幕尺寸下都必须完整显示,无需滚动。
黄金法则
Each slide = exactly one viewport height (100vh/100dvh)
Content overflows? → Split into multiple slides or reduce content
Never scroll within a slide.
内容密度限制
为确保适配视口,每张幻灯片必须遵守以下限制:
| 幻灯片类型 | 内容上限 |
|---|---|
| 标题幻灯片 | 1 个标题 + 1 个副标题 + 可选的标语 |
| 内容幻灯片 | 1 个标题 + 4-6 个项目符号,或 1 个标题 + 2 个段落 |
| 功能网格 | 1 个标题 + 最多 6 张卡片(2x3 或 3x2 网格) |
| 代码幻灯片 | 1 个标题 + 最多 8-10 行代码 |
| 引言幻灯片 | 1 段引言(最多 3 行)+ 出处 |
| 图片幻灯片 | 1 个标题 + 1 张图片(高度最多为 60vh) |
如果内容超出这些限制 → 拆分成多张幻灯片
必需的 CSS 架构
每份演示文稿都必须包含以下用于适配视口的基础 CSS:
/* ===========================================
VIEWPORT FITTING: MANDATORY BASE STYLES
These styles MUST be included in every presentation.
They ensure slides fit exactly in the viewport.
=========================================== */
/* 1. Lock html/body to viewport */
html, body {
height: 100%;
overflow-x: hidden;
}
html {
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
}
/* 2. Each slide = exact viewport height */
.slide {
width: 100vw;
height: 100vh;
height: 100dvh; /* Dynamic viewport height for mobile browsers */
overflow: hidden; /* CRITICAL: Prevent ANY overflow */
scroll-snap-align: start;
display: flex;
flex-direction: column;
position: relative;
}
/* 3. Content container with flex for centering */
.slide-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
max-height: 100%;
overflow: hidden; /* Double-protection against overflow */
padding: var(--slide-padding);
}
/* 4. ALL typography uses clamp() for responsive scaling */
:root {
/* Titles scale from mobile to desktop */
--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 text */
--body-size: clamp(0.75rem, 1.5vw, 1.125rem);
--small-size: clamp(0.65rem, 1vw, 0.875rem);
/* Spacing scales with viewport */
--slide-padding: clamp(1rem, 4vw, 4rem);
--content-gap: clamp(0.5rem, 2vw, 2rem);
--element-gap: clamp(0.25rem, 1vw, 1rem);
}
/* 5. Cards/containers use viewport-relative max sizes */
.card, .container, .content-box {
max-width: min(90vw, 1000px);
max-height: min(80vh, 700px);
}
/* 6. Lists auto-scale with viewport */
.feature-list, .bullet-list {
gap: clamp(0.4rem, 1vh, 1rem);
}
.feature-list li, .bullet-list li {
font-size: var(--body-size);
line-height: 1.4;
}
/* 7. Grids adapt to available space */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
gap: clamp(0.5rem, 1.5vw, 1rem);
}
/* 8. Images constrained to viewport */
img, .image-container {
max-width: 100%;
max-height: min(50vh, 400px);
object-fit: contain;
}
/* ===========================================
RESPONSIVE BREAKPOINTS
Aggressive scaling for smaller viewports
=========================================== */
/* Short viewports (< 700px height) */
@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);
}
}
/* Very short viewports (< 600px height) */
@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);
}
/* Hide non-essential elements */
.nav-dots, .keyboard-hint, .decorative {
display: none;
}
}
/* Extremely short (landscape phones, < 500px height) */
@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);
}
}
/* Narrow viewports (< 600px width) */
@media (max-width: 600px) {
:root {
--title-size: clamp(1.25rem, 7vw, 2.5rem);
}
/* Stack grids vertically */
.grid {
grid-template-columns: 1fr;
}
}
/* ===========================================
REDUCED MOTION
Respect user preferences
=========================================== */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.2s !important;
}
html {
scroll-behavior: auto;
}
}
防溢出检查清单
生成任何演示文稿之前,请先确认:
- ✅ 每个
.slide都有height: 100vh; height: 100dvh; overflow: hidden; - ✅ 所有字体大小都使用
clamp(min, preferred, max) - ✅ 所有间距都使用
clamp()或视口单位 - ✅ 内容容器都有
max-height限制 - ✅ 图片都有
max-height: min(50vh, 400px)或类似限制 - ✅ 网格使用带有
minmax()的auto-fit实现自适应列数 - ✅ 针对 700px、600px、500px 高度设置断点
- ✅ 内容元素不使用固定像素高度
- ✅ 每张幻灯片的内容符合密度限制
内容无法适配时
如果内容过多:
应该:
- 拆分成多张幻灯片
- 减少项目符号(每张最多 5-6 个)
- 缩短文本(每个项目尽量控制在 1-2 行)
- 使用更短的代码片段
- 创建一张“续篇”幻灯片
不应该:
- 将字体缩小到可读范围以下
- 完全移除内边距或间距
- 允许任何滚动
- 为了塞进内容而硬挤
测试视口适配
生成后,建议用户在以下尺寸下进行测试:
- 桌面端:1920×1080、1440×900、1280×720
- 平板:1024×768、768×1024(竖屏)
- 移动端:375×667、414×896
- 横屏手机:667×375、896×414
阶段 0:检测模式
首先,确定用户的需求:
模式 A:新建演示文稿
- 用户想从头开始创建幻灯片
- 进入阶段 1(内容探索)
模式 B:PPT 转换
- 用户有要转换的 PowerPoint 文件(.ppt、.pptx)
- 进入阶段 4(PPT 提取)
模式 C:增强现有演示文稿
- 用户有 HTML 演示文稿,想要改进
- 读取现有文件,了解其结构,然后进行增强
阶段 1:内容探索(新建演示文稿)
设计之前,先了解内容。通过 AskUserQuestion 提问:
步骤 1.1:演示文稿背景
问题 1:用途
- 标题:“用途”
- 问题:“这份演示文稿的用途是什么?”
- 选项:
- “融资路演稿”——向投资者/客户推介创意、产品或公司
- “教学/教程”——讲解概念、操作指南或教育内容
- “会议演讲”——在活动、技术分享或主题演讲中发言
- “内部演示”——团队动态、战略会议或公司动态
问题 2:页数
- 标题:“长度”
- 问题:“大约需要多少张幻灯片?”
- 选项:
- “短篇(5-10 张)”——快速推介、闪电演讲
- “中篇(10-20 张)”——标准演示文稿
- “长篇(20 张以上)”——深入探讨、全面演讲
问题 3:内容
- 标题:“内容”
- 问题:“你已经准备好内容了吗?还是需要帮助梳理结构?”
- 选项:
- “内容已全部准备好”——只需要设计演示文稿
- “有一些初步笔记”——需要帮助整理成幻灯片
- “只有一个主题”——需要帮助创建完整大纲
如果用户已有内容,请让他们分享(文字、要点、图片等)。
阶段 2:风格探索(视觉体验)
重要:这是一个“展示,而非描述”的阶段。
大多数人无法用语言准确表达设计偏好。因此,我们不问“你想要极简风还是大胆风?”,而是生成迷你预览,让他们直接选择。
用户如何选择预设风格
用户可以通过两种方式选择风格:
选项 A:引导式探索(默认)
- 用户回答有关氛围的问题
- 技能根据他们的回答生成 3 个预览文件
- 用户在浏览器中查看预览并选出最喜欢的
- 这种方式最适合尚未确定具体风格的用户
选项 B:直接选择
- 如果用户已经知道自己想要什么,可以按名称指定预设风格
- 示例:“使用 Bold Signal 风格”或“我想要类似 Dark Botanical 的风格”
- 直接跳到阶段 3
可用预设:
| 预设 | 氛围 | 适用场景 |
|---|---|---|
| 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 | 文学感、深思熟虑 | 讲故事 |
步骤 2.0:选择风格
首先,询问用户想如何选择风格:
问题:风格选择方式
- 标题:「风格」
- 问题:「您想如何选择演示文稿的风格?」
- 选项:
- 「展示选项」— 根据我的需求生成 3 个预览(推荐大多数用户选择)
- 「我知道自己想要什么」— 让我直接从预设列表中选择
- 「使用客户品牌形象」— 应用客户的品牌颜色、字体和美学风格
如果选择「展示选项」 → 继续步骤 2.1(氛围选择)
如果选择「我知道自己想要什么」 → 显示预设选择器:
问题:选择预设
- 标题:「预设」
- 问题:「您想使用哪种风格?」
- 选项:
- 「Bold Signal」— 深色背景上的鲜艳卡片,自信且醒目
- 「Dark Botanical」— 优雅暗色风格,搭配柔和的抽象图形
- 「Notebook Tabs」— 编辑风格的纸张质感,搭配彩色分区标签
- 「Pastel Geometry」— 友好的柔和色调,搭配装饰性胶囊图形
(如果用户选择其中一个,则跳至阶段 3。如果他们想查看更多选项,则展示其他预设或继续引导式探索。)
如果选择「使用客户品牌形象」 → 加载客户视觉识别:
- 询问这份演示文稿面向哪位客户(或根据上下文推断)
- 检查是否存在
clients/<client-name>/brand/visual-identity.md - 如果文件存在: 阅读「Slide Preset」部分。将其中的 CSS
:root自定义属性用作演示文稿的主题变量,将其字体排版用作字体组合,将其字体加载<link>标签用于加载字体,并将其标志性元素作为装饰性 CSS 的设计指导。 - 如果文件不存在: 告诉用户:「尚未为 [client] 提取视觉识别信息。您想现在提取吗?请提供客户的网站 URL。」然后运行
visual-brand-extractor技能以生成文件,并继续操作。 - 加载客户预设后,跳至阶段 3。
步骤 2.1:氛围选择(引导式探索)
问题 1:感受
- 标题:「氛围」
- 问题:「您希望观众观看幻灯片时有什么感受?」
- 选项:
- 「印象深刻/充满信心」— 专业、值得信赖,让人觉得这个团队很有实力
- 「兴奋/充满活力」— 创新、大胆,展现未来感
- 「平静/专注」— 清晰、周到、易于理解
- 「受到启发/感动」— 富有情感、善于讲故事、令人难忘
- 多选:true(最多可选 2 项)
步骤 2.2:生成风格预览
根据用户选择的氛围,在临时目录中生成 3 个不同的风格预览,保存为迷你 HTML 文件。每个预览都应是一张标题幻灯片,并展示:
- 字体排版(字体选择、标题/正文层级)
- 调色板(背景色、强调色、文字颜色)
- 动画风格(元素的进入方式)
- 整体美学感受
可考虑的预览风格(根据氛围选择 3 种):
| 氛围 | 风格选项 |
|---|---|
| 印象深刻/充满信心 | "Bold Signal"、"Electric Studio"、"Dark Botanical" |
| 兴奋/充满活力 | "Creative Voltage"、"Neon Cyber"、"Split Pastel" |
| 平静/专注 | "Notebook Tabs"、"Paper & Ink"、"Swiss Modern" |
| 受到启发/感动 | "Dark Botanical"、"Vintage Editorial"、"Pastel Geometry" |
重要:切勿使用以下通用设计模式:
- 白色背景上的紫色渐变
- Inter、Roboto 或系统字体
- 标准蓝色作为主色
- 可预测的主视觉布局
应改用有特色的设计选择:
- 独特的字体组合(Clash Display、Satoshi、Cormorant Garamond、DM Sans 等)
- 风格鲜明且协调统一的配色主题
- 富有氛围感的背景(渐变、细腻的图案、层次感)
- 标志性的动画时刻
第 2.3 步:展示预览
在此目录中创建预览:.claude-design/slide-previews/
.claude-design/slide-previews/
├── style-a.html # First style option
├── style-b.html # Second style option
├── style-c.html # Third style option
└── assets/ # Any shared assets
每个预览文件都应:
- 独立完整(内嵌 CSS/JS)
- 只包含一张展示视觉风格的“标题幻灯片”
- 带有动画,以展示动效风格
- 约 50–100 行,不要制作完整演示文稿
向用户展示:
I've created 3 style previews for you to compare:
**Style A: [Name]** — [1 sentence description]
**Style B: [Name]** — [1 sentence description]
**Style C: [Name]** — [1 sentence description]
Open each file to see them in action:
- .claude-design/slide-previews/style-a.html
- .claude-design/slide-previews/style-b.html
- .claude-design/slide-previews/style-c.html
Take a look and tell me:
1. Which style resonates most?
2. What do you like about it?
3. Anything you'd change?
然后使用 AskUserQuestion:
问题:选择你的风格
- 标题:“风格”
- 问题:“你更喜欢哪种风格预览?”
- 选项:
- “风格 A:[名称]” — [简短描述]
- “风格 B:[名称]” — [简短描述]
- “风格 C:[名称]” — [简短描述]
- “混合元素” — 组合不同风格的元素
如果选择“混合元素”,请询问具体要求。
第 3 阶段:生成演示文稿
现在,根据以下内容生成完整的演示文稿:
- 第 1 阶段的内容
- 第 2 阶段的风格
文件结构
对于单个演示文稿:
presentation.html # Self-contained presentation
assets/ # Images, if any
对于包含多个演示文稿的项目:
[presentation-name].html
[presentation-name]-assets/
HTML 架构
所有演示文稿都遵循以下结构:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Presentation Title</title>
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=...">
<style>
/* ===========================================
CSS CUSTOM PROPERTIES (THEME)
Easy to modify: change these to change the whole look
=========================================== */
:root {
/* Colors */
--bg-primary: #0a0f1c;
--bg-secondary: #111827;
--text-primary: #ffffff;
--text-secondary: #9ca3af;
--accent: #00ffcc;
--accent-glow: rgba(0, 255, 204, 0.3);
/* Typography - MUST use clamp() for responsive scaling */
--font-display: 'Clash Display', sans-serif;
--font-body: 'Satoshi', sans-serif;
--title-size: clamp(2rem, 6vw, 5rem);
--subtitle-size: clamp(0.875rem, 2vw, 1.25rem);
--body-size: clamp(0.75rem, 1.2vw, 1rem);
/* Spacing - MUST use clamp() for responsive scaling */
--slide-padding: clamp(1.5rem, 4vw, 4rem);
--content-gap: clamp(1rem, 2vw, 2rem);
/* Animation */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--duration-normal: 0.6s;
}
/* ===========================================
BASE STYLES
=========================================== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
scroll-snap-type: y mandatory;
height: 100%;
}
body {
font-family: var(--font-body);
background: var(--bg-primary);
color: var(--text-primary);
overflow-x: hidden;
height: 100%;
}
/* ===========================================
SLIDE CONTAINER
CRITICAL: Each slide MUST fit exactly in viewport
- Use height: 100vh (NOT min-height)
- Use overflow: hidden to prevent scroll
- Content must scale with clamp() values
=========================================== */
.slide {
width: 100vw;
height: 100vh; /* EXACT viewport height - no scrolling */
height: 100dvh; /* Dynamic viewport height for mobile */
padding: var(--slide-padding);
scroll-snap-align: start;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
overflow: hidden; /* Prevent any content overflow */
}
/* Content wrapper that prevents overflow */
.slide-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
max-height: 100%;
overflow: hidden;
}
/* ===========================================
RESPONSIVE BREAKPOINTS
Adjust content for different screen sizes
=========================================== */
@media (max-height: 600px) {
:root {
--slide-padding: clamp(1rem, 3vw, 2rem);
--content-gap: clamp(0.5rem, 1.5vw, 1rem);
}
}
@media (max-width: 768px) {
:root {
--title-size: clamp(1.5rem, 8vw, 3rem);
}
}
@media (max-height: 500px) and (orientation: landscape) {
/* Extra compact for landscape phones */
:root {
--title-size: clamp(1.25rem, 5vw, 2rem);
--slide-padding: clamp(0.75rem, 2vw, 1.5rem);
}
}
/* ===========================================
ANIMATIONS
Trigger via .visible class (added by JS on scroll)
=========================================== */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: opacity var(--duration-normal) var(--ease-out-expo),
transform var(--duration-normal) var(--ease-out-expo);
}
.slide.visible .reveal {
opacity: 1;
transform: translateY(0);
}
/* Stagger children */
.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; }
/* ... more styles ... */
</style>
</head>
<body>
<div class="progress-bar"></div>
<nav class="nav-dots">
</nav>
<section class="slide title-slide">
<h1 class="reveal">Presentation Title</h1>
<p class="reveal">Subtitle or author</p>
</section>
<section class="slide">
<h2 class="reveal">Slide Title</h2>
<p class="reveal">Content...</p>
</section>
<script>
/* ===========================================
SLIDE PRESENTATION CONTROLLER
Handles navigation, animations, and interactions
=========================================== */
class SlidePresentation {
constructor() {
// ... initialization
}
// ... methods
}
// Initialize
new SlidePresentation();
</script>
</body>
</html>
必需的 JavaScript 功能
每个演示文稿都应包含:
-
SlidePresentation 类 — 主控制器
- 键盘导航(方向键、空格键)
- 触控/滑动支持
- 鼠标滚轮导航
- 更新进度条
- 导航圆点
-
Intersection Observer — 用于滚动触发的动画
- 幻灯片进入视口时添加
.visible类 - 高效触发 CSS 动画
- 幻灯片进入视口时添加
-
可选增强功能(根据风格选择):
- 带轨迹效果的自定义光标
- 粒子系统背景(canvas)
- 视差效果
- 悬停时的 3D 倾斜效果
- 磁性按钮
- 计数器动画
代码质量要求
注释: 每个部分都应有清晰的注释,说明:
- 功能是什么
- 为何需要此部分
- 如何修改
/* ===========================================
CUSTOM CURSOR
Creates a stylized cursor that follows mouse with a trail effect.
- Uses lerp (linear interpolation) for smooth movement
- Grows larger when hovering over interactive elements
=========================================== */
class CustomCursor {
constructor() {
// ...
}
}
无障碍性:
- 语义化 HTML(
<section>、<nav>、<main>) - 支持键盘导航
- 在需要时添加 ARIA 标签
- 支持减少动态效果设置
@media (prefers-reduced-motion: reduce) {
.reveal {
transition: opacity 0.3s ease;
transform: none;
}
}
响应式设计与视口适配(至关重要):
完整的 CSS 和指南请参阅上方的“至关重要:视口适配要求”部分。
快速参考:
- 每个
.slide都必须设置height: 100vh; height: 100dvh; overflow: hidden; - 所有排版和间距都必须使用
clamp() - 遵守内容密度限制(最多 4–6 个项目符号、最多 6 张卡片等)
- 为以下高度添加断点:700px、600px、500px
- 内容无法容纳时 → 拆分为多张幻灯片,绝不滚动
第 4 阶段:转换 PPT
转换 PowerPoint 文件时:
第 4.1 步:提取内容
使用 Python 和 python-pptx 提取:
from pptx import Presentation
from pptx.util import Inches, Pt
import json
import os
import base64
def extract_pptx(file_path, output_dir):
"""
Extract all content from a PowerPoint file.
Returns a JSON structure with slides, text, and images.
"""
prs = Presentation(file_path)
slides_data = []
# Create assets directory
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:
# Extract title
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
})
# Extract images
if shape.shape_type == 13: # Picture
image = shape.image
image_bytes = image.blob
image_ext = image.ext
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_bytes)
slide_data['images'].append({
'path': f"assets/{image_name}",
'width': shape.width,
'height': shape.height
})
# Extract notes
if slide.has_notes_slide:
notes_frame = slide.notes_slide.notes_text_frame
slide_data['notes'] = notes_frame.text
slides_data.append(slide_data)
return slides_data
第 4.2 步:确认内容结构
向用户展示提取的内容:
I've extracted the following from your PowerPoint:
**Slide 1: [Title]**
- [Content summary]
- Images: [count]
**Slide 2: [Title]**
- [Content summary]
- Images: [count]
...
All images have been saved to the assets folder.
Does this look correct? Should I proceed with style selection?
第 4.3 步:选择风格
结合提取的内容,继续第 2 阶段(风格探索)。
第 4.4 步:生成 HTML
将提取的内容转换为所选风格,同时保留:
- 所有文本内容
- 所有图片(从 assets 文件夹引用)
- 幻灯片顺序
- 任何演讲者备注(作为 HTML 注释或单独的文件)
第 5 阶段:交付
最终输出
演示文稿完成后:
-
清理临时文件
- 如果
.claude-design/slide-previews/存在,则将其删除
- 如果
-
打开演示文稿
- 使用
open [filename].html在浏览器中打开
- 使用
-
提供摘要
Your presentation is ready!
📁 File: [filename].html
🎨 Style: [Style Name]
📊 Slides: [count]
**Navigation:**
- Arrow keys (← →) or Space to navigate
- Scroll/swipe also works
- Click the dots on the right to jump to a slide
**To customize:**
- Colors: Look for `:root` CSS variables at the top
- Fonts: Change the Fontshare/Google Fonts link
- Animations: Modify `.reveal` class timings
Would you like me to make any adjustments?
风格参考:效果 → 感受对应关系
使用此指南,让动画效果符合预期感受:
戏剧感 / 电影感
- 缓慢淡入(1–1.5 秒)
- 大幅缩放过渡(0.9 → 1)
- 深色背景与聚光灯效果
- 视差滚动
- 全幅图像
科技感 / 未来感
- 霓虹发光效果(使用强调色的 box-shadow)
- 粒子系统(canvas 背景)
- 网格图案
- 使用等宽字体作为点缀
- 故障或文字扰动效果
- 青色、洋红色、电光蓝配色
活泼 / 友好
- 弹跳缓动(弹簧物理效果)
- 圆角(大圆角)
- 柔和或鲜艳的色彩
- 漂浮/上下摆动动画
- 手绘或插画元素
专业 / 企业风
- 微妙、快速的动画(200-300ms)
- 简洁的无衬线字体
- 海军蓝、石板灰或炭灰色背景
- 精准的间距和对齐
- 极少的装饰元素
- 聚焦数据可视化
平和 / 极简
- 非常缓慢、微妙的动态效果
- 大量留白
- 柔和的配色
- 衬线字体
- 宽裕的内边距
- 以内容为中心,不受干扰
编辑 / 杂志风
- 鲜明的字体层级
- 引言和提示框
- 图文交互
- 打破网格的布局
- 衬线标题、无衬线正文字体
- 黑白配色,搭配一种强调色
动画模式参考
入场动画
/* Fade + Slide Up (most common) */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: opacity 0.6s var(--ease-out-expo),
transform 0.6s var(--ease-out-expo);
}
.visible .reveal {
opacity: 1;
transform: translateY(0);
}
/* Scale In */
.reveal-scale {
opacity: 0;
transform: scale(0.9);
transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
}
/* Slide from Left */
.reveal-left {
opacity: 0;
transform: translateX(-50px);
transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
}
/* Blur In */
.reveal-blur {
opacity: 0;
filter: blur(10px);
transition: opacity 0.8s, filter 0.8s var(--ease-out-expo);
}
背景效果
/* Gradient Mesh */
.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 Texture */
.noise-bg {
background-image: url("data:image/svg+xml,..."); /* Inline SVG noise */
}
/* Grid Pattern */
.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;
}
交互效果
/* 3D Tilt on Hover */
class TiltEffect {
constructor(element) {
this.element = element;
this.element.style.transformStyle = 'preserve-3d';
this.element.style.perspective = '1000px';
this.bindEvents();
}
bindEvents() {
this.element.addEventListener('mousemove', (e) => {
const rect = this.element.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width - 0.5;
const y = (e.clientY - rect.top) / rect.height - 0.5;
this.element.style.transform = `
rotateY(${x * 10}deg)
rotateX(${-y * 10}deg)
`;
});
this.element.addEventListener('mouseleave', () => {
this.element.style.transform = 'rotateY(0) rotateX(0)';
});
}
}
故障排除
常见问题
字体未加载:
- 检查 Fontshare/Google Fonts URL
- 确保 CSS 中的字体名称匹配
动画未触发:
- 确认 Intersection Observer 正在运行
- 检查是否已添加
.visible类
滚动捕捉不起作用:
- 确保在 html/body 上设置了
scroll-snap-type - 每张幻灯片都需要
scroll-snap-align: start
移动设备问题:
- 在 768px 断点处禁用开销较大的效果
- 测试触摸事件
- 减少粒子数量或禁用画布
性能问题:
- 谨慎使用
will-change - 优先使用
transform和opacity动画 - 对滚动 / 鼠标移动处理程序进行节流
相关技能
- learn — 为演示文稿生成 FORZARA.md 文档
- frontend-design — 用于制作超出幻灯片范围的复杂交互页面
- design-and-refine:design-lab — 用于迭代组件设计
示例会话流程
- 用户:「我想为我的 AI 初创公司制作一份融资演示文稿」
- 技能询问用途、长度和内容
- 用户分享要点和关键信息
- 技能询问期望的感受(印象深刻 + 兴奋)
- 技能生成 3 个风格预览
- 用户选择风格 B(霓虹赛博风),并要求使用更暗的背景
- 技能生成包含所有幻灯片的完整演示文稿
- 技能在浏览器中打开演示文稿
- 用户要求调整特定幻灯片
- 交付最终演示文稿
转换会话流程
- 用户:「将我的 slides.pptx 转换为网页演示文稿」
- 技能从 PPT 中提取内容和图像
- 技能与用户确认提取的内容
- 技能询问期望的感受 / 风格
- 技能生成风格预览
- 用户选择一种风格
- 技能生成保留原有资源的 HTML 演示文稿
- 交付最终演示文稿