--- name: interactive-slides description: "Creates interactive, animated web presentations viewable in any browser — not PowerPoint files. Use this skill when the user wants a web-based or browser-viewable presentation, an interactive slide deck, or something more engaging than a static slide file. Perfect for pitches, proposals, data stories, reports, and technical talks. Trigger when the user says 'interactive presentation', 'web slides', 'web presentation', 'HTML presentation', or when they want slides that can be shared as a URL, need animations/motion/interactivity, or want to 'wow' their audience with something live on the web. Also trigger for scroll-based storytelling or immersive one-pagers when the context is presenting information. If the user asks for a 'presentation' or 'deck' without specifying .pptx, prefer this skill over the pptx skill unless they explicitly want a PowerPoint file." --- # Interactive Presentation Skill You're creating an interactive web presentation — a self-contained HTML file that opens beautifully in any browser. Not a PowerPoint. Something alive: animated, polished, web-native. Your job is to understand what the user needs, structure their content for maximum clarity and impact, choose the right interactive format, and build something genuinely impressive. ## Phase 1: Discovery **This phase is mandatory. Do not skip it, do not abbreviate it, do not jump to style or build.** The discovery phase has a strict order: 1. Ask audience, goal, and delivery questions 2. Generate the style preview and get a pick 3. Produce a ghost list for confirmation (Phase 2) 4. Only then build Before writing a line of code, understand the full picture. Ask these questions — but don't just list them dryly. **Make proposals and offer clear options** based on what you already know. If the user said "investor pitch for a fintech startup," you already know quite a bit — lead with an educated guess and let them confirm or adjust. ### Questions to cover (adapt based on what you already know) **Audience & Goal** - Who will see this? (executives, developers, customers, investors, students...) - What's the core message — the one thing they should remember? - What do you want them to feel or do after? **Delivery** - Will you present it live (you control the flow) or share it for async viewing (they navigate themselves)? - Will it be shown on a big screen, shared as a URL, or embedded in a website? **Content** - What content do you have? Ask them to paste an outline, upload a document, describe the topic, or share a URL. - If they give you raw content, tell them what you're going to do with it before doing it. **Style & Brand** Always ask this before generating any style preview. Frame it as a simple choice: > *"Do you have a brand kit or style guidelines you'd like to use — or should I show you a few preset styles to pick from?"* **If they have a brand kit**, accept any of these (one is enough — don't ask for all of them): | Input | How to share it | |-------|----------------| | **Hex colors + font names** | Paste directly: `"primary: #2B4EFF, accent: #FF5733, body font: Helvetica Neue"` | | **Logo file** | File path or URL — placed on every slide | | **PPT template** | `.pptx` file path — used as the visual base for both HTML and editable export | | **Canva Brand Kit** | In Canva: Brand Kit → copy hex colors + font names, or Share → Download → PowerPoint to get a template file | Apply the brand colors to the HTML by creating a custom `:root {}` block instead of using a preset. Map their colors to `--bg`, `--accent`, `--text-primary`, `--text-secondary`, `--surface`. Load their fonts from Google Fonts if available. **If they don't have a brand kit**, proceed with the preset style picker: - Don't ask the user to describe their aesthetic in words — most people can't. Show them options instead. - Pick 3 presets from `STYLE_PRESETS.md` that fit the topic and audience. - Generate a `style-preview.html` file using the Style Preview Template from `STYLE_PRESETS.md`, showing all 3 presets as mini swatches side by side. - Tell the user: *"Open `style-preview.html` in your browser — which one feels right? Or describe something different."* - Wait for their pick before building. This one step prevents the most common iteration loop. **Interactivity** (recommend based on delivery mode) - Live presentation → recommend slide mode with animated reveals - Async share → recommend slide mode with self-navigation as the default; ask if they'd prefer scroll-based storytelling instead - Training or audience participation → recommend fully interactive (quizzes, branching) - Portfolio/showcase → ask: slide deck or scroll-based cinematic experience? **Default to slide mode (Mode A) unless the user specifically asks for scroll-based.** Slide mode works for both live and async sharing, gives users a familiar navigation experience, and is easier to repurpose as a .pptx later. Scroll story is a great choice but should be an opt-in, not the default. Be direct with your recommendation: "I'd go with slide mode — click/arrow-key navigation, works live or shared async. Want scroll-based storytelling instead? It reads more like an article and works well for longer reports or portfolio pieces." --- ## Phase 2: Content Processing ### Always produce a ghost list first (unless the user already gave you one) When the source is a document, URL, or free-form description, **do not go straight to building**. First convert the source into a proposed slide-by-slide ghost list and confirm it with the user. This prevents the most expensive iteration: building the wrong structure. **Ghost list format:** ``` Slide 1 — [Title] Headline: [One-sentence slide headline] Content: [What goes on this slide — key point, stat, visual idea] Slide 2 — [Title] Headline: ... Content: ... ``` After presenting the ghost list, append a density recommendation based on audience and delivery mode from Phase 1 — do not ask again, just state it. Then ask for confirmation on both together. **Density reference table:** | Audience / Mode | Recommended density | |-----------------|-------------------| | Executive, board, investor — live presentation | **Lean** — labels only, presenter fills verbally | | Senior IC, growth, product — live presentation | **Lean-to-medium** — short labels + one sublabel line max | | Technical audience — live presentation | **Medium** — labels + brief descriptor, code/diagram preferred over prose | | Any audience — async share or scroll story | **Medium-to-rich** — more text is fine, reader controls pace | **Format for the combined confirmation message:** > "Does this structure work? Anything to add, cut, or reorder? > > Also — based on [audience] viewing this as a [live/async] presentation, I'd recommend **[density level]**: [one-sentence explanation of what that means in practice]. Let me know if you'd like a different approach." Wait for the user's reply before proceeding. If they adjust the density, note it and apply it in the build. **Density is a global default, not a per-slide rule.** Apply the recommended level as the baseline, but let individual slides deviate when the content demands it: - Title and section-break slides are always lean regardless of global density — one strong line, nothing else. - Evidence and data slides may carry more text even in a lean deck — a chart without context misleads. - Conclusion and call-to-action slides return to lean — end on a clear, memorable line, not a wall of text. - Architecture, process, and comparison slides can go medium even in lean decks — the structure IS the content. --- ### Visual Treatment Decision (runs only if lean or lean-to-medium density is confirmed) **Step 2 — If lean or lean-to-medium density is chosen, ask about visual treatment for sparse slides:** When content per slide is minimal, cards and layouts can look visually empty. Resolve this once globally — the choice applies across the whole deck. Generate a small `visual-treatment-preview.html` file (similar to the style preview) showing these three options side by side using one of the deck's actual slides as the example: | Option | Description | |--------|-------------| | **B — Decorative numbers** | Large faded 01 / 02 / 03 in each card. Fills visual weight without adding reading load. Consistent with lesson/principle slides. | | **C — Icon anchors** | A relevant icon or emoji at the top of each card. Gives each item a visual identity and a focal point. | | **D — Layout restructure** | Drop the card grid. Use a large bold number or word as a left-side anchor, with items as a vertical list on the right. High visual impact, works well for 3–5 item slides. | Tell the user: *"Open `visual-treatment-preview.html` — which approach do you want for slides where content is sparse?"* Apply the chosen treatment consistently across all relevant slides in the build. **Only after both decisions are made, proceed to Phase 3 (Mode) and Phase 4 (Build).** --- ### Handling each input type **Ghost list / outline (user already gave you one)**: Skip the ghost list step — you have it. Expand thin bullets into full slide content with context, transitions, and narrative flow. Still confirm the structure if it's complex. **Document (PDF, Word, Markdown, URL)**: Read and extract. Identify the key ideas, reduce to essentials, create a logical arc. Build a ghost list showing what you're keeping, what you're cutting, and why. Present it for confirmation before building. **Free-form description**: Ask clarifying questions if the topic is vague, then draft the ghost list yourself — slide titles, key messages, supporting points. Confirm before building. ### Communication best practices (apply to all content) - **Pyramid Principle**: Lead with the conclusion. One clear headline per slide that stands alone. Details follow, not precede. - **One idea per slide**: If you need two sentences to summarize a slide's point, split it. - **Show, don't tell**: Replace bullet lists with visuals wherever possible — stats as large callouts, comparisons as side-by-side layouts, processes as step diagrams. - **Narrative arc**: Hook → Problem/Tension → Solution → Evidence → Call to Action. Every presentation tells a story. - **Audience calibration**: Executives want conclusions fast. Technical audiences want depth and data. Investors want traction and vision. Students want clarity. Match the vocabulary, depth, and pacing to who's watching. **Content density is not a fixed rule — it is a function of audience and delivery mode.** A live executive deck should have labels only (presenter fills verbally). An async technical report can carry full paragraphs. Never apply a blanket bullet limit — the answer lives in Phase 1: live presentation means lean, async means the text must stand on its own. - **Rule of clarity**: Every slide should answer "so what?" If it doesn't, cut it or reframe it. --- ## Phase 3: Choose the Presentation Mode **Default recommendation: Mode A (Slide Deck).** Only switch to Mode B if the user confirms they want scroll-based, or if the content is clearly a long-form report/portfolio piece. Recommend and confirm one of these three modes before building: ### Mode A: Slide Deck Classic slides, keyboard/click navigation (← → arrow keys, space bar). Best for live presentations. - Each slide fills the screen - Content enters with animated reveals (staggered text, charts that draw in, images that fade) - Smooth slide-to-slide transitions - Optional: presenter notes, progress bar, slide counter ### Mode B: Scroll Story User scrolls down to advance through the narrative. Best for async sharing, reports, one-pagers. - Each section locks into view as you scroll (scroll-jacking or smooth scroll) - Content animates in as it enters the viewport - Feels like an interactive article or annual report - No navigation UI — scrolling IS the interaction > **Note:** Do NOT use `scaleToViewport()` for scroll stories — they are full-page documents, not fixed-canvas slides. Viewport scaling is only for Mode A and Mode C. **HTML structure:** ```html

