Frontend Slides Skill

Create stunning, animation-rich HTML presentations from scratch or by converting PowerPoint files.

How to use it

  1. Hit Copy SKILL.md — or use the Claude Code line below to get every file.
  2. Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
    ChatGPT: make a Project and paste it into Instructions.
    Neither? Paste it at the top of a new chat — it works for that chat.
  3. Describe your job in plain words. The AI follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit gooseworks-ai/goose-skills/skills/design/capabilities/create-html-slides#main ~/.claude/skills/create-html-slides

For one project only, change the path to .claude/skills/create-html-slides. This skill also uses FORZARA.md — copying SKILL.md alone won't be enough. See the folder on GitHub.

Not working?
  • Check which app you pasted it into — the steps above name the right one.
  • Some skills need the paid tier of Claude or ChatGPT.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Show the full text1109 lines
create-html-slides/SKILL.md1109 lines32.5 KBpushed 96d agoRawView on GitHub

Deprecated: This skill is superseded by goose-graphics. See skills/composites/goose-graphics/ (install with npx goose-skills install goose-graphics). The slides format is one of seven formats in the newer skill and supports 36 style presets plus image sourcing and PNG export. This skill is retained for one release cycle before removal.

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

  1. Zero Dependencies — Single HTML files with inline CSS/JS. No npm, no build tools.
  2. Show, Don't Tell — People don't know what they want until they see it. Generate visual previews, not abstract choices.
  3. Distinctive Design — Avoid generic "AI slop" aesthetics. Every presentation should feel custom-crafted.
  4. Production Quality — Code should be well-commented, accessible, and performant.
  5. 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:

/* ===========================================
   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;
    }
}

Overflow Prevention Checklist

Before generating any presentation, mentally verify:

  1. ✅ Every .slide has height: 100vh; height: 100dvh; overflow: hidden;
  2. ✅ All font sizes use clamp(min, preferred, max)
  3. ✅ All spacing uses clamp() or viewport units
  4. ✅ Content containers have max-height constraints
  5. ✅ Images have max-height: min(50vh, 400px) or similar
  6. ✅ Grids use auto-fit with minmax() for responsive columns
  7. ✅ Breakpoints exist for heights: 700px, 600px, 500px
  8. ✅ No fixed pixel heights on content elements
  9. ✅ 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

DON'T:

  • Reduce font size below readable limits
  • Remove padding/spacing entirely
  • Allow any scrolling
  • Cram content to fit

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

Phase 1: Content Discovery (New Presentations)

Before designing, understand the content. Ask via AskUserQuestion:

Step 1.1: Presentation Context

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

If user has content, ask them to share it (text, bullet points, images, etc.).


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:

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
    • "Use client branding" — Apply a client's brand colors, fonts, and aesthetic

If "Show me options" → Continue to Step 2.1 (Mood Selection)

If "I know what I want" → Show preset picker:

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.)

If "Use client branding" → Load client visual identity:

  1. Ask which client this presentation is for (or infer from context)
  2. Check for clients/<client-name>/brand/visual-identity.md
  3. If the file exists: Read the "Slide Preset" section. Use its CSS :root custom properties as the presentation's theme variables, its typography as the font pairing, its font loading <link> tag, and its signature elements as design guidance for decorative CSS.
  4. If the file does not exist: Tell the user: "No visual identity has been extracted yet for [client]. Would you like me to extract it now? Please provide the client's website URL." Then run the visual-brand-extractor skill to generate the file, and continue.
  5. Skip to Phase 3 with the client preset loaded.

Step 2.1: Mood Selection (Guided Discovery)

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)

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

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 and tell me:
1. Which style resonates most?
2. What do you like about it?
3. Anything you'd change?

Then use AskUserQuestion:

Question: Pick Your Style

  • Header: "Style"
  • Question: "Which style preview do you prefer?"
  • Options:
    • "Style A: [Name]" — [Brief description]
    • "Style B: [Name]" — [Brief description]
    • "Style C: [Name]" — [Brief description]
    • "Mix elements" — Combine aspects from different styles

If "Mix elements", ask for specifics.


Phase 3: Generate Presentation

Now generate the full presentation based on:

  • Content from Phase 1
  • Style from Phase 2

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>

    <!-- Fonts (use Fontshare or Google Fonts) -->
    <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>
    <!-- Progress bar (optional) -->
    <div class="progress-bar"></div>

    <!-- Navigation dots (optional) -->
    <nav class="nav-dots">
        <!-- Generated by JS -->
    </nav>

    <!-- Slides -->
    <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>

    <!-- More slides... -->

    <script>
        /* ===========================================
           SLIDE PRESENTATION CONTROLLER
           Handles navigation, animations, and interactions
           =========================================== */

        class SlidePresentation {
            constructor() {
                // ... initialization
            }

            // ... methods
        }

        // Initialize
        new SlidePresentation();
    </script>
</body>
</html>

Required JavaScript Features

Every presentation should include:

  1. SlidePresentation Class — Main controller

    • Keyboard navigation (arrows, space)
    • Touch/swipe support
    • Mouse wheel navigation
    • Progress bar updates
    • Navigation dots
  2. Intersection Observer — For scroll-triggered animations

    • Add .visible class when slides enter viewport
    • Trigger CSS animations efficiently
  3. Optional Enhancements (based on style):

    • Custom cursor with trail
    • Particle system background (canvas)
    • Parallax effects
    • 3D tilt on hover
    • Magnetic buttons
    • Counter animations

