Install
$ agentstack add skill-zocomputer-skills-revealjs-presentation ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Reveal.js Presentation Skill
Build self-contained HTML slide decks using Reveal.js 5 + Chart.js 4. Every output is a single .html file that opens in any browser. Optionally publish to zo.space as a live page route.
Two Output Modes
1. Local HTML File (default)
Creates a single .html file saved to the user's workspace. No build step, no dependencies. Open in any browser.
2. zo.space Page Route (on request)
Publishes the presentation as a live page on the user's .zo.space. Uses the pre-installed reveal.js package + inline styles. Use when the user says "publish", "put it on zo.space", "make it live", or "share it online".
The publish script at scripts/publish.ts handles the conversion from local HTML to a zo.space-compatible React page route. Run it via:
bun /home/workspace/Skills/revealjs-presentation/scripts/publish.ts --file --route [--public]
When publishing to zo.space:
- Reveal.js is imported from the pre-installed package (
import Reveal from "reveal.js") - Chart.js is loaded from CDN via dynamic script injection in useEffect
- All CSS is inlined in the component
- The route defaults to private unless
--publicis passed - After publishing, check
get_space_errors()for build issues
Core Stack (Local HTML)
| Library | CDN | Purpose | |---------|-----|---------| | Reveal.js 5.1 | cdn.jsdelivr.net/npm/reveal.js@5.1.0 | Slide engine | | Chart.js 4.4 | cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js | Charts | | Google Fonts | fonts.googleapis.com | Typography |
Approved Extension Libraries
Reach for these when the core stack can't cover the requirement. All are CDN-loadable and UMD-compatible.
Data Visualization
| Need | Library | CDN | |------|---------|-----| | Custom charts | D3.js v7 | cdn.jsdelivr.net/npm/d3@7 | | Animated counters | CountUp.js | cdn.jsdelivr.net/npm/countup.js@2 | | Geographic maps | Leaflet.js | cdn.jsdelivr.net/npm/leaflet@1.9 | | Flow diagrams | Mermaid.js | cdn.jsdelivr.net/npm/mermaid@10 |
Animation & Motion
| Need | Library | CDN | |------|---------|-----| | Complex sequences | GSAP | cdn.jsdelivr.net/npm/gsap@3 | | Particle backgrounds | tsParticles | cdn.jsdelivr.net/npm/tsparticles@2 | | Typewriter effect | Typed.js | cdn.jsdelivr.net/npm/typed.js@2 | | Lottie animations | lottie-web | cdn.jsdelivr.net/npm/lottie-web@5 |
Content & Code
| Need | Library | CDN | |------|---------|-----| | Code highlighting | Prism.js | cdn.jsdelivr.net/npm/prismjs@1 | | Math / LaTeX | MathJax 3 | cdn.jsdelivr.net/npm/mathjax@3 | | Icons | Lucide | unpkg.com/lucide@latest/dist/umd/lucide.js |
Compatibility Checks for New Libraries
Before adding any library not listed above:
- Can CSS or existing stack handle it? Many effects don't need a library.
- UMD build on CDN? Must work via `` tag. ES module-only packages won't work.
- No conflict with Reveal.js? Watch for global keyboard/scroll listeners, body transform manipulation.
- Init after slide visible? Use
slidechangedevent handler, not DOMContentLoaded. - **
fonts → reveal.css → theme → chart.js →
SVG symbol defs (logos, icons)
x N slides
reveal.js script → init script → chart build functions
---
## Boilerplate Shell
```html
PRESENTATION TITLE
:root {
--bg: #FAFAF8;
--dk: #111111;
--dk2: #333333;
--gray: #888888;
--accent:#YOUR_COLOR;
--border:#E5E5E5;
--f-sans:'YOUR_FONT', system-ui, sans-serif;
--f-mono:'YOUR_MONO', 'SF Mono', monospace;
}
.reveal { font-family: var(--f-sans); }
.reveal .slides section {
text-align: left;
height: 100%;
padding: 0;
}
.S {
position: relative;
width: 100%; height: 100%;
display: flex; flex-direction: column;
padding: 32px 52px 24px;
overflow: hidden;
background: var(--bg);
color: var(--dk);
box-sizing: border-box;
}
.S > * { position: relative; z-index: 1; }
.pre { opacity: 0; }
.al { animation: fromLeft .5s ease forwards; }
.ar { animation: fromRight .5s ease forwards; }
.ab { animation: fromBelow .5s ease forwards; }
.af { animation: fadeUp .55s ease forwards; }
.ap { animation: pop .4s ease forwards; }
.d1 { animation-delay:.08s }
.d2 { animation-delay:.18s }
.d3 { animation-delay:.28s }
.d4 { animation-delay:.38s }
.d5 { animation-delay:.48s }
.d6 { animation-delay:.58s }
@keyframes fromLeft { from{opacity:0;transform:translateX(-22px)} to{opacity:1;transform:none} }
@keyframes fromRight { from{opacity:0;transform:translateX(22px)} to{opacity:1;transform:none} }
@keyframes fromBelow { from{opacity:0;transform:translateY(18px)} to{opacity:1;transform:none} }
@keyframes fadeUp { from{opacity:0} to{opacity:1} }
@keyframes pop { from{opacity:0;transform:scale(.93)} to{opacity:1;transform:scale(1)} }
.cw {
position: relative;
width: 100%;
min-height: 200px;
flex-shrink: 0;
}
.cw canvas {
position: absolute;
inset: 0;
width: 100% !important;
height: 100% !important;
}
.h1 { font-size:52px; font-weight:800; line-height:1.05; }
.h2 { font-size:38px; font-weight:700; line-height:1.1; }
.h3 { font-size:28px; font-weight:700; line-height:1.15; }
.stag {
font-family: var(--f-mono);
font-size: 11px;
letter-spacing: .25em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 10px;
}
.mono { font-family: var(--f-mono); }
.row { display:flex; flex-direction:row; gap:24px; }
.col { display:flex; flex-direction:column; gap:12px; }
.g2 { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.g3 { display:grid; grid-template-columns:1fr 1fr 1fr; gap:12px; }
.g4 { display:grid; grid-template-columns:1fr 1fr 1fr 1fr; gap:12px; }
.card {
background: #fff;
border: 1px solid var(--border);
border-radius: 12px;
padding: 18px 20px;
}
.card.accent {
background: var(--accent);
color: #fff;
border: none;
}
.pill {
display:inline-flex; align-items:center;
padding:3px 10px; border-radius:99px;
font-size:11px; font-weight:600; letter-spacing:.03em;
background: rgba(0,0,0,.07);
}
const chartMap = {};
const built = new Set();
Reveal.initialize({
hash: true,
transition: 'fade',
transitionSpeed: 'fast',
controls: true,
progress: true,
width: 1280,
height: 720,
margin: 0,
});
function animateSlide(slide) {
slide.querySelectorAll('.pre').forEach(el => el.classList.remove('pre'));
}
Reveal.on('slidechanged', ({ indexh }) => {
const slide = Reveal.getSlide(indexh);
if (slide) animateSlide(slide);
if (chartMap[indexh] && !built.has(indexh)) {
built.add(indexh);
chartMap[indexh]();
}
});
Reveal.on('ready', () => {
const slide = Reveal.getSlide(0);
if (slide) animateSlide(slide);
if (chartMap[0] && !built.has(0)) {
built.add(0);
chartMap[0]();
}
});
Slide Anatomy
Every slide follows this pattern:
Category
Headline with accent
Supporting text.
Layout Patterns
2-Column Split
Card Grid (3-up)
...
...
...
Hero Stat
$5.2B
market
CSS Treemap
Category
34%
Numbered Steps / Timeline
1
Step Title
Description
Comparison Table (CSS grid)
Feature
Option A
Option B
Feature name
No
Yes
Chart.js Recipes
Bar Chart
function buildBarChart() {
const ctx = document.getElementById('barChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Revenue',
data: [42, 68, 55, 89, 104, 127],
backgroundColor: 'YOUR_COLOR',
borderRadius: 6,
borderSkipped: false,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
x: { grid: { display: false }, border: { display: false } },
y: { grid: { color: '#f0f0f0' }, border: { display: false } }
}
}
});
}
Line Chart
function buildLineChart() {
const ctx = document.getElementById('lineChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [{
label: 'Series A',
data: [30, 55, 80, 120],
borderColor: 'YOUR_COLOR_1',
backgroundColor: 'YOUR_COLOR_1_20',
borderWidth: 2.5,
pointRadius: 4,
tension: 0.35,
fill: true,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: { grid: { display: false } },
y: { grid: { color: '#f0f0f0' } }
}
}
});
}
Doughnut
function buildDoughnut() {
const ctx = document.getElementById('doughnutChart').getContext('2d');
new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['A', 'B', 'C'],
datasets: [{
data: [40, 30, 30],
backgroundColor: ['#C1','#C2','#C3'],
borderWidth: 0,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '65%',
}
});
}
Canvas HTML:
Register in chartMap:
const chartMap = { 3: buildFunnelChart, 7: buildLineChart };
Animation Rules
- Add
.preto any element to start invisible - Add direction:
.al(left),.ar(right),.ab(below),.af(fade),.ap(pop) - Add delay:
.d1through.d6 - Init script removes
.prewhen slide becomes active
Critical Gotchas
- Chart height must be explicit.
.cwwrappers needmin-height. Chart.js reads container height; 0px = invisible. - Dead functions = errors. Every
chartMapentry needs a matching ``. - Don't reuse canvas IDs. Each chart needs a unique ID.
- SVG logos: define once, use everywhere with `
and`. - Reveal.js default is 960x700. For widescreen:
width: 1280, height: 720.
Slide Type Catalog
| Type | Best For | |------|----------| | Hero / Title | Opening, closing | | Stat Showcase | Key metrics | | Problem / Opportunity | Setting context | | Feature Grid | Capability overview | | Comparison | Competitive positioning | | Chart | Data storytelling | | Process / Steps | How it works | | Timeline | Roadmap | | Quote / Testimonial | Social proof | | Closing / CTA | Next steps |
Publishing to zo.space
When the user wants to publish a presentation to zo.space:
- First create the local HTML file as normal
- Run the publish script:
``bash bun /home/workspace/Skills/revealjs-presentation/scripts/publish.ts \ --file /home/workspace/Inbox/my-deck.html \ --route /presentations/my-deck \ --public ``
- The script extracts slides HTML, CSS variables, custom styles, chart functions, and SVG symbols from the HTML file
- It generates a React page route component that:
- Imports Reveal.js from the pre-installed package
- Loads Chart.js from CDN dynamically if charts are present
- Inlines all custom CSS
- Initializes Reveal.js in a useEffect
- Handles animations and chart building
- The route is deployed via
update_space_route - Check
get_space_errors()after publishing
The published presentation will be live at https://.zo.space.
Manual zo.space Publishing (without the script)
If you need more control, you can manually create a page route. The pattern:
import { useEffect, useRef } from "react";
import Reveal from "reveal.js";
export default function Presentation() {
const deckRef = useRef(null);
const revealRef = useRef(null);
useEffect(() => {
// Load reveal.js CSS from CDN
const revealCss = document.createElement("link");
revealCss.rel = "stylesheet";
revealCss.href = "https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/reveal.css";
document.head.appendChild(revealCss);
const themeCss = document.createElement("link");
themeCss.rel = "stylesheet";
themeCss.href = "https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/theme/white.css";
document.head.appendChild(themeCss);
// Init Reveal after CSS loads
themeCss.onload = () => {
if (deckRef.current && !revealRef.current) {
const deck = new Reveal(deckRef.current, {
hash: true,
transition: "fade",
transitionSpeed: "fast",
controls: true,
progress: true,
width: 1280,
height: 720,
margin: 0,
embedded: false,
});
deck.initialize();
revealRef.current = deck;
}
};
return () => {
revealRef.current?.destroy();
revealCss.remove();
themeCss.remove();
};
}, []);
return (
<>
{`
/* your custom CSS here */
`}
{/* slides here */}
);
}
Quick-Start Checklist
Before writing slides:
- [ ] Define CSS variables:
--bg,--dk,--accent,--f-sans,--f-mono - [ ] Choose font pair and add Google Fonts import
- [ ] Plan slide count and sequence
- [ ] Identify charts and assign unique canvas IDs
- [ ] Identify logos/icons to SVG-symbolize
When writing each slide:
- [ ] Wrap in ``
- [ ] Add
.pre+ animation class + delay to animated elements - [ ] Use explicit
min-heighton.cwchart wrappers
After all slides:
- [ ] Build
chartMapwith 0-based slide indices - [ ] One
buildXxx()per chart - [ ] Every
chartMapkey has a matching `` - [ ] If publishing to zo.space, run publish script
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: zocomputer
- Source: zocomputer/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.