Headline

Supporting text

``` **Required CSS:** ```css /* Full-page snap scrolling — each scroll lands on a complete section */ html { scroll-snap-type: y mandatory; overflow-y: scroll; height: 100%; } body { margin: 0; height: 100%; background: var(--bg); } .story-section { height: 100vh; /* exact — snap requires fixed height, not min-height */ overflow: hidden; /* clip content that exceeds one screen */ scroll-snap-align: start; scroll-snap-stop: always; /* prevents fast-scroll from skipping sections */ display: flex; align-items: center; justify-content: center; padding: 5rem; /* rem — scales with root font size scaler below */ position: relative; box-sizing: border-box; } .section-content { max-width: 60rem; /* rem — scales with root font size scaler below */ width: 100%; } /* Elements start invisible — ScrollTrigger reveals them on scroll */ .reveal { opacity: 0; transform: translateY(28px); } ``` > **scroll-snap-stop: always** is what makes "one section per scroll" work. Without it, a fast trackpad swipe can skip sections entirely. With it, every scroll gesture lands on exactly the next section — no exceptions. **Viewport-proportional scaling — REQUIRED for scroll stories:** Scroll stories cannot use `scaleToViewport()` (that's for fixed-canvas slide mode only). Instead, use a **root font size scaler**: a small JS snippet sets `html { font-size }` proportional to viewport width. All layout values then use `rem`, so the entire layout — headings, body text, padding, column width — scales together as the viewport grows, exactly like `scaleToViewport()` does for slide mode. **Required JS (add near the top of your script block, before GSAP init):** ```javascript // === SCROLL STORY SCALING === // Scales the entire layout by adjusting the root font size. // All rem-based values (fonts, padding, max-width) grow proportionally. // Reference: 1rem = 16px at a 1280px-wide viewport. const STORY_REF_WIDTH = 1280; function scaleStoryLayout() { const scale = Math.min(Math.max(window.innerWidth / STORY_REF_WIDTH, 0.5), 2.5); document.documentElement.style.fontSize = (16 * scale) + 'px'; } window.addEventListener('resize', scaleStoryLayout); scaleStoryLayout(); // run on load ``` **Required CSS for typography (all in rem — scales automatically):** ```css .story-section h1 { font-size: 3.5rem; line-height: 1.15; } .story-section h2 { font-size: 2.5rem; line-height: 1.25; } .story-section h3 { font-size: 1.75rem; line-height: 1.35; } .story-section p, .story-section li { font-size: 1.1rem; line-height: 1.75; } .story-section .stat { font-size: 6rem; font-weight: 700; } .story-section .label { font-size: 0.75rem; letter-spacing: 0.12em; text-transform: uppercase; } ``` > **Why root font size instead of clamp():** `clamp()` only scales font sizes, but the content column width (`max-width`) and padding stay fixed in `px` — so on a 2560px projector screen, you get bigger text inside the same 960px box. Root font scaling moves the entire layout together: fonts, padding, and column width all scale as one unit, giving the same proportional feel as `scaleToViewport()` on slide mode. The `Math.min(..., 2.5)` cap prevents runaway scaling on ultra-wide displays; `Math.max(..., 0.5)` keeps it readable on small screens. **Reveal animation setup — use IntersectionObserver, NOT ScrollTrigger:** With CSS snap scrolling, sections jump into view rather than scrolling progressively through intermediate positions. ScrollTrigger's position-based triggers (`start: 'top 82%'`) can miss the snap moment entirely. Use IntersectionObserver instead — it fires when a section becomes visible regardless of how it got there (snap scroll, dot nav click, keyboard). ```javascript // Observe each section — when it snaps into view, animate its .reveal elements const revealObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (!entry.isIntersecting) return; const section = entry.target; // Individual .reveal elements (not inside a stagger-group) const singles = [...section.querySelectorAll('.reveal')] .filter(el => !el.closest('.stagger-group')); gsap.to(singles, { opacity: 1, y: 0, duration: 0.7, ease: 'power2.out' }); // Stagger groups — children animate in sequence section.querySelectorAll('.stagger-group').forEach(group => { gsap.to(group.querySelectorAll('.reveal'), { opacity: 1, y: 0, duration: 0.6, stagger: 0.12, ease: 'power2.out' }); }); revealObserver.unobserve(section); // animate once per section }); }, { threshold: 0.4 }); // fires when 40% of the section is visible document.querySelectorAll('.story-section').forEach(s => revealObserver.observe(s)); ``` **Reading progress bar (always include for scroll stories):** ```html
``` ```css .progress-bar { position: fixed; top: 0; left: 0; height: 2px; width: 0%; background: var(--accent); z-index: 1000; transition: width 0.08s linear; } ``` ```javascript window.addEventListener('scroll', () => { const scrolled = window.scrollY; const maxScroll = document.body.scrollHeight - window.innerHeight; document.getElementById('progressBar').style.width = Math.min((scrolled / maxScroll) * 100, 100) + '%'; }, { passive: true }); ``` **Vertical section-dot nav (recommended for stories with 5+ sections):** ```html ``` ```css .section-nav { position: fixed; right: 28px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 10px; z-index: 100; } .section-nav .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text-secondary); opacity: 0.3; cursor: pointer; border: none; padding: 0; transition: all 0.3s; } .section-nav .dot.active { background: var(--accent); opacity: 1; transform: scale(1.4); } ``` ```javascript // Build section dots and highlight active section via IntersectionObserver const sections = document.querySelectorAll('.story-section'); const nav = document.getElementById('sectionNav'); sections.forEach((sec, i) => { const dot = document.createElement('button'); dot.className = 'dot' + (i === 0 ? ' active' : ''); dot.setAttribute('aria-label', `Go to section ${i + 1}`); dot.addEventListener('click', () => sec.scrollIntoView({ behavior: 'smooth' })); nav.appendChild(dot); }); const sectionObserver = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { const i = [...sections].indexOf(e.target); document.querySelectorAll('.section-nav .dot') .forEach((d, j) => d.classList.toggle('active', j === i)); } }); }, { threshold: 0.5 }); sections.forEach(s => sectionObserver.observe(s)); ``` ### Mode C: Interactive Deck Slide-based but with embedded interactions. Best for training, demos, proposals with choices. - Quizzes with scoring - Expandable detail panels ("click to learn more") - Branching paths ("choose your scenario") - Embedded calculators or configurators - Data filters / toggles --- ## Phase 4: Build ### Technical approach Build a **single self-contained HTML file** that works in any modern browser without a server. **Libraries (load via CDN — fast and reliable):** - **GSAP** + **ScrollTrigger**: all animations. Don't use CSS animations for anything complex. - **Chart.js**: data visualization when needed (charts, graphs) - **Google Fonts**: always load from the chosen preset in `STYLE_PRESETS.md` — never default to Inter, Roboto, or Arial. ```html ``` **CSS variables:** Copy the full `:root {}` block from the chosen preset in `STYLE_PRESETS.md` and use these variables throughout. Never hardcode colors or fonts inline — use `var(--accent)`, `var(--font-display)`, etc. This makes style iteration instant. **File structure (all inline in one .html):** ``` — Google Fonts, all CSS — slide/section markup