ai-vibe-slides
AI Vibe Slides — Beautiful HTML Slide Decks, Ready to Present
Goal
Produce a single HTML or React artifact file containing a complete slide deck that can:
- Present fullscreen directly in the browser
- Navigate via arrow keys, spacebar, or click
- Look professional, polished, and stylistically consistent
- Print or export to PDF when needed
Inspired by banana-slides: a 3-step pipeline of Idea → Outline → Finished Slides, but the output is a self-contained HTML file instead of a fullstack application.
Slide Creation Pipeline
Step 1: Understand the Request → Build an Outline
When the user provides a request, first build an outline mentally (no need to display it unless the user asks):
- Identify the main topic and target audience (students, business, tech talk...)
- Break it into logical sections:
- Slide 1: Cover (title + subtitle + author)
- Slides 2-3: Introduction / context
- Middle slides: Core content (one key idea per slide)
- Final slide: Conclusion / Call to action / Thank you
- Each slide should have: a title, 2-5 bullet points or visual content, and a layout type
If the user only gives a short sentence (e.g., "create slides about AI in healthcare"), automatically expand it into 8-12 slides with a logical structure.
Step 2: Choose a Design Direction
Based on context, commit to one clear design direction:
| Context | Suggested Style |
|---|---|
| Startup pitch deck | Bold, dark theme, gradient accents, strong sans-serif |
| Academic / education | Clean, light, diagram-heavy, readable fonts |
| Tech talk / conference | Modern dark, code-style typography, neon accents |
| Corporate / report | Minimal, professional, navy/white, serif headings |
| Creative / marketing | Colorful, asymmetric layout, bold typography |
| Kids / early education | Pastel, rounded corners, playful icons, large text |
General rules:
- Pick 2-3 primary colors and use them consistently across the entire deck
- 1 heading font + 1 body font (use Google Fonts)
- Minimal text, generous whitespace — max 5-6 lines per slide
- Clear visual hierarchy: large title → medium content → small notes
Step 3: Generate the HTML/React Slide Deck
Create a single file (.html or .jsx) containing everything:
HTML Slide Deck Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{Presentation Title}</title>
<link
href="https://fonts.googleapis.com/css2?family={Font1}&family={Font2}&display=swap"
rel="stylesheet"
/>
<style>
/* === RESET + BASE === */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* === SLIDE CONTAINER === */
.slide-deck {
width: 100vw;
height: 100vh;
overflow: hidden;
position: relative;
}
.slide {
width: 100%;
height: 100%;
display: none;
flex-direction: column;
justify-content: center;
padding: 60px 80px;
position: absolute;
top: 0;
left: 0;
}
.slide.active {
display: flex;
}
/* === TYPOGRAPHY === */
h1 {
font-family: "{Font1}", sans-serif;
font-size: 3.2rem;
margin-bottom: 1rem;
}
h2 {
font-family: "{Font1}", sans-serif;
font-size: 2.4rem;
margin-bottom: 1.5rem;
}
p,
li {
font-family: "{Font2}", sans-serif;
font-size: 1.4rem;
line-height: 1.8;
}
/* === THEME COLORS === */
:root {
--bg-primary: #0f172a;
--bg-slide: #1e293b;
--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--accent: #3b82f6;
--accent-2: #8b5cf6;
}
/* === NAVIGATION UI === */
.nav-hint {
position: fixed;
bottom: 20px;
right: 30px;
font-size: 0.8rem;
color: var(--text-secondary);
opacity: 0.5;
}
.slide-counter {
position: fixed;
bottom: 20px;
left: 30px;
font-size: 0.8rem;
color: var(--text-secondary);
}
/* === TRANSITIONS === */
.slide {
animation: fadeIn 0.4s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* === LAYOUT VARIANTS === */
.slide.cover {
justify-content: center;
align-items: center;
text-align: center;
}
.slide.two-column {
flex-direction: row;
gap: 60px;
align-items: center;
}
.slide.two-column .col {
flex: 1;
}
.slide.centered {
align-items: center;
text-align: center;
}
/* === VISUAL ELEMENTS === */
.card {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 24px;
margin: 8px 0;
}
.badge {
display: inline-block;
background: var(--accent);
color: white;
padding: 4px 14px;
border-radius: 20px;
font-size: 0.85rem;
}
.divider {
width: 60px;
height: 4px;
background: var(--accent);
border-radius: 2px;
margin: 16px 0;
}
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
/* === PRINT / PDF EXPORT === */
@media print {
.slide {
page-break-after: always;
display: flex !important;
position: relative;
}
.nav-hint,
.slide-counter {
display: none;
}
}
</style>
</head>
<body>
<div class="slide-deck" id="deck">
<!-- SLIDE 1: COVER -->
<div
class="slide cover active"
style="background: linear-gradient(135deg, var(--bg-primary), #1a1a2e);"
>
<div class="badge">Topic</div>
<h1 style="font-size: 3.8rem; margin-top: 20px;">Main Title</h1>
<p
style="color: var(--text-secondary); font-size: 1.3rem; margin-top: 12px;"
>
Short subtitle description
</p>
<div class="divider" style="margin: 20px auto;"></div>
<p style="color: var(--text-secondary); font-size: 1rem;">
Author — Date
</p>
</div>
<!-- SLIDE 2+: CONTENT -->
<div class="slide">
<h2>Slide Title</h2>
<div class="divider"></div>
<ul>
<li>Key point 1</li>
<li>Key point 2</li>
<li>Key point 3</li>
</ul>
</div>
<!-- ... more slides ... -->
</div>
<div class="slide-counter">
<span id="current">1</span> / <span id="total"></span>
</div>
<div class="nav-hint">← → or click to navigate</div>
<script>
const slides = document.querySelectorAll(".slide");
let currentSlide = 0;
document.getElementById("total").textContent = slides.length;
function showSlide(n) {
slides[currentSlide].classList.remove("active");
currentSlide = (n + slides.length) % slides.length;
slides[currentSlide].classList.add("active");
document.getElementById("current").textContent = currentSlide + 1;
}
document.addEventListener("keydown", (e) => {
if (e.key === "ArrowRight" || e.key === " ")
showSlide(currentSlide + 1);
if (e.key === "ArrowLeft") showSlide(currentSlide - 1);
if (e.key === "f") document.documentElement.requestFullscreen?.();
if (e.key === "Escape") document.exitFullscreen?.();
});
document.querySelector(".slide-deck").addEventListener("click", (e) => {
const x = e.clientX / window.innerWidth;
x > 0.5 ? showSlide(currentSlide + 1) : showSlide(currentSlide - 1);
});
</script>
</body>
</html>
Slide Layout Types
Each slide should use the layout that best fits its content:
1. Cover Slide
- Centered, extra-large font, gradient background
- Topic badge + main title + subtitle + author
2. Section Divider
- Only section title + number, accent color background
- Used to separate major sections of the presentation
3. Content + Bullets
- Left-aligned title + list of key points
- Use icons/emoji at the start of each bullet instead of plain dots
4. Two-Column
.two-columnlayout: text on left, visuals/list/cards on right- Great for comparisons, before-after, text+illustration
5. Cards Grid
- 2-3 column grid, each card containing icon + title + short description
- Great for features, benefits, team members
6. Big Number / Statistic
- Huge number in the center (font-size: 5rem+) + small label below
- Great for data points, KPIs, impact numbers
7. Quote / Highlight
- Large text, centered, with decorative quotation marks
- Different background (light accent color)
8. Timeline / Steps
- Horizontal or vertical flexbox, dots connecting each step
- Great for processes, roadmaps, history
9. Thank You / CTA
- Centered, simple, contact info or call to action
MANDATORY Design Rules
- 16:9 aspect ratio: Always use
width: 100vw; height: 100vh— each slide fills the entire screen - Minimal text: Maximum 6 lines per slide. If content is long → split into multiple slides
- Large font sizes: Heading ≥ 2.4rem, body ≥ 1.3rem — must be readable on a projector
- High contrast: Text must be clearly legible against its background. Verify visually
- Consistency: Same fonts, same color palette, same spacing throughout the entire deck
- No placeholder images: Never use
<img src="placeholder">. Replace with CSS shapes, gradients, icons (emoji or Lucide for React), or inline SVGs - Subtle animation: Only fadeIn on slide transition. No complex animations that distract
- Responsive fullscreen: Must work well at all screen sizes
- Print-ready: Include
@media printrules so each slide becomes one printed page
When Using React (.jsx) Instead of HTML
If creating a React artifact, use this pattern:
import { useState, useEffect, useCallback } from "react";
const slides = [
{ type: "cover", title: "...", subtitle: "..." },
{ type: "content", title: "...", points: ["...", "..."] },
{ type: "twoColumn", title: "...", left: "...", right: "..." },
// ...
];
export default function SlideDeck() {
const [current, setCurrent] = useState(0);
const next = useCallback(
() => setCurrent((c) => (c + 1) % slides.length),
[],
);
const prev = useCallback(
() => setCurrent((c) => (c - 1 + slides.length) % slides.length),
[],
);
useEffect(() => {
const handleKey = (e) => {
if (e.key === "ArrowRight" || e.key === " ") next();
if (e.key === "ArrowLeft") prev();
if (e.key === "f") document.documentElement.requestFullscreen?.();
};
window.addEventListener("keydown", handleKey);
return () => window.removeEventListener("keydown", handleKey);
}, [next, prev]);
const renderSlide = (slide) => {
switch (slide.type) {
case "cover":
return /* cover layout */;
case "content":
return /* content layout */;
case "twoColumn":
return /* two-column layout */;
// ...
}
};
return (
<div
className="w-screen h-screen overflow-hidden relative"
onClick={(e) => (e.clientX > window.innerWidth / 2 ? next() : prev())}
style={{ fontFamily: "'Outfit', sans-serif" }}
>
{renderSlide(slides[current])}
<div className="fixed bottom-5 left-8 text-sm opacity-40">
{current + 1} / {slides.length}
</div>
</div>
);
}
Advantages of React: Tailwind CSS utilities, Lucide icons, more complex logic, recharts for charts.
Pre-Delivery Checklist
- Correct number of slides (cover + content + closing)
- Arrow keys ← → work, click navigates
- Counter displays correct "X / N"
- Press F for fullscreen
- All text is readable with sufficient contrast
- No broken images or placeholders
- Google Fonts load correctly (or good fallback)
- Print/PDF exports properly (
@media print) - Consistent style throughout the entire deck
- Each slide has exactly one key idea, not overloaded with text
Natural Language Editing
After initial creation, the user can request modifications:
- "Switch to a light theme" → update CSS variables
- "Add 2 slides about case studies" → insert slides into the array
- "Slide 3 has too much text, split it" → refactor content across slides
- "Change the font to Playfair Display" → update Google Fonts link + CSS
- "Add a chart to slide 5" → use CSS chart or recharts (React)
On each edit, keep unchanged slides intact and only update what the user requested.