From jsphh-teaching
Generate styled HTML presentations from a topic or viewpoint. Supports multiple visual styles (sketch-notes, minimal, dark-pro, data-viz). Produces a self-contained single-file HTML deck with arrow-key navigation and zero dependencies. Use when (1) user wants to make a PPT, (2) user mentions "sketch notes", "簡報", "做 PPT", "agentic PPT", or "生成投影片".
How this skill is triggered — by the user, by Claude, or both
Slash command
/jsphh-teaching:sketch-note-pptThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You will generate a **single self-contained HTML file** that renders as a styled presentation. The output must be a fully working HTML deck — no frameworks, no npm, no build step. The file should work by simply opening it in a browser.
You will generate a single self-contained HTML file that renders as a styled presentation. The output must be a fully working HTML deck — no frameworks, no npm, no build step. The file should work by simply opening it in a browser.
Before generating, ask the user (or infer from context):
sketch-notes (default) — warm cream paper + manga-inspired hand-drawn feelminimal — black & white, large typography, maximum whitespacedark-pro — dark background, pro tech aesthetic, cyan/teal accentsdata-viz — vibrant palette, bold charts, data-forward layoutPlan the deck before generating HTML. A good structure:
| # | Slide Type | Purpose |
|---|---|---|
| 1 | Cover | Title + subtitle + one-line hook in a speech bubble |
| 2 | Overview | 3–5 key points (tool cards or insight grid) |
| 3–N | Content | One idea per slide. Tables, comparisons, decision trees |
| N | Takeaway | 2–3 bold conclusions in speech bubbles |
Rule: One idea per slide. If content overflows, split into two slides.
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>[Presentation Title]</title>
<style>
/* === 1. Style token override (if non-default style) === */
/* === 2. All CSS from Phase 3 === */
/* === 3. Slide-specific styles === */
</style>
</head>
<body>
<div class="halftone"></div>
<div class="deck">
<!-- Slides here -->
</div>
<nav class="nav">
<button onclick="prev()" aria-label="Previous">←</button>
<span class="counter" id="counter">1 / N</span>
<button onclick="next()" aria-label="Next">→</button>
</nav>
<script>
const S=document.querySelectorAll('.slide');let c=0;
function show(n){S[c].classList.remove('active');c=(n+S.length)%S.length;S[c].classList.add('active');document.getElementById('counter').textContent=`${c+1} / ${S.length}`;}
function next(){show(c+1)} function prev(){show(c-1)}
document.addEventListener('keydown',e=>{if(e.key==='ArrowRight'||e.key===' ')next();if(e.key==='ArrowLeft')prev();});
let tx=0;document.addEventListener('touchstart',e=>{tx=e.touches[0].clientX});
document.addEventListener('touchend',e=>{const d=e.changedTouches[0].clientX-tx;if(d<-50)next();if(d>50)prev();});
</script>
</body>
</html>
If the user picks a non-default style, prepend one :root block in the <style> tag before all other rules. Each variant only overrides CSS custom properties — component classes and layout stay the same. See Phase 3.5 for the exact :root blocks.
/* Example: dark-pro override */
:root {
--ink: #e0e0e0;
--paper: #1a1a2e;
--accent: #4ECDC4;
/* ... see Phase 3.5 for complete tokens */
}
For
dark-proand other dark themes, also invert the.halftoneoverlay opacity and adjust.navbutton colors to match--ink.
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700;900&family=Caveat:wght@400;700&family=Permanent+Marker&display=swap');
| Element | Font | Weight |
|---|---|---|
| Main headings (EN) | Permanent Marker | — |
| Body text, CJK | Noto Sans SC | 400 / 700 / 900 |
| Handwritten accents | Caveat | 400 / 700 |
/* Good */ font-size: clamp(36px, 5vw, 72px);
/* Bad */ font-size: 48px; /* ← NEVER do this */
:root {
--ink: #1a1a1a;
--paper: #f5f0e8; /* warm cream */
--grey: #888;
--light-grey: #d0ccc4;
}
Add .reveal to elements that should animate in when their slide becomes active.
.reveal {
opacity: 0; transform: translateY(30px);
transition: opacity 0.6s cubic-bezier(0.16,1,0.3,1),
transform 0.6s cubic-bezier(0.16,1,0.3,1);
}
.slide.active .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; }
Use
.revealon headings, cards, and bubbles. Do not animate body text paragraphs.
When the user selects a non-default style, use the corresponding :root block below and swap the @import font URL. All component classes (.bubble, .tool-card, etc.) remain the same — only tokens and fonts change.
minimal — Black & White@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;500;700;900&family=Noto+Sans+SC:wght@400;700;900&display=swap');
:root {
--ink: #111;
--paper: #ffffff;
--grey: #999;
--light-grey: #e5e5e5;
--accent: #111;
}
| Element | Font | Notes |
|---|---|---|
| Headings | Outfit 900 | All-caps recommended |
| Body / CJK | Noto Sans SC 400 |
Hide
.halftoneand.crosshatchoverlays. Rely on typography hierarchy and whitespace only.
dark-pro — Tech Dark@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&family=Noto+Sans+SC:wght@400;700;900&display=swap');
:root {
--ink: #e0e0e0;
--paper: #1a1a2e;
--grey: #7f8c9b;
--light-grey: #2a2a4a;
--accent: #4ECDC4;
}
| Element | Font | Notes |
|---|---|---|
| Headings | Space Grotesk 700 | Use --accent for key terms |
| Body / CJK | Noto Sans SC 400 |
Replace
.halftonewith a subtle CSS grid pattern (--light-greylines on--paper). Addbox-shadow: 0 0 20px var(--accent)glow on.tool-card:hover.
data-viz — Vibrant Data@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;800&family=Noto+Sans+SC:wght@400;700;900&display=swap');
:root {
--ink: #2d3436;
--paper: #fafafa;
--grey: #636e72;
--light-grey: #dfe6e9;
--accent: #6C5CE7;
--chart-1: #00b894;
--chart-2: #fdcb6e;
--chart-3: #e17055;
--chart-4: #0984e3;
}
| Element | Font | Notes |
|---|---|---|
| Headings | Plus Jakarta Sans 800 | |
| Body / CJK | Noto Sans SC 400 |
Use
--chart-1through--chart-4for bar/pie/line chart segments. Addborder-left: 4px solid var(--accent)on.insight-card.
.bubbleBest for: quotes, key takeaways, the author's voice.
<div class="bubble">
<p>Your insight here.</p>
</div>
.tool-card in .tools-gridBest for: comparing 4–6 items side by side.
<div class="tools-grid">
<div class="tool-card">
<div class="icon">⚡</div>
<div class="tag">CATEGORY</div>
<h3>Title</h3>
<div class="cn-name">中文名稱</div>
<p>Description.</p>
</div>
</div>
.decision-treeBest for: "How to choose?" slides.
<div class="decision-tree">
<div class="dt-node question">Core question?</div>
<div class="dt-arrow"></div>
<div class="dt-branch">
<div class="dt-branch-item">
<div class="dt-label">Option A</div>
<div class="dt-arrow"></div>
<div class="dt-node answer">Answer A</div>
</div>
<div class="dt-branch-item">
<div class="dt-label">Option B</div>
<div class="dt-arrow"></div>
<div class="dt-node answer">Answer B</div>
</div>
</div>
</div>
.insight-grid / .insight-cardBest for: 4–6 insight/principle cards with large background number.
<div class="insight-grid">
<div class="insight-card">
<div class="num">01</div>
<h3>Principle title</h3>
<p>Explanation.</p>
</div>
</div>
.compare-tableBest for: head-to-head feature comparison.
.emphasis-boxBest for: a single powerful statement.
<div class="emphasis-box">One powerful sentence here</div>
<!-- Manga crosshatch corner patch (sketch-notes only) -->
<div class="crosshatch" style="width:200px;height:200px;top:40px;left:30px;"></div>
<!-- Sketch underline on heading -->
<h2><span class="sketch-underline">Section Title</span></h2>
| Slide Type | Max Content |
|---|---|
| Cover | 1 title + 1 subtitle + 1 speech bubble |
| Content | 1 heading + 4–6 points OR 1 heading + 2 paragraphs |
| Card grid | 1 heading + max 6 cards (2×3 or 3×2) |
| Quote | 1 quote (max 3 lines) + attribution |
| Takeaway | 2–3 speech bubbles with bold conclusions |
If content overflows → split into two slides. Never compress or scroll.
px sizes (use clamp())overflow: hidden on .deck)#6366f1 or purposeless glassmorphismSelf-awareness check: You tend to converge toward "on distribution" outputs — the same layout, the same decorations, the same color choices every time. This is what users call "AI slop." Even within the same style, vary your decorative placement, card arrangements, and visual surprises across each generation.
Common AI-generated CSS mistakes — avoid these:
/* ❌ Negative clamp — silently ignored by browsers */
right: -clamp(28px, 3.5vw, 44px);
/* ✅ Correct */
right: calc(-1 * clamp(28px, 3.5vw, 44px));
/* ❌ CJK text overflow — long Chinese strings may not wrap */
/* ✅ Always add on text containers */
word-break: break-word;
overflow-wrap: break-word;
/* ❌ vh on mobile Safari — address bar causes overflow */
/* ✅ Use dvh with fallback */
height: 100vh;
height: 100dvh;
Output a single .html file to output/[topic]-ppt.html. The output/ directory is gitignored — all generated decks live there.
Include a brief summary of:
→ / Space = next, ← = prevnpx claudepluginhub jsphh7124/jsphh-teachingCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.