From agent-skills
Generates self-contained HTML or React slide decks for fullscreen browser presentations from ideas, outlines, or content. Pipeline: outline structure, select design theme, output single artifact.
npx claudepluginhub unclecatvn/agent-skillsThis skill uses the workspace's default tool permissions.
Produce **a single HTML or React artifact file** containing a complete slide deck that can:
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Produce a single HTML or React artifact file containing a complete slide deck that can:
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.
When the user provides a request, first build an outline mentally (no need to display it unless the user asks):
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.
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:
Create a single file (.html or .jsx) containing everything:
<!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>
Each slide should use the layout that best fits its content:
.two-column layout: text on left, visuals/list/cards on rightwidth: 100vw; height: 100vh — each slide fills the entire screen<img src="placeholder">. Replace with CSS shapes, gradients, icons (emoji or Lucide for React), or inline SVGs@media print rules so each slide becomes one printed pageIf 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.
@media print)After initial creation, the user can request modifications:
On each edit, keep unchanged slides intact and only update what the user requested.