| name | frontend-slides |
| description | Create stunning, animation-rich HTML presentations from scratch or by converting PowerPoint files. Use when the user wants to build a presentation, convert a PPT/PPTX to web, or create slides for a talk/pitch. Helps non-designers discover their aesthetic through visual exploration rather than abstract choices. |
| requires_hitl_plan | false |
Frontend Slides Skill
Create zero-dependency, animation-rich HTML presentations that run entirely in the browser. This skill helps non-designers discover their preferred aesthetic through visual exploration ("show, don't tell"), then generates production-quality slide decks.
Core Philosophy
- Zero Dependencies — Single HTML files with inline CSS/JS. No npm, no build tools.
- Show, Don't Tell — People don't know what they want until they see it. Generate visual previews, not abstract choices.
- Distinctive Design — Avoid generic "AI slop" aesthetics. Every presentation should feel custom-crafted.
- Production Quality — Code should be well-commented, accessible, and performant.
- Viewport Fitting (CRITICAL) — Every slide MUST fit exactly within the viewport. No scrolling within slides, ever. This is non-negotiable.
CRITICAL: Viewport Fitting Requirements
This section is mandatory for ALL presentations. Every slide must be fully visible without scrolling on any screen size.
The Golden Rule
Each slide = exactly one viewport height (100vh/100dvh)
Content overflows? → Split into multiple slides or reduce content
Never scroll within a slide.
Content Density Limits
To guarantee viewport fitting, enforce these limits per slide:
| Slide Type | Maximum Content |
|---|
| Title slide | 1 heading + 1 subtitle + optional tagline |
| Content slide | 1 heading + 4-6 bullet points OR 1 heading + 2 paragraphs |
| Feature grid | 1 heading + 6 cards maximum (2x3 or 3x2 grid) |
| Code slide | 1 heading + 8-10 lines of code maximum |
| Quote slide | 1 quote (max 3 lines) + attribution |
| Image slide | 1 heading + 1 image (max 60vh height) |
If content exceeds these limits → Split into multiple slides
Required CSS Architecture
Every presentation MUST include this base CSS for viewport fitting:
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-height: 100%;
overflow: hidden;
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);
}
.card, .container, .content-box {
max-width: min(90vw, 1000px);
max-height: min(80vh, 700px);
}
.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;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
gap: clamp(0.5rem, 1.5vw, 1rem);
}
img, .image-container {
max-width: 100%;
max-height: min(50vh, 400px);
object-fit: contain;
}
@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;
}
}
Overflow Prevention Checklist
Before generating any presentation, mentally verify:
- ✅ Every
.slide has height: 100vh; height: 100dvh; overflow: hidden;
- ✅ All font sizes use
clamp(min, preferred, max)
- ✅ All spacing uses
clamp() or viewport units
- ✅ Content containers have
max-height constraints
- ✅ Images have
max-height: min(50vh, 400px) or similar
- ✅ Grids use
auto-fit with minmax() for responsive columns
- ✅ Breakpoints exist for heights: 700px, 600px, 500px
- ✅ No fixed pixel heights on content elements
- ✅ Content per slide respects density limits
When Content Doesn't Fit
If you find yourself with too much content:
DO:
- Split into multiple slides
- Reduce bullet points (max 5-6 per slide)
- Shorten text (aim for 1-2 lines per bullet)
- Use smaller code snippets
- Create a "continued" slide
- When adding images to existing slides: Move image to new slide or reduce other content first
DON'T:
- Reduce font size below readable limits
- Remove padding/spacing entirely
- Allow any scrolling
- Cram content to fit
- Add images without checking if existing content already fills the viewport
Testing Viewport Fit
After generating, recommend the user test at these sizes:
- Desktop: 1920×1080, 1440×900, 1280×720
- Tablet: 1024×768, 768×1024 (portrait)
- Mobile: 375×667, 414×896
- Landscape phone: 667×375, 896×414
Phase 0: Detect Mode
First, determine what the user wants:
Mode A: New Presentation
- User wants to create slides from scratch
- Proceed to Phase 1 (Content Discovery)
Mode B: PPT Conversion
- User has a PowerPoint file (.ppt, .pptx) to convert
- Proceed to Phase 4 (PPT Extraction)
Mode C: Existing Presentation Enhancement
- User has an HTML presentation and wants to improve it
- Read the existing file, understand the structure, then enhance
- CRITICAL: When modifying existing slides, ALWAYS ensure viewport fitting is maintained
Mode C: Critical Modification Rules
When enhancing existing presentations, follow these mandatory rules:
1. Before Adding Any Content:
- Read the current slide structure and count existing elements
- Check against content density limits (see table above)
- Calculate if the new content will fit within viewport constraints
2. When Adding Images (MOST COMMON ISSUE):
- Images must have
max-height: min(50vh, 400px) or similar viewport constraint
- Check if current slide already has maximum content (1 heading + 1 image)
- If adding an image to a slide with existing content → Split into two slides
- Example: If slide has heading + 4 bullets, and user wants to add an image:
- DON'T: Cram image onto same slide
- DO: Create new slide with heading + image, keep bullets on original slide
- OR: Reduce bullets to 2-3 and add image with proper constraints
3. When Adding Text Content:
- Max 4-6 bullet points per slide
- Max 2 paragraphs per slide
- If adding content exceeds limits → Split into multiple slides or create a continuation slide
4. Required Checks After ANY Modification:
✅ Does the slide have `overflow: hidden` on `.slide` class?
✅ Are all new elements using `clamp()` for font sizes?
✅ Do new images have viewport-relative max-height?
✅ Does total content respect density limits?
✅ Will this fit on a 1280×720 screen? On mobile portrait?
5. Proactive Reorganization (NOT Optional):
When you detect that modifications will cause overflow:
- Automatically split content across slides — Don't wait for user to ask
- Inform user: "I've reorganized the content across 2 slides to ensure proper viewport fitting"
- Use "continued" pattern for split content (e.g., "Key Features" → "Key Features (Continued)")
6. Testing After Modifications:
Mentally verify the modified slide at these viewport sizes:
- Desktop: 1280×720 (smallest common)
- Tablet portrait: 768×1024
- Mobile: 375×667
If in doubt → Split the content. Never allow scrolling within a slide.
MANDATORY INTERRUPT CHECKLIST & STRICT A2UI CONTRACT
Every new-presentation run MUST emit ALL of these request_clarification interrupts before the final HTML is generated. Under our strict A2UI (Agent-to-User Interface) contract, each required gate must call request_clarification with a valid, non-empty payload containing structured gate metadata in context_json (and display_payload/displayPayload).
Strict Contract Rules:
- Tool Calls Required: Each gate MUST call
request_clarification. You cannot skip or advance past required human-input gates using assistant text prose alone.
- Include Gate Metadata: Every call must include
gateId and expected expectedComponent in its context_json payload.
- No Prose-Only Forms: Prose may describe the outline or previews, but it must not be the only request for input. A UI form only exists when the tool call is emitted and the run stops.
- Execution Stops: The run must stop immediately after the interrupt.
- No Synthetic Fallback: If the model turn ends with no tool calls while the active gate requires input, the system will loop back with a strict instruction to call
request_clarification. If it still fails, the run fails with a clear contract error.
Gate Definition Table:
| Gate ID | Step | expectedComponent | Description & Purpose |
|---|
presentation_context | Step 1.1 | clarification_form | Purpose, length, content, images, editing |
outline_confirmation | Step 1.2 | clarification_form | Slide-by-slide structure & image assignments |
style_path_selection | Step 2.0 | clarification_form | Guided Exploration vs Direct Preset Pick |
mood_or_preset_selection | Step 2.1 | clarification_form | Vibe check / Mood selection or Direct Preset chooser |
style_preview_selection | Step 2.3 | style_preview_chooser | Interactive visual style preview selection |
You may NOT skip any gate. Each gate requires an actual request_clarification tool call that pauses the run. Writing "please confirm above" or "select from the form" as prose does NOT create a form — it just prints dead text.
Phase 1: Content Discovery (New Presentations)
Before designing, understand the content. Use request_clarification for every decision gate in this skill. Do not ask these questions in plain chat prose, and do not continue to later phases after a clarification tool call until the user has resumed the run.
Hard stop for UI forms: If you are about to write phrases like "select from the form above", "choose from the options above", "please confirm in the UI", "confirm using the form", or "pick a vibe/style" in normal assistant text, STOP IMMEDIATELY and call request_clarification instead. A UI form only exists when this tool emits an interrupt; prose alone will not create one. This is the single most common failure mode of this skill.
Step 1.1: Presentation Context + Images (Single Form)
IMPORTANT: Ask ALL 5 questions in a single request_clarification call so the user can fill everything out at once before submitting. Pass them via questions_json, not as a prose list in the chat response.
Question 1: Purpose
- Header: "Purpose"
- Question: "What is this presentation for?"
- Options:
- "Pitch deck" — Selling an idea, product, or company to investors/clients
- "Teaching/Tutorial" — Explaining concepts, how-to guides, educational content
- "Conference talk" — Speaking at an event, tech talk, keynote
- "Internal presentation" — Team updates, strategy meetings, company updates
Question 2: Slide Count
- Header: "Length"
- Question: "Approximately how many slides?"
- Options:
- "Short (5-10)" — Quick pitch, lightning talk
- "Medium (10-20)" — Standard presentation
- "Long (20+)" — Deep dive, comprehensive talk
Question 3: Content
- Header: "Content"
- Question: "Do you have the content ready, or do you need help structuring it?"
- Options:
- "I have all content ready" — Just need to design the presentation
- "I have rough notes" — Need help organizing into slides
- "I have a topic only" — Need help creating the full outline
Question 4: Images
- Header: "Images"
- Question: "Do you have images to include? Select 'No images' or select Other and type/paste your image folder path."
- Options:
- "No images" — Text-only presentation (use CSS-generated visuals instead)
- "./assets" — Use the
assets/ folder in the current project
The user can select "Other" to type or paste any custom folder path (e.g. ~/Desktop/screenshots). This way the image folder path is collected in the same form — no extra round-trip.
Question 5: Inline Editing
- Header: "Editing"
- Question: "Do you need to edit text directly in the browser after generation?"
- Options:
- "Yes (Recommended)" — Can edit text in-browser, auto-save to localStorage, export file
- "No" — Presentation only, keeps file smaller
Remember the user's choice — it determines whether edit-related HTML/CSS/JS is included in Phase 3.
Exact tool call parameters (Gate 1):
request_ui(
component="clarification.form",
props_json='''{
"title": "Presentation Setup",
"description": "Please configure the basic settings for your presentation.",
"questions": [
{
"id": "purpose",
"header": "Purpose",
"question": "What is this presentation for?",
"options": [
{"id": "pitch", "label": "Pitch deck", "value": "Pitch deck"},
{"id": "tutorial", "label": "Teaching/Tutorial", "value": "Teaching/Tutorial"},
{"id": "talk", "label": "Conference talk", "value": "Conference talk"},
{"id": "internal", "label": "Internal presentation", "value": "Internal presentation"}
]
},
{
"id": "length",
"header": "Length",
"question": "Approximately how many slides?",
"options": [
{"id": "short", "label": "Short (5-10)", "value": "Short (5-10)"},
{"id": "medium", "label": "Medium (10-20)", "value": "Medium (10-20)"},
{"id": "long", "label": "Long (20+)", "value": "Long (20+)"}
]
},
{
"id": "content",
"header": "Content",
"question": "Do you have the content ready, or do you need help structuring it?",
"options": [
{"id": "ready", "label": "I have all content ready", "value": "I have all content ready"},
{"id": "notes", "label": "I have rough notes", "value": "I have rough notes"},
{"id": "topic", "label": "I have a topic only", "value": "I have a topic only"}
]
},
{
"id": "images",
"header": "Images",
"question": "Do you have images to include?",
"options": [
{"id": "no-images", "label": "No images", "value": "No images"},
{"id": "assets", "label": "./assets", "value": "./assets"}
]
},
{
"id": "editing",
"header": "Editing",
"question": "Do you need to edit text directly in the browser after generation?",
"options": [
{"id": "edit-yes", "label": "Yes (Recommended)", "value": "Yes (Recommended)"},
{"id": "edit-no", "label": "No", "value": "No"}
]
}
]
}''',
context_json='''{
"skill": "frontend-slides"
}''',
gate_id="presentation_context",
required=True,
resume_mode="submit"
)
If user has content, ask them to share it (text, bullet points, images, etc.).
Step 1.2: Image Evaluation
User-provided assets are important visual anchors — but not every asset is necessarily usable. The first step is always to evaluate. After evaluation, the curated assets become additional context that shapes how the presentation is built. This is a co-design process: text content + curated visuals together inform the slide structure from the start, not a post-hoc "fit images in after the fact."
If user selected "No images" → Skip the entire image pipeline. Proceed directly to Phase 2 (Style Discovery) and Phase 3 (Generate Presentation) using text content only. The presentation will use CSS-generated visuals (gradients, shapes, patterns, typography) for visual interest — this is the original behavior and produces fully polished results without any images.
If user provides an image folder:
- Scan the folder — Use
ls to list all image files (.png, .jpg, .jpeg, .gif, .svg, .webp)
- View each image — Use the Read tool to see what each image contains (Claude is multimodal)
- Evaluate each image — For each image, assess:
- Filename and dimensions
- What it shows (screenshot, logo, chart, diagram, photo)
- Usability: Is the image clear, relevant to the presentation topic, and high enough quality? Mark as
USABLE or NOT USABLE (with reason: blurry, irrelevant, broken, etc.)
- Content signal: What feature or concept does this image represent? (e.g., "chat_ui.png" → "conversational interface feature")
- Shape: square, landscape, portrait, circular
- Dominant colors (important for style compatibility later)
- Present the evaluation and proposed slide outline to the user — Show which images are usable and which are not, with reasons. Then show the proposed slide outline with image assignments.
Co-design: curated assets inform the outline
After evaluation, the usable images become context for planning the slide structure alongside text content. This is not "plan slides then add images" — it's designing the presentation around both text and visuals from the start:
- 3 usable product screenshots → plan 3 feature slides, each anchored by one screenshot
- 1 usable logo → title slide and/or closing slide
- 1 usable architecture diagram → dedicated "How It Works" slide
- 1 blurry/irrelevant image → excluded, with explanation to user
This means curated images are factored in before style selection (Phase 2) and before HTML generation (Phase 3). They are co-equal context in the design process.
- Confirm outline via
request_clarification (MANDATORY — Gate 2 in the interrupt checklist):
⚠️ THIS IS THE MOST COMMONLY SKIPPED GATE. After presenting the outline and image evaluation in your assistant text, your VERY NEXT ACTION must be a request_clarification tool call. Do NOT end the turn with prose like "Please confirm the outline above" — that creates dead text with no interactive form.
At this point, do not write a normal assistant message that says "Next Steps", "use the forms in the sidebar", "confirm the outline", "confirm using the form above", or "choose your style discovery method". Those words are only valid inside the structured request_clarification payload. If you have an outline ready, your next action must be the tool call below, then stop.
Exact tool call parameters (Gate 2):
request_clarification(
title="Outline Confirmation",
description="Review the proposed slide outline and image assignments above.",
questions_json='''[
{
"id": "outline",
"header": "Outline",
"question": "Does this slide outline and image selection look right?",
"options": [
{"id": "confirm", "label": "Looks good, proceed", "value": "Looks good, proceed", "description": "Move on to style selection"},
{"id": "adjust-images", "label": "Adjust images", "value": "Adjust images", "description": "I want to change which images go where"},
{"id": "adjust-outline", "label": "Adjust outline", "value": "Adjust outline", "description": "I want to change the slide structure"}
]
}
]''',
allow_freeform=true,
context_json='''{
"skill": "frontend-slides",
"gateId": "outline_confirmation",
"uiContract": "a2ui",
"expectedComponent": "clarification_form"
}''',
submit_label="Continue"
)
After this tool call, STOP. Do not move into style selection or preview generation until the user responds. If you find yourself writing "After confirmation, we will move to Style Discovery" — that means you forgot to call request_clarification. Go back and call it.
Phase 2: Style Discovery (Visual Exploration)
CRITICAL: This is the "show, don't tell" phase.
Most people can't articulate design preferences in words. Instead of asking "do you want minimalist or bold?", we generate mini-previews and let them react.
How Users Choose Presets
Users can select a style in two ways:
Option A: Guided Discovery (Default)
- User answers mood questions
- Skill generates 3 preview files based on their answers
- User views previews in browser and picks their favorite
- This is best for users who don't have a specific style in mind
Option B: Direct Selection
- If user already knows what they want, they can request a preset by name
- Example: "Use the Bold Signal style" or "I want something like Dark Botanical"
- Skip to Phase 3 immediately
Available Presets:
| Preset | Vibe | Best For |
|---|
| Bold Signal | Confident, high-impact | Pitch decks, keynotes |
| Electric Studio | Clean, professional | Agency presentations |
| Creative Voltage | Energetic, retro-modern | Creative pitches |
| Dark Botanical | Elegant, sophisticated | Premium brands |
| Notebook Tabs | Editorial, organized | Reports, reviews |
| Pastel Geometry | Friendly, approachable | Product overviews |
| Split Pastel | Playful, modern | Creative agencies |
| Vintage Editorial | Witty, personality-driven | Personal brands |
| Neon Cyber | Futuristic, techy | Tech startups |
| Terminal Green | Developer-focused | Dev tools, APIs |
| Swiss Modern | Minimal, precise | Corporate, data |
| Paper & Ink | Literary, thoughtful | Storytelling |
Step 2.0: Style Path Selection
First, ask how the user wants to choose their style using request_clarification:
Question: Style Selection Method
- Header: "Style"
- Question: "How would you like to choose your presentation style?"
- Options:
- "Show me options" — Generate 3 previews based on my needs (recommended for most users)
- "I know what I want" — Let me pick from the preset list directly
Exact tool call parameters (Gate 3):
request_clarification(
title="Choose Style Selection Method",
description="Select how you'd like to decide on the presentation's design.",
questions_json='''[
{
"id": "style_path",
"header": "Style Selection Method",
"question": "How would you like to choose your presentation style?",
"options": [
{"id": "guided", "label": "Show me options", "value": "Show me options", "description": "Generate 3 previews based on my needs (recommended)"},
{"id": "direct", "label": "I know what I want", "value": "I know what I want", "description": "Let me pick from the preset list directly"}
]
}
]''',
context_json='''{
"skill": "frontend-slides",
"gateId": "style_path_selection",
"uiContract": "a2ui",
"expectedComponent": "clarification_form"
}''',
submit_label="Continue"
)
If "Show me options" → Continue to Step 2.1 (Mood Selection)
If "I know what I want" → Show preset picker via a second request_clarification call:
Question: Pick a Preset
- Header: "Preset"
- Question: "Which style would you like to use?"
- Options:
- "Bold Signal" — Vibrant card on dark, confident and high-impact
- "Dark Botanical" — Elegant dark with soft abstract shapes
- "Notebook Tabs" — Editorial paper look with colorful section tabs
- "Pastel Geometry" — Friendly pastels with decorative pills
(If user picks one, skip to Phase 3. If they want to see more options, show additional presets or proceed to guided discovery.)
Exact tool call parameters if picking preset directly (Gate 4):
request_clarification(
title="Select Style Preset",
description="Pick one of our premium presentation styles directly.",
questions_json='''[
{
"id": "preset",
"header": "Preset",
"question": "Which style would you like to use?",
"options": [
{"id": "bold", "label": "Bold Signal", "value": "Bold Signal", "description": "Vibrant card on dark, confident and high-impact"},
{"id": "botanical", "label": "Dark Botanical", "value": "Dark Botanical", "description": "Elegant dark with soft abstract shapes"},
{"id": "notebook", "label": "Notebook Tabs", "value": "Notebook Tabs", "description": "Editorial paper look with colorful section tabs"},
{"id": "pastel", "label": "Pastel Geometry", "value": "Pastel Geometry", "description": "Friendly pastels with decorative pills"}
]
}
]''',
context_json='''{
"skill": "frontend-slides",
"gateId": "mood_or_preset_selection",
"uiContract": "a2ui",
"expectedComponent": "clarification_form"
}''',
submit_label="Use selected style"
)
Step 2.1: Mood Selection (Guided Discovery)
Use request_clarification for this step as well. This is a structured chooser, not a prose question.
Question 1: Feeling
- Header: "Vibe"
- Question: "What feeling should the audience have when viewing your slides?"
- Options:
- "Impressed/Confident" — Professional, trustworthy, this team knows what they're doing
- "Excited/Energized" — Innovative, bold, this is the future
- "Calm/Focused" — Clear, thoughtful, easy to follow
- "Inspired/Moved" — Emotional, storytelling, memorable
- multiSelect: true (can choose up to 2)
Call request_clarification immediately at this step. Use questions_json, set multi_select=true, allow_freeform=false, and submit_label="Generate style previews". Do not say "select the feeling from the form above" unless the tool call has actually happened and the run is stopping on the interrupt.
Exact tool call parameters for Guided Mood Selection (Gate 4):
request_clarification(
title="Vibe & Mood Selection",
description="Tell us about the desired vibe for this presentation.",
questions_json='''[
{
"id": "mood",
"header": "Vibe",
"question": "What feeling should the audience have when viewing your slides?",
"options": [
{"id": "impressed", "label": "Impressed/Confident", "value": "Impressed/Confident"},
{"id": "excited", "label": "Excited/Energized", "value": "Excited/Energized"},
{"id": "calm", "label": "Calm/Focused", "value": "Calm/Focused"},
{"id": "inspired", "label": "Inspired/Moved", "value": "Inspired/Moved"}
]
}
]''',
multi_select=true,
context_json='''{
"skill": "frontend-slides",
"gateId": "mood_or_preset_selection",
"uiContract": "a2ui",
"expectedComponent": "clarification_form"
}''',
submit_label="Generate style previews"
)
Step 2.2: Generate Style Previews
Based on their mood selection, generate 3 distinct style previews as mini HTML files in a temporary directory. Each preview should be a single title slide showing:
- Typography (font choices, heading/body hierarchy)
- Color palette (background, accent, text colors)
- Animation style (how elements enter)
- Overall aesthetic feel
Preview Styles to Consider (pick 3 based on mood):
| Mood | Style Options |
|---|
| Impressed/Confident | "Bold Signal", "Electric Studio", "Dark Botanical" |
| Excited/Energized | "Creative Voltage", "Neon Cyber", "Split Pastel" |
| Calm/Focused | "Notebook Tabs", "Paper & Ink", "Swiss Modern" |
| Inspired/Moved | "Dark Botanical", "Vintage Editorial", "Pastel Geometry" |
IMPORTANT: Never use these generic patterns:
- Purple gradients on white backgrounds
- Inter, Roboto, or system fonts
- Standard blue primary colors
- Predictable hero layouts
Instead, use distinctive choices:
- Unique font pairings (Clash Display, Satoshi, Cormorant Garamond, DM Sans, etc.)
- Cohesive color themes with personality
- Atmospheric backgrounds (gradients, subtle patterns, depth)
- Signature animation moments
Step 2.3: Present Previews
Create the previews in: .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
Each preview file should be:
- Self-contained (inline CSS/JS)
- A single "title slide" showing the aesthetic
- Animated to demonstrate motion style
- ~50-100 lines, not a full presentation
Logo in previews (if available): If the user provided images in Step 1.2 and a logo was identified as USABLE, embed it (base64) into each of the 3 style previews. This creates a "wow moment" — the user sees their own brand identity styled three different ways, making the choice feel personal rather than abstract. Apply any necessary processing (e.g., circular crop) per-style so each preview shows the logo as it would actually appear in the final presentation. If no logo was provided, generate previews without one — this is fine.
Present to user:
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 at the three files, then pause with `request_clarification` so the user can choose in the UI instead of replying in free text.
Then use request_clarification with preview metadata so HelpUDoc can render a thumbnail chooser window. Do not only mention a "Choose Your Presentation Style" window in prose; the window exists only when this interrupt is emitted.
Required tool call shape:
title="Choose Your Presentation Style"
description="Preview each direction, then choose the one you want me to use for the full deck."
allow_freeform=false
multi_select=false
submit_label="Use selected style"
options_json with stable ids and values:
{"id":"style-a","label":"Style A: [Name]","value":"Style A: [Name]","description":"[Brief description]"}
{"id":"style-b","label":"Style B: [Name]","value":"Style B: [Name]","description":"[Brief description]"}
{"id":"style-c","label":"Style C: [Name]","value":"Style C: [Name]","description":"[Brief description]"}
{"id":"mix-elements","label":"Mix elements","value":"Mix elements","description":"Combine aspects from different styles"}
context_json must include:
{
"skill": "frontend-slides",
"gateId": "style_preview_selection",
"chooser": "style-previews",
"uiContract": "a2ui",
"expectedComponent": "style_preview_chooser",
"stylePreviews": [
{
"id": "style-a",
"label": "Style A: [Name]",
"description": "[Brief description]",
"path": ".claude-design/slide-previews/style-a.html"
},
{
"id": "style-b",
"label": "Style B: [Name]",
"description": "[Brief description]",
"path": ".claude-design/slide-previews/style-b.html"
},
{
"id": "style-c",
"label": "Style C: [Name]",
"description": "[Brief description]",
"path": ".claude-design/slide-previews/style-c.html"
}
]
}
Exact tool call parameters (Gate 5):
request_clarification(
title="Choose Your Presentation Style",
description="Preview each direction, then choose the one you want me to use for the full deck.",
options_json='''[
{"id":"style-a","label":"Style A: [Name]","value":"Style A: [Name]","description":"[Brief description]"},
{"id":"style-b","label":"Style B: [Name]","value":"Style B: [Name]","description":"[Brief description]"},
{"id":"style-c","label":"Style C: [Name]","value":"Style C: [Name]","description":"[Brief description]"},
{"id":"mix-elements","label":"Mix elements","value":"Mix elements","description":"Combine aspects from different styles"}
]''',
allow_freeform=false,
multi_select=false,
context_json='''{
"skill": "frontend-slides",
"gateId": "style_preview_selection",
"chooser": "style-previews",
"uiContract": "a2ui",
"expectedComponent": "style_preview_chooser",
"stylePreviews": [
{
"id": "style-a",
"label": "Style A: [Name]",
"description": "[Brief description]",
"path": ".claude-design/slide-previews/style-a.html"
},
{
"id": "style-b",
"label": "Style B: [Name]",
"description": "[Brief description]",
"path": ".claude-design/slide-previews/style-b.html"
},
{
"id": "style-c",
"label": "Style C: [Name]",
"description": "[Brief description]",
"path": ".claude-design/slide-previews/style-c.html"
}
]
}''',
submit_label="Use selected style"
)
If "Mix elements", ask for specifics.
After this tool call, stop and wait. Do not generate the final presentation until the user has answered the style-selection interrupt.
Phase 3: Generate Presentation
Now generate the full presentation based on:
- Content from Phase 1 (text only, or text + curated images)
- Style from Phase 2
If the user provided images, the slide outline already incorporates them as visual anchors from Step 1.2. If not, proceed with text-only content — CSS-generated visuals (gradients, shapes, patterns) provide visual interest.
Image Pipeline (skip if no images)
If the user chose "No images" in Step 1.2, skip this entire section and go straight to generating HTML. The presentation will be text-only with CSS-generated visuals — this is a fully supported, first-class path.
If the user provided images, execute these steps before generating HTML.
Key principle: Co-design, not post-hoc. The curated images from Step 1.2 (those marked USABLE) are already part of the slide outline. The pipeline's job here is to process images for the chosen style and place them in the HTML.
Step 3.1: Image Processing (Pillow)
For each curated image, determine what processing it needs based on the chosen style (e.g., circular crop for logos, resize for large files) and what CSS framing will bridge any color gaps between the image and the style's palette. Then process accordingly.
Rules:
- Never repeat the same image on multiple slides (except logos which may bookend title + closing)
- Always add CSS framing (border, glow, shadow) for images whose colors clash with the style's palette
Dependency: Python Pillow library (the standard image processing library for Python).
pip install Pillow
This is analogous to how python-pptx is used in Phase 4 (PPT Conversion) — a standard, well-maintained Python package that any user can install.
Common processing operations:
from PIL import Image, ImageDraw
def crop_circle(input_path, output_path):
"""Crop a square image to a circle with transparent background."""
img = Image.open(input_path).convert('RGBA')
w, h = img.size
size = min(w, h)
left = (w - size) // 2
top = (h - size) // 2
img = img.crop((left, top, left + size, top + size))
mask = Image.new('L', (size, size), 0)
draw = ImageDraw.Draw(mask)
draw.ellipse([0, 0, size, size], fill=255)
img.putalpha(mask)
img.save(output_path, 'PNG')
def resize_max(input_path, output_path, max_dim=1200):
"""Resize image so largest dimension <= max_dim. Preserves aspect ratio."""
img = Image.open(input_path)
img.thumbnail((max_dim, max_dim), Image.LANCZOS)
img.save(output_path, quality=85)
def add_padding(input_path, output_path, padding=40, bg_color=(0, 0, 0, 0)):
"""Add transparent padding around an image."""
img = Image.open(input_path).convert('RGBA')
w, h = img.size
new = Image.new('RGBA', (w + 2*padding, h + 2*padding), bg_color)
new.paste(img, (padding, padding), img)
new.save(output_path, 'PNG')
When to apply each operation:
| Situation | Operation |
|---|
| Square logo on a style with rounded aesthetics | crop_circle() |
| Image > 1MB (slow to load) | resize_max(max_dim=1200) |
| Screenshot needs breathing room in layout | add_padding() |
| Image has wrong aspect ratio for its slide slot | Manual crop with img.crop((left, top, right, bottom)) |
Save processed images alongside originals with a _processed suffix (e.g., logo_round.png). Never overwrite the user's original files.
Step 3.2: Place Images
Use direct file paths — do NOT convert images to base64 data URIs. Since presentations are viewed locally, reference images with relative paths from the HTML file:
<img src="assets/logo_round.png" alt="Logo" class="slide-image logo">
<img src="assets/screenshot.png" alt="Screenshot" class="slide-image screenshot">
This keeps the HTML file small and images easy to swap. Only use base64 encoding if the user explicitly requests a fully self-contained single-file presentation.
Image CSS classes (adapt border/glow colors to match the chosen style):
.slide-image {
max-width: 100%;
max-height: min(50vh, 400px);
object-fit: contain;
border-radius: 8px;
}
.slide-image.screenshot {
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.slide-image.logo {
max-height: min(30vh, 200px);
}
IMPORTANT: Adapt the .screenshot border and shadow colors to match the chosen style's accent color. For example:
- Dark Botanical (gold accent):
border: 1px solid rgba(197, 160, 89, 0.2); box-shadow: 0 0 20px rgba(197, 160, 89, 0.08);
- Creative Voltage (neon yellow):
border: 2px solid rgba(212, 255, 0, 0.25); box-shadow: 0 0 20px rgba(212, 255, 0, 0.08);
Placement patterns:
- Title slide: Logo centered above or beside the title
- Feature slides: Screenshot on one side, text on the other (two-column layout)
- Full-bleed: Image as slide background with text overlay (use with caution)
- Inline: Image within content flow, centered, with caption below
Note: Processed images (e.g. logo_round.png) are saved alongside originals in the assets folder. Reference them with relative paths in the HTML.
File Structure
For single presentations:
presentation.html # Self-contained presentation
assets/ # Images, if any
For projects with multiple presentations:
[presentation-name].html
[presentation-name]-assets/
HTML Architecture
Follow this structure for all presentations:
<!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>
:root {
--bg-primary: #0a0f1c;
--bg-secondary: #111827;
--text-primary: #ffffff;
--text-secondary: #9ca3af;
--accent: #00ffcc;
--accent-glow: rgba(0, 255, 204, 0.3);
--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);
--slide-padding: clamp(1.5rem, 4vw, 4rem);
--content-gap: clamp(1rem, 2vw, 2rem);
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--duration-normal: 0.6s;
}
* {
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 {
width: 100vw;
height: 100vh;
height: 100dvh;
padding: var(--slide-padding);
scroll-snap-align: start;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
overflow: hidden;
}
.slide-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
max-height: 100%;
overflow: hidden;
}
@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) {
:root {
--title-size: clamp(1.25rem, 5vw, 2rem);
--slide-padding: clamp(0.75rem, 2vw, 1.5rem);
}
}
.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);
}
.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; }
</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>
class SlidePresentation {
constructor() {
}
}
new SlidePresentation();
</script>
</body>
</html>
Required JavaScript Features
Every presentation should include:
-
SlidePresentation Class — Main controller
- Keyboard navigation (arrows, space)
- Touch/swipe support
- Mouse wheel navigation
- Progress bar updates
- Navigation dots
-
Intersection Observer — For scroll-triggered animations
- Add
.visible class when slides enter viewport
- Trigger CSS animations efficiently
-
Optional Enhancements (based on style):
- Custom cursor with trail
- Particle system background (canvas)
- Parallax effects
- 3D tilt on hover
- Magnetic buttons
- Counter animations
- Inline editing (only if user opted in during content discovery):
- Edit toggle button (hidden by default, revealed via hover hotzone or
E key)
- Auto-save to localStorage
- Export/save file functionality
- See "Edit Button Implementation" section below for required code patterns
Edit Button Implementation (When User Opts In)
If the user chose "No" for inline editing in Phase 1, skip this entirely — do not generate any edit-related HTML, CSS, or JS.
⚠️ Critical: Do NOT use CSS ~ sibling selector for hover-based show/hide.
The CSS-only approach (edit-hotzone:hover ~ .edit-toggle) fails because pointer-events: none on the toggle button causes the hover chain to break: user hovers hotzone → button becomes visible → mouse moves toward button → leaves hotzone → button disappears before click reaches it.
Required approach: JS-based hover with delay timeout.
HTML structure:
<div class="edit-hotzone"></div>
<button class="edit-toggle" id="editToggle" title="编辑模式 (E)">✏️</button>
CSS (visibility controlled by JS classes only):
.edit-hotzone {
position: fixed; top: 0; left: 0;
width: 80px; height: 80px;
z-index: 10000;
cursor: pointer;
}
.edit-toggle {
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
z-index: 10001;
}
.edit-toggle.show,
.edit-toggle.active {
opacity: 1;
pointer-events: auto;
}
JS (all three interaction methods):
document.getElementById('editToggle').addEventListener('click', () => {
editor.toggleEditMode();
});
const hotzone = document.querySelector('.edit-hotzone');
const editToggle = document.getElementById('editToggle');
let hideTimeout = null;
hotzone.addEventListener('mouseenter', () => {
clearTimeout(hideTimeout);
editToggle.classList.add('show');
});
hotzone.addEventListener('mouseleave', () => {
hideTimeout = setTimeout(() => {
if (!editor.isActive) editToggle.classList.remove('show');
}, 400);
});
editToggle.addEventListener('mouseenter', () => {
clearTimeout(hideTimeout);
});
editToggle.addEventListener('mouseleave', () => {
hideTimeout = setTimeout(() => {
if (!editor.isActive) editToggle.classList.remove('show');
}, 400);
});
hotzone.addEventListener('click', () => {
editor.toggleEditMode();
});
document.addEventListener('keydown', (e) => {
if ((e.key === 'e' || e.key === 'E') && !e.target.getAttribute('contenteditable')) {
editor.toggleEditMode();
}
});
Code Quality Requirements
Comments:
Every section should have clear comments explaining:
- What it does
- Why it exists
- How to modify it
class CustomCursor {
constructor() {
}
}
Accessibility:
- Semantic HTML (
<section>, <nav>, <main>)
- Keyboard navigation works
- ARIA labels where needed
- Reduced motion support
@media (prefers-reduced-motion: reduce) {
.reveal {
transition: opacity 0.3s ease;
transform: none;
}
}
CSS Function Negation:
- Never negate CSS functions directly —
-clamp(), -min(), -max() are silently ignored by browsers with no console error
- Always use
calc(-1 * clamp(...)) instead. See STYLE_PRESETS.md → "CSS Gotchas" for details.
Responsive & Viewport Fitting (CRITICAL):
See the "CRITICAL: Viewport Fitting Requirements" section above for complete CSS and guidelines.
Quick reference:
- Every
.slide must have height: 100vh; height: 100dvh; overflow: hidden;
- All typography and spacing must use
clamp()
- Respect content density limits (max 4-6 bullets, max 6 cards, etc.)
- Include breakpoints for heights: 700px, 600px, 500px
- When content doesn't fit → split into multiple slides, never scroll
Phase 4: PPT Conversion
When converting PowerPoint files:
Step 4.1: Extract Content
Use Python with python-pptx to extract:
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 = []
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_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
})
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
Step 4.2: Confirm Content Structure
Present the extracted content to the user:
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?
Step 4.3: Style Selection
Proceed to Phase 2 (Style Discovery) with the extracted content in mind.
Step 4.4: Generate HTML
Convert the extracted content into the chosen style, preserving:
- All text content
- All images (referenced from assets folder)
- Slide order
- Any speaker notes (as HTML comments or separate file)
Phase 5: Delivery
Final Output
When the presentation is complete:
-
Clean up temporary files
- Delete
.claude-design/slide-previews/ if it exists
-
Open the presentation
- Use
open [filename].html to launch in browser
-
Provide summary
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?
If the user opted in to inline editing, also include:
**Editing:**
- Hover over top-left corner or press E to enter edit mode
- Click any text to edit directly
- Ctrl+S or click "Save file" to save changes
Style Reference: Effect → Feeling Mapping
Use this guide to match animations to intended feelings:
Dramatic / Cinematic
- Slow fade-ins (1-1.5s)
- Large scale transitions (0.9 → 1)
- Dark backgrounds with spotlight effects
- Parallax scrolling
- Full-bleed images
Techy / Futuristic
- Neon glow effects (box-shadow with accent color)
- Particle systems (canvas background)
- Grid patterns
- Monospace fonts for accents
- Glitch or scramble text effects
- Cyan, magenta, electric blue palette
Playful / Friendly
- Bouncy easing (spring physics)
- Rounded corners (large radius)
- Pastel or bright colors
- Floating/bobbing animations
- Hand-drawn or illustrated elements
Professional / Corporate
- Subtle, fast animations (200-300ms)
- Clean sans-serif fonts
- Navy, slate, or charcoal backgrounds
- Precise spacing and alignment
- Minimal decorative elements
- Data visualization focus
Calm / Minimal
- Very slow, subtle motion
- High whitespace
- Muted color palette
- Serif typography
- Generous padding
- Content-focused, no distractions
Editorial / Magazine
- Strong typography hierarchy
- Pull quotes and callouts
- Image-text interplay
- Grid-breaking layouts
- Serif headlines, sans-serif body
- Black and white with one accent
Animation Patterns Reference
Entrance Animations
.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);
}
.reveal-scale {
opacity: 0;
transform: scale(0.9);
transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
}
.reveal-left {
opacity: 0;
transform: translateX(-50px);
transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
}
.reveal-blur {
opacity: 0;
filter: blur(10px);
transition: opacity 0.8s, filter 0.8s var(--ease-out-expo);
}
Background Effects
.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;
}
Interactive Effects
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)';
});
}
}
Troubleshooting
Common Issues
Fonts not loading:
- Check Fontshare/Google Fonts URL
- Ensure font names match in CSS
Animations not triggering:
- Verify Intersection Observer is running
- Check that
.visible class is being added
Scroll snap not working:
- Ensure
scroll-snap-type on html/body
- Each slide needs
scroll-snap-align: start
Mobile issues:
- Disable heavy effects at 768px breakpoint
- Test touch events
- Reduce particle count or disable canvas
Performance issues:
- Use
will-change sparingly
- Prefer
transform and opacity animations
- Throttle scroll/mousemove handlers
Related Skills
- learn — Generate FORZARA.md documentation for the presentation
- frontend-design — For more complex interactive pages beyond slides
- design-and-refine:design-lab — For iterating on component designs
Example Session Flow
- User: "I want to create a pitch deck for my AI startup"
- Skill uses
request_clarification to ask about purpose, length, content, images, and editing (single form)
- User shares bullet points, selects
./assets folder
- Evaluate: Skill views each image (multimodal), builds slide outline with image assignments:
logo.png → USABLE → title/closing slide
chat_ui.png → USABLE → feature slide
dashboard.png → USABLE → feature slide
launch_card.png → USABLE → feature slide
blurry_team.jpg → NOT USABLE (too low resolution)
- User confirms outline via
request_clarification
- Skill asks about desired feeling via
request_clarification (Impressed + Excited)
- Skill generates 3 style previews
- User picks Style B (Neon Cyber) via
request_clarification
- Process + Generate: Skill runs Pillow operations (circular crop, resize), generates full presentation with direct image paths
- Skill opens the presentation in browser
- User requests tweaks to specific slides
- Final presentation delivered
Conversion Session Flow
- User: "Convert my slides.pptx to a web presentation"
- Skill extracts content and images from PPT
- Skill confirms extracted content with user
- Skill asks about desired feeling/style via
request_clarification
- Skill generates style previews
- User picks a style
- Skill generates HTML presentation with preserved assets
- Final presentation delivered