$npx -y skills add proyecto26/slides-ai-plugin --skill html-slidesUsed when the user asks to "create HTML slides", "generate an HTML presentation", "make animated slides", "build interactive slides", "create web slides", "GSAP presentation", "browser-based slides", "convert PPTX to HTML", "animated deck", "reveal.js slides", or when the output
| 1 | # HTML Slides — Animated, Viewport-Fitted Presentations |
| 2 | |
| 3 | Generate single-file, self-contained HTML presentations with professional animations, responsive viewport fitting, and curated style presets. Zero external dependencies at runtime — all CSS/JS inline. |
| 4 | |
| 5 | ## Architecture |
| 6 | |
| 7 | Every HTML presentation follows this structure: |
| 8 | |
| 9 | 1. **Single file** — One `.html` file with inline CSS and JS (no build tools) |
| 10 | 2. **Viewport fitted** — Each slide locks to `100vh`/`100dvh` with `overflow: hidden` |
| 11 | 3. **CSS custom properties** — All theming via `:root` variables for easy restyling |
| 12 | 4. **Semantic HTML** — `<section>` per slide, `<nav>` for controls |
| 13 | 5. **Progressive enhancement** — Works without JS, animations enhance with JS |
| 14 | |
| 15 | ## Mandatory Viewport Rules |
| 16 | |
| 17 | Apply the viewport base CSS from `assets/viewport-base.css` to EVERY presentation. Key rules: |
| 18 | |
| 19 | - Every slide: `height: 100vh; height: 100dvh; overflow: hidden` |
| 20 | - All typography uses `clamp(min, preferred, max)` for responsive scaling |
| 21 | - Images constrained to `min(50vh, 400px)` max height |
| 22 | - Responsive breakpoints at 700px, 600px, 500px heights |
| 23 | - `prefers-reduced-motion` support included |
| 24 | - Grid fallback: `grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr))` |
| 25 | |
| 26 | Never allow scrolling within a slide. If content exceeds capacity, split across multiple slides. |
| 27 | |
| 28 | **Edge Case: Negative Clamp Values** |
| 29 | Use `calc(-1 * clamp(...))` when you need negative viewport-edge spacing (e.g., negative margins, negative padding). This pattern preserves the clamp function's responsiveness while inverting the value. |
| 30 | |
| 31 | **iOS Safari 100vh Bug** |
| 32 | The `100vh` unit in iOS Safari includes the address bar, causing content to overflow. Always pair `100vh` with `100dvh` (dynamic viewport height) in fallback chains. Modern viewports ignore `100vh` if `100dvh` is present. |
| 33 | |
| 34 | ## Animation System |
| 35 | |
| 36 | ### CSS-First Approach (Default) |
| 37 | |
| 38 | Use CSS animations as the baseline. Apply `.reveal` class with staggered `transition-delay`: |
| 39 | |
| 40 | ```css |
| 41 | .reveal { |
| 42 | opacity: 0; |
| 43 | transform: translateY(20px); |
| 44 | transition: opacity 0.6s var(--ease-out-expo), transform 0.6s var(--ease-out-expo); |
| 45 | } |
| 46 | .reveal.visible { |
| 47 | opacity: 1; |
| 48 | transform: translateY(0); |
| 49 | } |
| 50 | ``` |
| 51 | |
| 52 | Trigger with Intersection Observer adding `.visible` class on viewport entry. |
| 53 | |
| 54 | **Easing**: `--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1)` for all entrance animations. |
| 55 | |
| 56 | ### GSAP Enhancement (When Requested) |
| 57 | |
| 58 | For sophisticated animations, load GSAP from CDN and use timeline-based choreography. Consult `references/animation-patterns.md` for detailed GSAP recipes and the [GSAP docs](https://gsap.com/docs/) for API reference. Key integration points: |
| 59 | |
| 60 | - Load GSAP + ScrollTrigger from `https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js` |
| 61 | - Use `gsap.timeline()` for sequenced slide entrance animations |
| 62 | - Apply `gsap.matchMedia()` for responsive animation behavior |
| 63 | - Respect `prefers-reduced-motion` — disable animations when active |
| 64 | |
| 65 | **Timeline Entrance with Position Parameter** |
| 66 | Stagger reveals without explicit delays using the position parameter in timeline. Example: |
| 67 | ```javascript |
| 68 | const tl = gsap.timeline(); |
| 69 | tl.to('.heading', { opacity: 1, duration: 0.6 }) |
| 70 | .to('.subtitle', { opacity: 1, duration: 0.4 }, '<0.2') // Starts 0.2s before heading ends |
| 71 | .to('.bullet', { opacity: 1, stagger: 0.1 }, '<0.15'); |
| 72 | ``` |
| 73 | |
| 74 | **SplitText for Headlines and Word/Char Reveals** |
| 75 | Animate individual words or characters in headlines: |
| 76 | ```javascript |
| 77 | gsap.registerPlugin(SplitText); |
| 78 | const split = new SplitText('.headline', { type: 'words,chars' }); |
| 79 | gsap.from(split.chars, { |
| 80 | opacity: 0, |
| 81 | duration: 0.4, |
| 82 | stagger: 0.05, |
| 83 | ease: 'back.out' |
| 84 | }); |
| 85 | ``` |
| 86 | |
| 87 | **ScrollTrigger for Slide-by-Slide Navigation** |
| 88 | Tie slide transitions to scroll position for interactive presentations: |
| 89 | ```javascript |
| 90 | gsap.registerPlugin(ScrollTrigger); |
| 91 | gsap.to('.slide', { |
| 92 | scrollTrigger: { |
| 93 | trigger: '.slide-container', |
| 94 | pin: true, |
| 95 | scrub: 1, |
| 96 | snap: 1 / slideCount |
| 97 | } |
| 98 | }); |
| 99 | ``` |
| 100 | |
| 101 | **Spring Physics Timing from Remotion** |
| 102 | Translate Remotion spring physics into GSAP elastic easing for natural motion: |
| 103 | - **Smooth** (damping: 200) → `elastic.out(1, 0.1)` — subtle bounce, feels polished |
| 104 | - **Snappy** (damping: 20) → `elastic.out(1, 0.35)` — noticeable spring, modern feel |
| 105 | - **Bouncy** (damping: 8) → `elastic.out(1, 0.5)` — playful bounce, energetic |
| 106 | |
| 107 | ### Animation Inventory |
| 108 | |
| 109 | | Animation | CSS Class | Use Case | |
| 110 | |-----------|----------| |