Code Quality Requirements

Comments: Every section should have clear comments explaining:

  • What it does
  • Why it exists
  • How to modify it
/* ===========================================
   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() {
        // ...
    }
}

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;
    }
}

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 = []

    # 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

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:

  1. Clean up temporary files

    • Delete .claude-design/slide-previews/ if it exists
  2. Open the presentation

    • Use open [filename].html to launch in browser
  3. 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?

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

/* 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);
}

Background Effects

/* 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;
}

Interactive Effects

/* 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)';
        });
    }
}

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

  1. User: "I want to create a pitch deck for my AI startup"
  2. Skill asks about purpose, length, content
  3. User shares their bullet points and key messages
  4. Skill asks about desired feeling (Impressed + Excited)
  5. Skill generates 3 style previews
  6. User picks Style B (Neon Cyber), asks for darker background
  7. Skill generates full presentation with all slides
  8. Skill opens the presentation in browser
  9. User requests tweaks to specific slides
  10. Final presentation delivered

Conversion Session Flow

  1. User: "Convert my slides.pptx to a web presentation"
  2. Skill extracts content and images from PPT
  3. Skill confirms extracted content with user
  4. Skill asks about desired feeling/style
  5. Skill generates style previews
  6. User picks a style
  7. Skill generates HTML presentation with preserved assets
  8. Final presentation delivered
1---
2name: frontend-slides
3description: 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.
4---
5 
6> **Deprecated:** This skill is superseded by `goose-graphics`. See `skills/composites/goose-graphics/` (install with `npx goose-skills install goose-graphics`). The slides format is one of seven formats in the newer skill and supports 36 style presets plus image sourcing and PNG export. This skill is retained for one release cycle before removal.
7 
8# Frontend Slides Skill
9 
10Create 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.
11 
12## Core Philosophy
13 
141. **Zero Dependencies** — Single HTML files with inline CSS/JS. No npm, no build tools.
152. **Show, Don't Tell** — People don't know what they want until they see it. Generate visual previews, not abstract choices.
163. **Distinctive Design** — Avoid generic "AI slop" aesthetics. Every presentation should feel custom-crafted.
174. **Production Quality** — Code should be well-commented, accessible, and performant.
185. **Viewport Fitting (CRITICAL)** — Every slide MUST fit exactly within the viewport. No scrolling within slides, ever. This is non-negotiable.
19 
20---
21 
22## CRITICAL: Viewport Fitting Requirements
23 
24**This section is mandatory for ALL presentations. Every slide must be fully visible without scrolling on any screen size.**
25 
26### The Golden Rule
27 
28```
29Each slide = exactly one viewport height (100vh/100dvh)
30Content overflows? → Split into multiple slides or reduce content
31Never scroll within a slide.
32```
33 
34### Content Density Limits
35 
36To guarantee viewport fitting, enforce these limits per slide:
37 
38| Slide Type | Maximum Content |
39|------------|-----------------|
40| Title slide | 1 heading + 1 subtitle + optional tagline |
41| Content slide | 1 heading + 4-6 bullet points OR 1 heading + 2 paragraphs |
42| Feature grid | 1 heading + 6 cards maximum (2x3 or 3x2 grid) |
43| Code slide | 1 heading + 8-10 lines of code maximum |
44| Quote slide | 1 quote (max 3 lines) + attribution |
45| Image slide | 1 heading + 1 image (max 60vh height) |
46 
47**If content exceeds these limits → Split into multiple slides**
48 
49### Required CSS Architecture
50 
51Every presentation MUST include this base CSS for viewport fitting:
52 
53```css
54/* ===========================================
55 VIEWPORT FITTING: MANDATORY BASE STYLES
56 These styles MUST be included in every presentation.
57 They ensure slides fit exactly in the viewport.
58 =========================================== */
59 
60/* 1. Lock html/body to viewport */
61html, body {
62 height: 100%;
63 overflow-x: hidden;
64}
65 
66html {
67 scroll-snap-type: y mandatory;
68 scroll-behavior: smooth;
69}
70 
71/* 2. Each slide = exact viewport height */
72.slide {
73 width: 100vw;
74 height: 100vh;
75 height: 100dvh; /* Dynamic viewport height for mobile browsers */
76 overflow: hidden; /* CRITICAL: Prevent ANY overflow */
77 scroll-snap-align: start;
78 display: flex;
79 flex-direction: column;
80 position: relative;
81}
82 
83/* 3. Content container with flex for centering */
84.slide-content {
85 flex: 1;
86 display: flex;
87 flex-direction: column;
88 justify-content: center;
89 max-height: 100%;
90 overflow: hidden; /* Double-protection against overflow */
91 padding: var(--slide-padding);
92}
93 
94/* 4. ALL typography uses clamp() for responsive scaling */
95:root {
96 /* Titles scale from mobile to desktop */
97 --title-size: clamp(1.5rem, 5vw, 4rem);
98 --h2-size: clamp(1.25rem, 3.5vw, 2.5rem);
99 --h3-size: clamp(1rem, 2.5vw, 1.75rem);
100 
101 /* Body text */
102 --body-size: clamp(0.75rem, 1.5vw, 1.125rem);
103 --small-size: clamp(0.65rem, 1vw, 0.875rem);
104 
105 /* Spacing scales with viewport */
106 --slide-padding: clamp(1rem, 4vw, 4rem);
107 --content-gap: clamp(0.5rem, 2vw, 2rem);
108 --element-gap: clamp(0.25rem, 1vw, 1rem);
109}
110 
111/* 5. Cards/containers use viewport-relative max sizes */
112.card, .container, .content-box {
113 max-width: min(90vw, 1000px);
114 max-height: min(80vh, 700px);
115}
116 
117/* 6. Lists auto-scale with viewport */
118.feature-list, .bullet-list {
119 gap: clamp(0.4rem, 1vh, 1rem);
120}
121 
122.feature-list li, .bullet-list li {
123 font-size: var(--body-size);
124 line-height: 1.4;
125}
126 
127/* 7. Grids adapt to available space */
128.grid {
129 display: grid;
130 grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
131 gap: clamp(0.5rem, 1.5vw, 1rem);
132}
133 
134/* 8. Images constrained to viewport */
135img, .image-container {
136 max-width: 100%;
137 max-height: min(50vh, 400px);
138 object-fit: contain;
139}
140 
141/* ===========================================
142 RESPONSIVE BREAKPOINTS
143 Aggressive scaling for smaller viewports
144 =========================================== */
145 
146/* Short viewports (< 700px height) */
147@media (max-height: 700px) {
148 :root {
149 --slide-padding: clamp(0.75rem, 3vw, 2rem);
150 --content-gap: clamp(0.4rem, 1.5vw, 1rem);
151 --title-size: clamp(1.25rem, 4.5vw, 2.5rem);
152 --h2-size: clamp(1rem, 3vw, 1.75rem);
153 }
154}
155 
156/* Very short viewports (< 600px height) */
157@media (max-height: 600px) {
158 :root {
159 --slide-padding: clamp(0.5rem, 2.5vw, 1.5rem);
160 --content-gap: clamp(0.3rem, 1vw, 0.75rem);
161 --title-size: clamp(1.1rem, 4vw, 2rem);
162 --body-size: clamp(0.7rem, 1.2vw, 0.95rem);
163 }
164 
165 /* Hide non-essential elements */
166 .nav-dots, .keyboard-hint, .decorative {
167 display: none;
168 }
169}
170 
171/* Extremely short (landscape phones, < 500px height) */
172@media (max-height: 500px) {
173 :root {
174 --slide-padding: clamp(0.4rem, 2vw, 1rem);
175 --title-size: clamp(1rem, 3.5vw, 1.5rem);
176 --h2-size: clamp(0.9rem, 2.5vw, 1.25rem);
177 --body-size: clamp(0.65rem, 1vw, 0.85rem);
178 }
179}
180 
181/* Narrow viewports (< 600px width) */
182@media (max-width: 600px) {
183 :root {
184 --title-size: clamp(1.25rem, 7vw, 2.5rem);
185 }
186 
187 /* Stack grids vertically */
188 .grid {
189 grid-template-columns: 1fr;
190 }
191}
192 
193/* ===========================================
194 REDUCED MOTION
195 Respect user preferences
196 =========================================== */
197@media (prefers-reduced-motion: reduce) {
198 *, *::before, *::after {
199 animation-duration: 0.01ms !important;
200 transition-duration: 0.2s !important;
201 }
202 
203 html {
204 scroll-behavior: auto;
205 }
206}
207```
208 
209### Overflow Prevention Checklist
210 
211Before generating any presentation, mentally verify:
212 
2131. ✅ Every `.slide` has `height: 100vh; height: 100dvh; overflow: hidden;`
2142. ✅ All font sizes use `clamp(min, preferred, max)`
2153. ✅ All spacing uses `clamp()` or viewport units
2164. ✅ Content containers have `max-height` constraints
2175. ✅ Images have `max-height: min(50vh, 400px)` or similar
2186. ✅ Grids use `auto-fit` with `minmax()` for responsive columns
2197. ✅ Breakpoints exist for heights: 700px, 600px, 500px
2208. ✅ No fixed pixel heights on content elements
2219. ✅ Content per slide respects density limits
222 
223### When Content Doesn't Fit
224 
225If you find yourself with too much content:
226 
227**DO:**
228- Split into multiple slides
229- Reduce bullet points (max 5-6 per slide)
230- Shorten text (aim for 1-2 lines per bullet)
231- Use smaller code snippets
232- Create a "continued" slide
233 
234**DON'T:**
235- Reduce font size below readable limits
236- Remove padding/spacing entirely
237- Allow any scrolling
238- Cram content to fit
239 
240### Testing Viewport Fit
241 
242After generating, recommend the user test at these sizes:
243- Desktop: 1920×1080, 1440×900, 1280×720
244- Tablet: 1024×768, 768×1024 (portrait)
245- Mobile: 375×667, 414×896
246- Landscape phone: 667×375, 896×414
247 
248---
249 
250## Phase 0: Detect Mode
251 
252First, determine what the user wants:
253 
254**Mode A: New Presentation**
255- User wants to create slides from scratch
256- Proceed to Phase 1 (Content Discovery)
257 
258**Mode B: PPT Conversion**
259- User has a PowerPoint file (.ppt, .pptx) to convert
260- Proceed to Phase 4 (PPT Extraction)
261 
262**Mode C: Existing Presentation Enhancement**
263- User has an HTML presentation and wants to improve it
264- Read the existing file, understand the structure, then enhance
265 
266---
267 
268## Phase 1: Content Discovery (New Presentations)
269 
270Before designing, understand the content. Ask via AskUserQuestion:
271 
272### Step 1.1: Presentation Context
273 
274**Question 1: Purpose**
275- Header: "Purpose"
276- Question: "What is this presentation for?"
277- Options:
278 - "Pitch deck" — Selling an idea, product, or company to investors/clients
279 - "Teaching/Tutorial" — Explaining concepts, how-to guides, educational content
280 - "Conference talk" — Speaking at an event, tech talk, keynote
281 - "Internal presentation" — Team updates, strategy meetings, company updates
282 
283**Question 2: Slide Count**
284- Header: "Length"
285- Question: "Approximately how many slides?"
286- Options:
287 - "Short (5-10)" — Quick pitch, lightning talk
288 - "Medium (10-20)" — Standard presentation
289 - "Long (20+)" — Deep dive, comprehensive talk
290 
291**Question 3: Content**
292- Header: "Content"
293- Question: "Do you have the content ready, or do you need help structuring it?"
294- Options:
295 - "I have all content ready" — Just need to design the presentation
296 - "I have rough notes" — Need help organizing into slides
297 - "I have a topic only" — Need help creating the full outline
298 
299If user has content, ask them to share it (text, bullet points, images, etc.).
300 
301---
302 
303## Phase 2: Style Discovery (Visual Exploration)
304 
305**CRITICAL: This is the "show, don't tell" phase.**
306 
307Most 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.
308 
309### How Users Choose Presets
310 
311Users can select a style in **two ways**:
312 
313**Option A: Guided Discovery (Default)**
314- User answers mood questions
315- Skill generates 3 preview files based on their answers
316- User views previews in browser and picks their favorite
317- This is best for users who don't have a specific style in mind
318 
319**Option B: Direct Selection**
320- If user already knows what they want, they can request a preset by name
321- Example: "Use the Bold Signal style" or "I want something like Dark Botanical"
322- Skip to Phase 3 immediately
323 
324**Available Presets:**
325| Preset | Vibe | Best For |
326|--------|------|----------|
327| Bold Signal | Confident, high-impact | Pitch decks, keynotes |
328| Electric Studio | Clean, professional | Agency presentations |
329| Creative Voltage | Energetic, retro-modern | Creative pitches |
330| Dark Botanical | Elegant, sophisticated | Premium brands |
331| Notebook Tabs | Editorial, organized | Reports, reviews |
332| Pastel Geometry | Friendly, approachable | Product overviews |
333| Split Pastel | Playful, modern | Creative agencies |
334| Vintage Editorial | Witty, personality-driven | Personal brands |
335| Neon Cyber | Futuristic, techy | Tech startups |
336| Terminal Green | Developer-focused | Dev tools, APIs |
337| Swiss Modern | Minimal, precise | Corporate, data |
338| Paper & Ink | Literary, thoughtful | Storytelling |
339 
340### Step 2.0: Style Path Selection
341 
342First, ask how the user wants to choose their style:
343 
344**Question: Style Selection Method**
345- Header: "Style"
346- Question: "How would you like to choose your presentation style?"
347- Options:
348 - "Show me options" — Generate 3 previews based on my needs (recommended for most users)
349 - "I know what I want" — Let me pick from the preset list directly
350 - "Use client branding" — Apply a client's brand colors, fonts, and aesthetic
351 
352**If "Show me options"** → Continue to Step 2.1 (Mood Selection)
353 
354**If "I know what I want"** → Show preset picker:
355 
356**Question: Pick a Preset**
357- Header: "Preset"
358- Question: "Which style would you like to use?"
359- Options:
360 - "Bold Signal" — Vibrant card on dark, confident and high-impact
361 - "Dark Botanical" — Elegant dark with soft abstract shapes
362 - "Notebook Tabs" — Editorial paper look with colorful section tabs
363 - "Pastel Geometry" — Friendly pastels with decorative pills
364 
365(If user picks one, skip to Phase 3. If they want to see more options, show additional presets or proceed to guided discovery.)
366 
367**If "Use client branding"** → Load client visual identity:
368 
3691. Ask which client this presentation is for (or infer from context)
3702. Check for `clients/<client-name>/brand/visual-identity.md`
3713. **If the file exists:** Read the "Slide Preset" section. Use its CSS `:root` custom properties as the presentation's theme variables, its typography as the font pairing, its font loading `<link>` tag, and its signature elements as design guidance for decorative CSS.
3724. **If the file does not exist:** Tell the user: "No visual identity has been extracted yet for [client]. Would you like me to extract it now? Please provide the client's website URL." Then run the `visual-brand-extractor` skill to generate the file, and continue.
3735. Skip to Phase 3 with the client preset loaded.
374 
375### Step 2.1: Mood Selection (Guided Discovery)
376 
377**Question 1: Feeling**
378- Header: "Vibe"
379- Question: "What feeling should the audience have when viewing your slides?"
380- Options:
381 - "Impressed/Confident" — Professional, trustworthy, this team knows what they're doing
382 - "Excited/Energized" — Innovative, bold, this is the future
383 - "Calm/Focused" — Clear, thoughtful, easy to follow
384 - "Inspired/Moved" — Emotional, storytelling, memorable
385- multiSelect: true (can choose up to 2)
386 
387### Step 2.2: Generate Style Previews
388 
389Based 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:
390 
391- Typography (font choices, heading/body hierarchy)
392- Color palette (background, accent, text colors)
393- Animation style (how elements enter)
394- Overall aesthetic feel
395 
396**Preview Styles to Consider (pick 3 based on mood):**
397 
398| Mood | Style Options |
399|------|---------------|
400| Impressed/Confident | "Bold Signal", "Electric Studio", "Dark Botanical" |
401| Excited/Energized | "Creative Voltage", "Neon Cyber", "Split Pastel" |
402| Calm/Focused | "Notebook Tabs", "Paper & Ink", "Swiss Modern" |
403| Inspired/Moved | "Dark Botanical", "Vintage Editorial", "Pastel Geometry" |
404 
405**IMPORTANT: Never use these generic patterns:**
406- Purple gradients on white backgrounds
407- Inter, Roboto, or system fonts
408- Standard blue primary colors
409- Predictable hero layouts
410 
411**Instead, use distinctive choices:**
412- Unique font pairings (Clash Display, Satoshi, Cormorant Garamond, DM Sans, etc.)
413- Cohesive color themes with personality
414- Atmospheric backgrounds (gradients, subtle patterns, depth)
415- Signature animation moments
416 
417### Step 2.3: Present Previews
418 
419Create the previews in: `.claude-design/slide-previews/`
420 
421```
422.claude-design/slide-previews/
423├── style-a.html # First style option
424├── style-b.html # Second style option
425├── style-c.html # Third style option
426└── assets/ # Any shared assets
427```
428 
429Each preview file should be:
430- Self-contained (inline CSS/JS)
431- A single "title slide" showing the aesthetic
432- Animated to demonstrate motion style
433- ~50-100 lines, not a full presentation
434 
435Present to user:
436```
437I've created 3 style previews for you to compare:
438 
439**Style A: [Name]** — [1 sentence description]
440**Style B: [Name]** — [1 sentence description]
441**Style C: [Name]** — [1 sentence description]
442 
443Open each file to see them in action:
444- .claude-design/slide-previews/style-a.html
445- .claude-design/slide-previews/style-b.html
446- .claude-design/slide-previews/style-c.html
447 
448Take a look and tell me:
4491. Which style resonates most?
4502. What do you like about it?
4513. Anything you'd change?
452```
453 
454Then use AskUserQuestion:
455 
456**Question: Pick Your Style**
457- Header: "Style"
458- Question: "Which style preview do you prefer?"
459- Options:
460 - "Style A: [Name]" — [Brief description]
461 - "Style B: [Name]" — [Brief description]
462 - "Style C: [Name]" — [Brief description]
463 - "Mix elements" — Combine aspects from different styles
464 
465If "Mix elements", ask for specifics.
466 
467---
468 
469## Phase 3: Generate Presentation
470 
471Now generate the full presentation based on:
472- Content from Phase 1
473- Style from Phase 2
474 
475### File Structure
476 
477For single presentations:
478```
479presentation.html # Self-contained presentation
480assets/ # Images, if any
481```
482 
483For projects with multiple presentations:
484```
485[presentation-name].html
486[presentation-name]-assets/
487```
488 
489### HTML Architecture
490 
491Follow this structure for all presentations:
492 
493```html
494<!DOCTYPE html>
495<html lang="en">
496<head>
497 <meta charset="UTF-8">
498 <meta name="viewport" content="width=device-width, initial-scale=1.0">
499 <title>Presentation Title</title>
500 
501 <!-- Fonts (use Fontshare or Google Fonts) -->
502 <link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=...">
503 
504 <style>
505 /* ===========================================
506 CSS CUSTOM PROPERTIES (THEME)
507 Easy to modify: change these to change the whole look
508 =========================================== */
509 :root {
510 /* Colors */
511 --bg-primary: #0a0f1c;
512 --bg-secondary: #111827;
513 --text-primary: #ffffff;
514 --text-secondary: #9ca3af;
515 --accent: #00ffcc;
516 --accent-glow: rgba(0, 255, 204, 0.3);
517 
518 /* Typography - MUST use clamp() for responsive scaling */
519 --font-display: 'Clash Display', sans-serif;
520 --font-body: 'Satoshi', sans-serif;
521 --title-size: clamp(2rem, 6vw, 5rem);
522 --subtitle-size: clamp(0.875rem, 2vw, 1.25rem);
523 --body-size: clamp(0.75rem, 1.2vw, 1rem);
524 
525 /* Spacing - MUST use clamp() for responsive scaling */
526 --slide-padding: clamp(1.5rem, 4vw, 4rem);
527 --content-gap: clamp(1rem, 2vw, 2rem);
528 
529 /* Animation */
530 --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
531 --duration-normal: 0.6s;
532 }
533 
534 /* ===========================================
535 BASE STYLES
536 =========================================== */
537 * {
538 margin: 0;
539 padding: 0;
540 box-sizing: border-box;
541 }
542 
543 html {
544 scroll-behavior: smooth;
545 scroll-snap-type: y mandatory;
546 height: 100%;
547 }
548 
549 body {
550 font-family: var(--font-body);
551 background: var(--bg-primary);
552 color: var(--text-primary);
553 overflow-x: hidden;
554 height: 100%;
555 }
556 
557 /* ===========================================
558 SLIDE CONTAINER
559 CRITICAL: Each slide MUST fit exactly in viewport
560 - Use height: 100vh (NOT min-height)
561 - Use overflow: hidden to prevent scroll
562 - Content must scale with clamp() values
563 =========================================== */
564 .slide {
565 width: 100vw;
566 height: 100vh; /* EXACT viewport height - no scrolling */
567 height: 100dvh; /* Dynamic viewport height for mobile */
568 padding: var(--slide-padding);
569 scroll-snap-align: start;
570 display: flex;
571 flex-direction: column;
572 justify-content: center;
573 position: relative;
574 overflow: hidden; /* Prevent any content overflow */
575 }
576 
577 /* Content wrapper that prevents overflow */
578 .slide-content {
579 flex: 1;
580 display: flex;
581 flex-direction: column;
582 justify-content: center;
583 max-height: 100%;
584 overflow: hidden;
585 }
586 
587 /* ===========================================
588 RESPONSIVE BREAKPOINTS
589 Adjust content for different screen sizes
590 =========================================== */
591 @media (max-height: 600px) {
592 :root {
593 --slide-padding: clamp(1rem, 3vw, 2rem);
594 --content-gap: clamp(0.5rem, 1.5vw, 1rem);
595 }
596 }
597 
598 @media (max-width: 768px) {
599 :root {
600 --title-size: clamp(1.5rem, 8vw, 3rem);
601 }
602 }
603 
604 @media (max-height: 500px) and (orientation: landscape) {
605 /* Extra compact for landscape phones */
606 :root {
607 --title-size: clamp(1.25rem, 5vw, 2rem);
608 --slide-padding: clamp(0.75rem, 2vw, 1.5rem);
609 }
610 }
611 
612 /* ===========================================
613 ANIMATIONS
614 Trigger via .visible class (added by JS on scroll)
615 =========================================== */
616 .reveal {
617 opacity: 0;
618 transform: translateY(30px);
619 transition: opacity var(--duration-normal) var(--ease-out-expo),
620 transform var(--duration-normal) var(--ease-out-expo);
621 }
622 
623 .slide.visible .reveal {
624 opacity: 1;
625 transform: translateY(0);
626 }
627 
628 /* Stagger children */
629 .reveal:nth-child(1) { transition-delay: 0.1s; }
630 .reveal:nth-child(2) { transition-delay: 0.2s; }
631 .reveal:nth-child(3) { transition-delay: 0.3s; }
632 .reveal:nth-child(4) { transition-delay: 0.4s; }
633 
634 /* ... more styles ... */
635 </style>
636</head>
637<body>
638 <!-- Progress bar (optional) -->
639 <div class="progress-bar"></div>
640 
641 <!-- Navigation dots (optional) -->
642 <nav class="nav-dots">
643 <!-- Generated by JS -->
644 </nav>
645 
646 <!-- Slides -->
647 <section class="slide title-slide">
648 <h1 class="reveal">Presentation Title</h1>
649 <p class="reveal">Subtitle or author</p>
650 </section>
651 
652 <section class="slide">
653 <h2 class="reveal">Slide Title</h2>
654 <p class="reveal">Content...</p>
655 </section>
656 
657 <!-- More slides... -->
658 
659 <script>
660 /* ===========================================
661 SLIDE PRESENTATION CONTROLLER
662 Handles navigation, animations, and interactions
663 =========================================== */
664 
665 class SlidePresentation {
666 constructor() {
667 // ... initialization
668 }
669 
670 // ... methods
671 }
672 
673 // Initialize
674 new SlidePresentation();
675 </script>
676</body>
677</html>
678```
679 
680### Required JavaScript Features
681 
682Every presentation should include:
683 
6841. **SlidePresentation Class** — Main controller
685 - Keyboard navigation (arrows, space)
686 - Touch/swipe support
687 - Mouse wheel navigation
688 - Progress bar updates
689 - Navigation dots
690 
6912. **Intersection Observer** — For scroll-triggered animations
692 - Add `.visible` class when slides enter viewport
693 - Trigger CSS animations efficiently
694 
6953. **Optional Enhancements** (based on style):
696 - Custom cursor with trail
697 - Particle system background (canvas)
698 - Parallax effects
699 - 3D tilt on hover
700 - Magnetic buttons
701 - Counter animations
702 
703### Code Quality Requirements
704 
705**Comments:**
706Every section should have clear comments explaining:
707- What it does
708- Why it exists
709- How to modify it
710 
711```javascript
712/* ===========================================
713 CUSTOM CURSOR
714 Creates a stylized cursor that follows mouse with a trail effect.
715 - Uses lerp (linear interpolation) for smooth movement
716 - Grows larger when hovering over interactive elements
717 =========================================== */
718class CustomCursor {
719 constructor() {
720 // ...
721 }
722}
723```
724 
725**Accessibility:**
726- Semantic HTML (`<section>`, `<nav>`, `<main>`)
727- Keyboard navigation works
728- ARIA labels where needed
729- Reduced motion support
730 
731```css
732@media (prefers-reduced-motion: reduce) {
733 .reveal {
734 transition: opacity 0.3s ease;
735 transform: none;
736 }
737}
738```
739 
740**Responsive & Viewport Fitting (CRITICAL):**
741 
742**See the "CRITICAL: Viewport Fitting Requirements" section above for complete CSS and guidelines.**
743 
744Quick reference:
745- Every `.slide` must have `height: 100vh; height: 100dvh; overflow: hidden;`
746- All typography and spacing must use `clamp()`
747- Respect content density limits (max 4-6 bullets, max 6 cards, etc.)
748- Include breakpoints for heights: 700px, 600px, 500px
749- When content doesn't fit → split into multiple slides, never scroll
750 
751---
752 
753## Phase 4: PPT Conversion
754 
755When converting PowerPoint files:
756 
757### Step 4.1: Extract Content
758 
759Use Python with `python-pptx` to extract:
760 
761```python
762from pptx import Presentation
763from pptx.util import Inches, Pt
764import json
765import os
766import base64
767 
768def extract_pptx(file_path, output_dir):
769 """
770 Extract all content from a PowerPoint file.
771 Returns a JSON structure with slides, text, and images.
772 """
773 prs = Presentation(file_path)
774 slides_data = []
775 
776 # Create assets directory
777 assets_dir = os.path.join(output_dir, 'assets')
778 os.makedirs(assets_dir, exist_ok=True)
779 
780 for slide_num, slide in enumerate(prs.slides):
781 slide_data = {
782 'number': slide_num + 1,
783 'title': '',
784 'content': [],
785 'images': [],
786 'notes': ''
787 }
788 
789 for shape in slide.shapes:
790 # Extract title
791 if shape.has_text_frame:
792 if shape == slide.shapes.title:
793 slide_data['title'] = shape.text
794 else:
795 slide_data['content'].append({
796 'type': 'text',
797 'content': shape.text
798 })
799 
800 # Extract images
801 if shape.shape_type == 13: # Picture
802 image = shape.image
803 image_bytes = image.blob
804 image_ext = image.ext
805 image_name = f"slide{slide_num + 1}_img{len(slide_data['images']) + 1}.{image_ext}"
806 image_path = os.path.join(assets_dir, image_name)
807 
808 with open(image_path, 'wb') as f:
809 f.write(image_bytes)
810 
811 slide_data['images'].append({
812 'path': f"assets/{image_name}",
813 'width': shape.width,
814 'height': shape.height
815 })
816 
817 # Extract notes
818 if slide.has_notes_slide:
819 notes_frame = slide.notes_slide.notes_text_frame
820 slide_data['notes'] = notes_frame.text
821 
822 slides_data.append(slide_data)
823 
824 return slides_data
825```
826 
827### Step 4.2: Confirm Content Structure
828 
829Present the extracted content to the user:
830 
831```
832I've extracted the following from your PowerPoint:
833 
834**Slide 1: [Title]**
835- [Content summary]
836- Images: [count]
837 
838**Slide 2: [Title]**
839- [Content summary]
840- Images: [count]
841 
842...
843 
844All images have been saved to the assets folder.
845 
846Does this look correct? Should I proceed with style selection?
847```
848 
849### Step 4.3: Style Selection
850 
851Proceed to Phase 2 (Style Discovery) with the extracted content in mind.
852 
853### Step 4.4: Generate HTML
854 
855Convert the extracted content into the chosen style, preserving:
856- All text content
857- All images (referenced from assets folder)
858- Slide order
859- Any speaker notes (as HTML comments or separate file)
860 
861---
862 
863## Phase 5: Delivery
864 
865### Final Output
866 
867When the presentation is complete:
868 
8691. **Clean up temporary files**
870 - Delete `.claude-design/slide-previews/` if it exists
871 
8722. **Open the presentation**
873 - Use `open [filename].html` to launch in browser
874 
8753. **Provide summary**
876```
877Your presentation is ready!
878 
879📁 File: [filename].html
880🎨 Style: [Style Name]
881📊 Slides: [count]
882 
883**Navigation:**
884- Arrow keys (← →) or Space to navigate
885- Scroll/swipe also works
886- Click the dots on the right to jump to a slide
887 
888**To customize:**
889- Colors: Look for `:root` CSS variables at the top
890- Fonts: Change the Fontshare/Google Fonts link
891- Animations: Modify `.reveal` class timings
892 
893Would you like me to make any adjustments?
894```
895 
896---
897 
898## Style Reference: Effect → Feeling Mapping
899 
900Use this guide to match animations to intended feelings:
901 
902### Dramatic / Cinematic
903- Slow fade-ins (1-1.5s)
904- Large scale transitions (0.9 → 1)
905- Dark backgrounds with spotlight effects
906- Parallax scrolling
907- Full-bleed images
908 
909### Techy / Futuristic
910- Neon glow effects (box-shadow with accent color)
911- Particle systems (canvas background)
912- Grid patterns
913- Monospace fonts for accents
914- Glitch or scramble text effects
915- Cyan, magenta, electric blue palette
916 
917### Playful / Friendly
918- Bouncy easing (spring physics)
919- Rounded corners (large radius)
920- Pastel or bright colors
921- Floating/bobbing animations
922- Hand-drawn or illustrated elements
923 
924### Professional / Corporate
925- Subtle, fast animations (200-300ms)
926- Clean sans-serif fonts
927- Navy, slate, or charcoal backgrounds
928- Precise spacing and alignment
929- Minimal decorative elements
930- Data visualization focus
931 
932### Calm / Minimal
933- Very slow, subtle motion
934- High whitespace
935- Muted color palette
936- Serif typography
937- Generous padding
938- Content-focused, no distractions
939 
940### Editorial / Magazine
941- Strong typography hierarchy
942- Pull quotes and callouts
943- Image-text interplay
944- Grid-breaking layouts
945- Serif headlines, sans-serif body
946- Black and white with one accent
947 
948---
949 
950## Animation Patterns Reference
951 
952### Entrance Animations
953 
954```css
955/* Fade + Slide Up (most common) */
956.reveal {
957 opacity: 0;
958 transform: translateY(30px);
959 transition: opacity 0.6s var(--ease-out-expo),
960 transform 0.6s var(--ease-out-expo);
961}
962 
963.visible .reveal {
964 opacity: 1;
965 transform: translateY(0);
966}
967 
968/* Scale In */
969.reveal-scale {
970 opacity: 0;
971 transform: scale(0.9);
972 transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
973}
974 
975/* Slide from Left */
976.reveal-left {
977 opacity: 0;
978 transform: translateX(-50px);
979 transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
980}
981 
982/* Blur In */
983.reveal-blur {
984 opacity: 0;
985 filter: blur(10px);
986 transition: opacity 0.8s, filter 0.8s var(--ease-out-expo);
987}
988```
989 
990### Background Effects
991 
992```css
993/* Gradient Mesh */
994.gradient-bg {
995 background:
996 radial-gradient(ellipse at 20% 80%, rgba(120, 0, 255, 0.3) 0%, transparent 50%),
997 radial-gradient(ellipse at 80% 20%, rgba(0, 255, 200, 0.2) 0%, transparent 50%),
998 var(--bg-primary);
999}
1000 
1001/* Noise Texture */
1002.noise-bg {
1003 background-image: url("data:image/svg+xml,..."); /* Inline SVG noise */
1004}
1005 
1006/* Grid Pattern */
1007.grid-bg {
1008 background-image:
1009 linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
1010 linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
1011 background-size: 50px 50px;
1012}
1013```
1014 
1015### Interactive Effects
1016 
1017```javascript
1018/* 3D Tilt on Hover */
1019class TiltEffect {
1020 constructor(element) {
1021 this.element = element;
1022 this.element.style.transformStyle = 'preserve-3d';
1023 this.element.style.perspective = '1000px';
1024 this.bindEvents();
1025 }
1026 
1027 bindEvents() {
1028 this.element.addEventListener('mousemove', (e) => {
1029 const rect = this.element.getBoundingClientRect();
1030 const x = (e.clientX - rect.left) / rect.width - 0.5;
1031 const y = (e.clientY - rect.top) / rect.height - 0.5;
1032 
1033 this.element.style.transform = `
1034 rotateY(${x * 10}deg)
1035 rotateX(${-y * 10}deg)
1036 `;
1037 });
1038 
1039 this.element.addEventListener('mouseleave', () => {
1040 this.element.style.transform = 'rotateY(0) rotateX(0)';
1041 });
1042 }
1043}
1044```
1045 
1046---
1047 
1048## Troubleshooting
1049 
1050### Common Issues
1051 
1052**Fonts not loading:**
1053- Check Fontshare/Google Fonts URL
1054- Ensure font names match in CSS
1055 
1056**Animations not triggering:**
1057- Verify Intersection Observer is running
1058- Check that `.visible` class is being added
1059 
1060**Scroll snap not working:**
1061- Ensure `scroll-snap-type` on html/body
1062- Each slide needs `scroll-snap-align: start`
1063 
1064**Mobile issues:**
1065- Disable heavy effects at 768px breakpoint
1066- Test touch events
1067- Reduce particle count or disable canvas
1068 
1069**Performance issues:**
1070- Use `will-change` sparingly
1071- Prefer `transform` and `opacity` animations
1072- Throttle scroll/mousemove handlers
1073 
1074---
1075 
1076## Related Skills
1077 
1078- **learn** — Generate FORZARA.md documentation for the presentation
1079- **frontend-design** — For more complex interactive pages beyond slides
1080- **design-and-refine:design-lab** — For iterating on component designs
1081 
1082---
1083 
1084## Example Session Flow
1085 
10861. User: "I want to create a pitch deck for my AI startup"
10872. Skill asks about purpose, length, content
10883. User shares their bullet points and key messages
10894. Skill asks about desired feeling (Impressed + Excited)
10905. Skill generates 3 style previews
10916. User picks Style B (Neon Cyber), asks for darker background
10927. Skill generates full presentation with all slides
10938. Skill opens the presentation in browser
10949. User requests tweaks to specific slides
109510. Final presentation delivered
1096 
1097---
1098 
1099## Conversion Session Flow
1100 
11011. User: "Convert my slides.pptx to a web presentation"
11022. Skill extracts content and images from PPT
11033. Skill confirms extracted content with user
11044. Skill asks about desired feeling/style
11055. Skill generates style previews
11066. User picks a style
11077. Skill generates HTML presentation with preserved assets
11088. Final presentation delivered
1109 

Discussion

Alternatives

Also in Styling & layout