frontend-slides
SkillFiles & storageCreate 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.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the frontend-slides skill
What this skill tells your AI
The instructions your AI receives, as published by gooseworks-ai/goose-skills in skills/design/capabilities/create-html-slides/SKILL.md and read by ahel’s review.
Deprecated: This skill is superseded by
goose-graphics. Seeskills/composites/goose-graphics/(install withnpx 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
- 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:
/* ===========================================
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:
- ✅ Every
.slidehasheight: 100vh; height: 100dvh; overflow: hidden; - ✅ All font sizes use
clamp(min, preferred, max) - ✅ All spacing uses
clamp()or viewport units - ✅ Content containers have
max-heightconstraints - ✅ Images have
max-height: min(50vh, 400px)or similar - ✅ Grids use
auto-fitwithminmax()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
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:
- Ask which client this presentation is for (or infer from context)
- Check for
clients/<client-name>/brand/visual-identity.md - If the file exists: Read the "Slide Preset" section. Use its CSS
:rootcustom 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. - 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-extractorskill to generate the file, and continue. - 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:
-
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
.visibleclass when slides enter viewport - Trigger CSS animations efficiently
- Add
-
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
.slidemust haveheight: 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
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 1k
- Forks
- 206
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
frontend-slides-gooseworks-ai- Source
- github.com/gooseworks-ai/goose-skills