
Frontend Slides
- 1.8k installs
- 82k repo stars
- Updated July 28, 2026
- nexu-io/open-design
HTML slide deck on 16:9 stage.
About
The frontend-slides skill creates animation-rich zero-dependency HTML presentations on fixed 1920 by 1080 stage scaling to viewport. Start from example.html copying stage CSS slide shell SlidePresentation controller verbatim then replace content only. Hard spec locks 16:9 canvas deck-viewport deck-stage uniform scale letterboxing no responsive breakpoints inside slides distinctive typography committed palettes staggered reveals hash routing. Curated from zarazhangrui frontend-slides embedding viewport-base.css. Use for html slides web ppt interactive decks single-file browser presentations avoiding generic AI-slop aesthetics with choreographed reveals. Fixed 1920x1080 uniform viewport scale. Zero-dependency single-file HTML deck. Copy example.html stage system only. Keyboard nav and hash routing. Staggered reveals committed palette. HTML slide deck on 16:9 stage. User asks html slides or web ppt.
- Fixed 1920x1080 uniform viewport scale.
- Zero-dependency single-file HTML deck.
- Copy example.html stage system only.
- Keyboard nav and hash routing.
- Staggered reveals committed palette.
Frontend Slides by the numbers
- 1,843 all-time installs (skills.sh)
- +127 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #242 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
frontend-slides capabilities & compatibility
- Capabilities
- stage scaling · animations
- Use cases
- presentations · frontend
npx skills add https://github.com/nexu-io/open-design --skill frontend-slidesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.8k |
|---|---|
| repo stars | ★ 82k |
| Last updated | July 28, 2026 |
| Repository | nexu-io/open-design ↗ |
Animation-rich HTML slides?
Build zero-dependency HTML decks on fixed 1920x1080 stage with animation reveals and keyboard navigation.
Who is it for?
Devs building web presentations.
Skip if: PowerPoint export only.
When should I use this skill?
User asks html slides or web ppt.
What you get
Single-file deck with navigation.
- HTML slide deck
- style preview variants
By the numbers
- Curated from upstream project zarazhangrui/frontend-slides
- Defines 5 trigger phrases: html slides, animation slides, interactive deck, web ppt, reveal slides
Files
Frontend Slides
Zero-dependency, animation-rich HTML presentations that run entirely in the browser. Curated from the MIT-licensed zarazhangrui/frontend-slides.
Start from `example.html` in this plugin folder. It is the proven seed: copy its stage CSS, slide shell, and the entire `SlidePresentation` controller script verbatim, then replace slide content. Do not rewrite the stage system, the navigation script, or the design tokens architecture from scratch.
Hard spec (locked — every deck must satisfy all of these)
Fixed 16:9 stage — NON-NEGOTIABLE
- Every deck is authored on a fixed 1920×1080 canvas:
.deck-viewport(fills the window) wraps.deck-stage(1920×1080,transform-origin: 0 0). - JavaScript scales the whole stage uniformly:
factor = min(innerWidth/1920, innerHeight/1080), thentranslate(x, y) scale(factor)to center with letterbox/pillarbox. Re-run onresize. - Never reflow slide content per device. No responsive breakpoints inside slides. All slide measurements are fixed px at the 1920×1080 design size.
- Include the FULL contents of
references/viewport-base.cssin the<style>block (the seed already embeds it).
Slide structure
- Each page is one
<section class="slide">directly inside.deck-stage. Around 10 slides for a standard deck; split content into more slides rather than shrinking type. - Slide switching uses
.active/.visibleclasses togglingvisibility,opacity,pointer-events— never `display: none/block` (layout classes likedisplay: flexon children would override it and show every slide at once). - No scrolling, no overflow, no overlapping panels, no text below comfortable reading size at 1920×1080.
Navigation runtime (keep the seed's script)
- Keyboard:
←/→,↑/↓,Space,PageUp/PageDown,Home/End. - Hash routing: current slide mirrored to
#/<index>; deep links andhashchangerestore the slide. - Mouse wheel (debounced ~650ms) and touch swipe (≥40px threshold).
- Page counter chrome lives in
.deck-controls, fixed-positioned outside the scaled stage.
Design tokens
- All colors, fonts, sizes, and easing live in
:rootCSS custom properties; retheme by editing tokens only. - Typography: distinctive webfonts from Google Fonts or Fontshare — never Inter, Roboto, Arial, or system fonts as display type. Avoid
#6366f1indigo and purple-gradient-on-white clichés. - Pick one preset from
references/STYLE_PRESETS.md(12 curated presets: Bold Signal, Electric Studio, Creative Voltage, Dark Botanical, Notebook Tabs, Pastel Geometry, Split Pastel, Vintage Editorial, Neon Cyber, Terminal Green, Swiss Modern, Paper & Ink) or design a custom system with the same discipline. The seed ships Bold Signal.
Layout vocabulary (compose slides from these)
title-card (colored focal card), agenda (numbered editorial list), section divider (giant outlined number), bullets (icon + heading + support line, max 4-6), big-stat (oversized number + side note), quote, two-column comparison, principle grid (2×2 cards), CSS bar chart (scaleY-animated bars), closing. Keep the slide-number top-left, breadcrumb nav top-right, and baseline rule bottom — that chrome is the deck's signature.
Motion
- Entrances via
.revealelements that transition when the slide gains.visible; stagger withtransition-delaysteps (~0.1s). - One signature easing per deck (seed:
cubic-bezier(0.16, 1, 0.3, 1)); animate onlytransformandopacity. prefers-reduced-motionsupport is mandatory (included in viewport-base.css).- Match animation character to the content's feeling using
references/animation-patterns.md.
Density modes
Ask (or infer) whether the deck is speaker-led or reading-first:
- Low density / speaker-led — one idea per slide, large type, 1-3 bullets max, more slides.
- High density / reading-first — self-contained slides, structured grids/tables, 4-6 cards max, still no overflow.
Output contract
- Single self-contained
.htmlfile: all CSS and JS inline, no build step, no external JS libraries, no CDN scripts. - Icons are inline SVG. No remote images from slow CDNs; CSS-generated visuals (gradients, shapes, patterns) are a first-class path.
- Comment every section:
/* === SECTION NAME === */. - CSS gotcha: never negate CSS functions directly (
-clamp()is silently ignored) — usecalc(-1 * clamp(...)).
References (read on demand)
| File | When |
|---|---|
references/STYLE_PRESETS.md | Choosing the visual direction |
references/viewport-base.css | Mandatory stage CSS — embed in full |
references/html-template.md | Controller architecture, inline-editing pattern, image pipeline |
references/animation-patterns.md | Matching effects to feeling |
Attribution
Design system, fixed-stage model, presets, and workflow come from the upstream MIT-licensed zarazhangrui/frontend-slides (© 2025 Zara Zhang). The LICENSE file ships in this plugin folder; keep it in place when redistributing.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Open Design · Frontend Slides</title>
<!-- Fonts: distinctive pairing from the Bold Signal preset — never system fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Space+Grotesk:wght@400;500;700&display=swap" rel="stylesheet">
<style>
/* ===========================================
CSS CUSTOM PROPERTIES (THEME)
Bold Signal preset — change these to retheme
=========================================== */
:root {
--stage-bg: #0d0d0d;
--slide-bg: #1a1a1a;
--bg-gradient: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 50%, #1a1a1a 100%);
--card-bg: #FF5722;
--card-bg-soft: rgba(255, 87, 34, 0.12);
--text-primary: #ffffff;
--text-secondary: rgba(255, 255, 255, 0.72);
--text-faint: rgba(255, 255, 255, 0.58);
--text-on-card: #1a1a1a;
--line: rgba(255, 255, 255, 0.14);
/* Typography — authored at the 1920×1080 stage size */
--font-display: 'Archivo Black', sans-serif;
--font-body: 'Space Grotesk', sans-serif;
--title-size: 128px;
--h2-size: 84px;
--subtitle-size: 34px;
--body-size: 28px;
/* Spacing — authored at the 1920×1080 stage size */
--slide-pad: 96px;
/* Animation */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--duration-normal: 0.7s;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
/* ===========================================
FIXED 16:9 STAGE: MANDATORY BASE STYLES
(full contents of viewport-base.css)
Slides are authored at 1920×1080 and scaled as a whole.
=========================================== */
html,
body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
background: var(--stage-bg, #000);
}
.deck-viewport {
position: fixed;
inset: 0;
overflow: hidden;
background: var(--stage-bg, #000);
}
/* Fixed 16:9 design canvas. JavaScript sets transform: translate(...) scale(...). */
.deck-stage {
position: absolute;
left: 0;
top: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
transform-origin: 0 0;
background: var(--slide-bg, #fff);
}
/* Slides stack inside the fixed stage. Content laid out at 1920×1080, never reflowed per device. */
.slide {
position: absolute;
inset: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
display: block;
visibility: hidden;
opacity: 0;
pointer-events: none;
background: var(--slide-bg, #fff);
}
.slide.active,
.slide.visible {
visibility: visible;
opacity: 1;
pointer-events: auto;
z-index: 1;
}
img, video, canvas, svg {
max-width: 100%;
max-height: 100%;
}
/* Presentation chrome stays outside the slide design system */
.deck-controls {
position: fixed;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
z-index: 1000;
}
@media print {
html, body { width: 1920px; height: auto; overflow: visible; background: #fff; }
.deck-viewport { position: static; overflow: visible; background: #fff; }
.deck-stage { position: static; width: auto; height: auto; transform: none !important; background: none; }
.slide {
position: relative;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
pointer-events: auto !important;
width: 1920px;
height: 1080px;
break-after: page;
page-break-after: always;
}
.slide:last-child { break-after: auto; page-break-after: auto; }
.deck-controls { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.2s !important;
transition-delay: 0s !important;
}
.reveal { transform: none; }
.bar { transform: scaleY(1); }
}
/* ===========================================
SLIDE SHELL — shared chrome inside every slide
=========================================== */
.slide {
background: var(--bg-gradient);
font-family: var(--font-body);
color: var(--text-primary);
transition: opacity 0.55s var(--ease-out-expo);
}
.slide-inner {
position: absolute;
inset: 0;
padding: var(--slide-pad);
display: flex;
flex-direction: column;
}
/* Slide number, top-left — Bold Signal signature */
.slide-no {
position: absolute;
top: 56px;
left: var(--slide-pad);
font-family: var(--font-display);
font-size: 30px;
letter-spacing: 0.08em;
color: var(--card-bg);
}
/* Breadcrumb nav, top-right — active section at full opacity */
.crumbs {
position: absolute;
top: 62px;
right: var(--slide-pad);
display: flex;
gap: 36px;
font-size: 21px;
font-weight: 500;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-faint);
}
.crumbs .on { color: var(--text-primary); }
/* Baseline rule across the bottom of each slide */
.baseline {
position: absolute;
left: var(--slide-pad);
right: var(--slide-pad);
bottom: 64px;
border-top: 1px solid var(--line);
padding-top: 18px;
display: flex;
justify-content: space-between;
font-size: 19px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--text-faint);
}
/* ===========================================
TYPOGRAPHY
=========================================== */
.display {
font-family: var(--font-display);
font-size: var(--title-size);
line-height: 0.98;
text-transform: uppercase;
letter-spacing: -0.01em;
}
.h2 {
font-family: var(--font-display);
font-size: var(--h2-size);
line-height: 1.02;
text-transform: uppercase;
}
.kicker {
font-size: 24px;
font-weight: 700;
letter-spacing: 0.32em;
text-transform: uppercase;
color: var(--card-bg);
margin-bottom: 30px;
}
.lede {
font-size: var(--subtitle-size);
line-height: 1.5;
color: var(--text-secondary);
max-width: 60ch;
}
/* ===========================================
ANIMATIONS — trigger via .visible on the slide
=========================================== */
.reveal {
opacity: 0;
transform: translateY(34px);
transition: opacity var(--duration-normal) var(--ease-out-expo),
transform var(--duration-normal) var(--ease-out-expo);
}
.slide.visible .reveal { opacity: 1; transform: translateY(0); }
.reveal:nth-child(1) { transition-delay: 0.08s; }
.reveal:nth-child(2) { transition-delay: 0.18s; }
.reveal:nth-child(3) { transition-delay: 0.28s; }
.reveal:nth-child(4) { transition-delay: 0.38s; }
.reveal:nth-child(5) { transition-delay: 0.48s; }
.reveal:nth-child(6) { transition-delay: 0.58s; }
/* Nested reveal groups enter after the slide heading, top-down */
.agenda .reveal:nth-child(1),
.points .reveal:nth-child(1),
.compare .reveal:nth-child(1),
.grid4 .reveal:nth-child(1),
.chart .reveal:nth-child(1) { transition-delay: 0.46s; }
.agenda .reveal:nth-child(2),
.points .reveal:nth-child(2),
.compare .reveal:nth-child(2),
.grid4 .reveal:nth-child(2),
.chart .reveal:nth-child(2) { transition-delay: 0.54s; }
.agenda .reveal:nth-child(3),
.points .reveal:nth-child(3),
.grid4 .reveal:nth-child(3),
.chart .reveal:nth-child(3) { transition-delay: 0.62s; }
.agenda .reveal:nth-child(4),
.grid4 .reveal:nth-child(4),
.chart .reveal:nth-child(4) { transition-delay: 0.7s; }
/* ===========================================
S1 · TITLE — orange card focal point
=========================================== */
.title-card {
margin: auto 0;
background: var(--card-bg);
color: var(--text-on-card);
padding: 90px 100px 84px;
width: 1300px;
}
.title-card .display { color: var(--text-on-card); }
.title-card .meta {
display: flex;
gap: 48px;
margin-top: 44px;
font-size: 24px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
}
/* ===========================================
S2 · AGENDA — numbered editorial list
=========================================== */
.agenda { margin-top: 56px; display: flex; flex-direction: column; }
.agenda-row {
display: flex;
align-items: baseline;
gap: 56px;
padding: 34px 0;
border-top: 1px solid var(--line);
font-size: 44px;
font-weight: 500;
}
.agenda-row:last-child { border-bottom: 1px solid var(--line); }
.agenda-row .idx {
font-family: var(--font-display);
font-size: 30px;
color: var(--card-bg);
min-width: 90px;
}
.agenda-row .hint {
margin-left: auto;
font-size: 23px;
color: var(--text-faint);
letter-spacing: 0.08em;
text-transform: uppercase;
}
/* ===========================================
S3 · SECTION DIVIDER — giant number
=========================================== */
.divider-no {
font-family: var(--font-display);
font-size: 460px;
line-height: 0.9;
color: transparent;
-webkit-text-stroke: 3px var(--card-bg);
}
/* ===========================================
S4 · BULLETS — one idea, three proofs
=========================================== */
.points { margin-top: 64px; display: grid; gap: 44px; max-width: 1380px; }
.point { display: flex; gap: 40px; align-items: flex-start; }
.point .tick {
flex: none;
width: 56px; height: 56px;
background: var(--card-bg-soft);
border: 2px solid var(--card-bg);
display: grid;
place-items: center;
}
.point .tick svg { width: 26px; height: 26px; stroke: var(--card-bg); }
.point h3 { font-size: 35px; font-weight: 700; margin-bottom: 8px; }
.point p { font-size: 26px; line-height: 1.5; color: var(--text-secondary); }
/* ===========================================
S5 · BIG STAT
=========================================== */
.stat-wrap { margin: auto 0; display: flex; align-items: baseline; gap: 72px; }
.stat-num {
font-family: var(--font-display);
font-size: 380px;
line-height: 0.9;
color: var(--card-bg);
}
.stat-side { max-width: 560px; }
.stat-side h3 { font-size: 44px; font-weight: 700; margin-bottom: 20px; line-height: 1.15; }
.stat-side p { font-size: 26px; line-height: 1.55; color: var(--text-secondary); }
/* ===========================================
S6 · QUOTE
=========================================== */
.quote-mark {
font-family: var(--font-display);
font-size: 200px;
line-height: 0.6;
color: var(--card-bg);
}
.quote-text {
font-size: 66px;
font-weight: 500;
line-height: 1.28;
max-width: 1480px;
margin-top: 36px;
}
.quote-attr { margin-top: 52px; font-size: 26px; color: var(--text-secondary); letter-spacing: 0.1em; text-transform: uppercase; }
.quote-attr strong { color: var(--card-bg); }
/* ===========================================
S7 · TWO-COLUMN COMPARISON
=========================================== */
.compare { margin-top: 60px; margin-bottom: 64px; display: grid; grid-template-columns: 1fr 1fr; gap: 48px; flex: 1; min-height: 0; }
.compare .col { border: 1px solid var(--line); padding: 56px 60px; display: flex; flex-direction: column; }
.compare .col.hot { background: var(--card-bg); color: var(--text-on-card); border-color: var(--card-bg); }
.compare h3 { font-family: var(--font-display); font-size: 40px; text-transform: uppercase; margin-bottom: 36px; }
.compare ul { list-style: none; display: grid; gap: 24px; font-size: 27px; line-height: 1.45; }
.compare .col li { padding-left: 40px; position: relative; color: var(--text-secondary); }
.compare .col.hot li { color: rgba(26, 26, 26, 0.92); }
.compare li::before {
content: "";
position: absolute;
left: 0; top: 16px;
width: 18px; height: 3px;
background: var(--card-bg);
}
.compare .col.hot li::before { background: var(--text-on-card); }
/* ===========================================
S8 · PRINCIPLE GRID — 4 cards
=========================================== */
.grid4 { margin-top: 60px; margin-bottom: 64px; display: grid; grid-template-columns: 1fr 1fr; gap: 36px; flex: 1; min-height: 0; }
.gcard { border: 1px solid var(--line); padding: 48px 52px; position: relative; }
.gcard .gno {
font-family: var(--font-display);
font-size: 27px;
color: var(--card-bg);
margin-bottom: 22px;
}
.gcard h3 { font-size: 33px; font-weight: 700; margin-bottom: 12px; }
.gcard p { font-size: 24px; line-height: 1.5; color: var(--text-secondary); }
/* ===========================================
S9 · BAR CHART — pure CSS data viz
=========================================== */
.chart { margin-top: 70px; flex: 1; display: flex; align-items: flex-end; gap: 64px; padding-bottom: 70px; }
.bar-group { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; gap: 20px; }
.bar {
background: var(--card-bg-soft);
border: 2px solid var(--card-bg);
position: relative;
transform-origin: bottom;
transform: scaleY(0);
transition: transform 0.9s var(--ease-out-expo);
}
.bar.solid { background: var(--card-bg); }
.slide.visible .bar { transform: scaleY(1); }
.slide.visible .bar-group:nth-child(1) .bar { transition-delay: 0.5s; }
.slide.visible .bar-group:nth-child(2) .bar { transition-delay: 0.62s; }
.slide.visible .bar-group:nth-child(3) .bar { transition-delay: 0.74s; }
.slide.visible .bar-group:nth-child(4) .bar { transition-delay: 0.86s; }
.bar-val {
position: absolute;
top: -52px; left: 0; right: 0;
text-align: center;
font-family: var(--font-display);
font-size: 30px;
color: var(--text-primary);
}
.bar-label { text-align: center; font-size: 23px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-secondary); }
/* ===========================================
S10 · CLOSING — inverted card
=========================================== */
.close-card { margin: auto 0; }
.close-card .display span { color: var(--card-bg); }
.close-links { display: flex; gap: 56px; margin-top: 64px; font-size: 26px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; }
.close-links .lk { display: flex; align-items: center; gap: 16px; color: var(--text-secondary); }
.close-links svg { width: 30px; height: 30px; stroke: var(--card-bg); }
/* ===========================================
DECK CHROME — page counter (outside the stage)
=========================================== */
.deck-controls {
font-family: var(--font-body);
font-size: 14px;
letter-spacing: 0.22em;
color: rgba(255, 255, 255, 0.72);
background: rgba(13, 13, 13, 0.6);
border: 1px solid rgba(255, 255, 255, 0.14);
padding: 8px 18px;
border-radius: 999px;
user-select: none;
}
.deck-controls .cur { color: var(--card-bg); font-weight: 700; }
</style>
<style id="od-no-pager">.deck-controls,#counter,.deck-counter{display:none!important}</style>
</head>
<body>
<div class="deck-viewport">
<main class="deck-stage" id="deckStage" aria-label="Slide deck">
<!-- === SLIDE 1: TITLE === -->
<section class="slide active visible">
<div class="slide-inner">
<div class="slide-no">01</div>
<nav class="crumbs"><span class="on">Intro</span><span>Why</span><span>How</span><span>Next</span></nav>
<div class="title-card reveal">
<h1 class="display">Open<br>Design</h1>
<div class="meta"><span>Apache-2.0</span><span>·</span><span>Runs Locally</span><span>·</span><span>BYOK</span></div>
</div>
<div class="baseline"><span>Open Design</span><span>github.com/nexu-io/open-design</span></div>
</div>
</section>
<!-- === SLIDE 2: AGENDA === -->
<section class="slide">
<div class="slide-inner">
<div class="slide-no">02</div>
<nav class="crumbs"><span class="on">Intro</span><span>Why</span><span>How</span><span>Next</span></nav>
<h2 class="h2 reveal">Agenda</h2>
<div class="agenda">
<div class="agenda-row reveal"><span class="idx">01</span>Why design should be open<span class="hint">Why</span></div>
<div class="agenda-row reveal"><span class="idx">02</span>The numbers behind the project<span class="hint">Stats</span></div>
<div class="agenda-row reveal"><span class="idx">03</span>Open Design vs. closed cloud<span class="hint">How</span></div>
<div class="agenda-row reveal"><span class="idx">04</span>Core capabilities & what's next<span class="hint">Next</span></div>
</div>
<div class="baseline"><span>Agenda</span><span>02 / 10</span></div>
</div>
</section>
<!-- === SLIDE 3: SECTION DIVIDER === -->
<section class="slide">
<div class="slide-inner" style="justify-content: center;">
<nav class="crumbs"><span>Intro</span><span class="on">Why</span><span>How</span><span>Next</span></nav>
<div class="divider-no reveal">01</div>
<h2 class="h2 reveal" style="margin-top: 28px;">Why design<br>should be open</h2>
<div class="baseline"><span>Section</span><span>03 / 10</span></div>
</div>
</section>
<!-- === SLIDE 4: BULLETS === -->
<section class="slide">
<div class="slide-inner">
<div class="slide-no">04</div>
<nav class="crumbs"><span>Intro</span><span class="on">Why</span><span>How</span><span>Next</span></nav>
<p class="kicker reveal">Three claims</p>
<h2 class="h2 reveal" style="font-size: 64px;">Open, local,<br>agent-native</h2>
<div class="points">
<div class="point reveal">
<span class="tick"><svg viewBox="0 0 24 24" fill="none" stroke="#FF5722" stroke-width="3"><path d="M4 12l5 5L20 6"/></svg></span>
<div><h3>Desktop-native</h3><p>Design happens on the desktop. Local files, Figma exports, and code repos are directly readable; agents get full terminal power.</p></div>
</div>
<div class="point reveal">
<span class="tick"><svg viewBox="0 0 24 24" fill="none" stroke="#FF5722" stroke-width="3"><path d="M4 12l5 5L20 6"/></svg></span>
<div><h3>Plug in agents, don't rebuild them</h3><p>Claude Code, Codex, and Cursor on your machine are already powerful. Open Design wires them into a complete design workflow.</p></div>
</div>
<div class="point reveal">
<span class="tick"><svg viewBox="0 0 24 24" fill="none" stroke="#FF5722" stroke-width="3"><path d="M4 12l5 5L20 6"/></svg></span>
<div><h3>It learns your taste</h3><p>Every choice becomes design systems, preferences, and memory, so the next generation lands closer to what you want.</p></div>
</div>
</div>
<div class="baseline"><span>Why open</span><span>04 / 10</span></div>
</div>
</section>
<!-- === SLIDE 5: BIG STAT === -->
<section class="slide">
<div class="slide-inner">
<div class="slide-no">05</div>
<nav class="crumbs"><span>Intro</span><span class="on">Why</span><span>How</span><span>Next</span></nav>
<div class="stat-wrap">
<div class="stat-num reveal">60K<span style="font-size: 160px;">+</span></div>
<div class="stat-side reveal">
<h3>GitHub Stars</h3>
<p>Open source under Apache-2.0, built with 300+ contributors — plus 217+ plugins, 129 design systems, and 21 coding agents on board.</p>
</div>
</div>
<div class="baseline"><span>By the numbers</span><span>05 / 10</span></div>
</div>
</section>
<!-- === SLIDE 6: QUOTE === -->
<section class="slide">
<div class="slide-inner" style="justify-content: center;">
<nav class="crumbs"><span>Intro</span><span class="on">Why</span><span>How</span><span>Next</span></nav>
<div class="quote-mark reveal">“</div>
<p class="quote-text reveal">File-system semantics plus git-diffable design artifacts — design is finally under version control.</p>
<p class="quote-attr reveal"><strong>—</strong> Indie developer</p>
<div class="baseline"><span>Quote</span><span>06 / 10</span></div>
</div>
</section>
<!-- === SLIDE 7: COMPARISON === -->
<section class="slide">
<div class="slide-inner">
<div class="slide-no">07</div>
<nav class="crumbs"><span>Intro</span><span>Why</span><span class="on">How</span><span>Next</span></nav>
<h2 class="h2 reveal" style="font-size: 60px;">Closed cloud vs. Open Design</h2>
<div class="compare">
<div class="col reveal">
<h3>Closed cloud</h3>
<ul>
<li>Closed-source, paid, cloud-only</li>
<li>Locked to a single model vendor</li>
<li>No agent choice, no self-hosting, no BYOK</li>
<li>Around $500 a month per seat</li>
</ul>
</div>
<div class="col hot reveal">
<h3>Open Design</h3>
<ul>
<li>Apache-2.0, runs on your own machine</li>
<li>21 coding agents, auto-detected from PATH</li>
<li>Full-stack BYOK and self-hosting allowed</li>
<li>Around $73 a month — over 80% less</li>
</ul>
</div>
</div>
<div class="baseline"><span>Open vs. closed</span><span>07 / 10</span></div>
</div>
</section>
<!-- === SLIDE 8: PRINCIPLE GRID === -->
<section class="slide">
<div class="slide-inner">
<div class="slide-no">08</div>
<nav class="crumbs"><span>Intro</span><span>Why</span><span class="on">How</span><span>Next</span></nav>
<h2 class="h2 reveal" style="font-size: 60px;">Core capabilities</h2>
<div class="grid4">
<div class="gcard reveal"><div class="gno">A</div><h3>Desktop-first</h3><p>Local files, Figma, and code repos readable in place — agents work where your design actually lives.</p></div>
<div class="gcard reveal"><div class="gno">B</div><h3>HTML video pipeline</h3><p>Programmatic video via HyperFrames: HTML-to-MP4 marketing shorts and product demos, end to end.</p></div>
<div class="gcard reveal"><div class="gno">C</div><h3>129 design systems</h3><p>Import from GitHub, Figma, or local. Markdown design systems keep tokens from drifting.</p></div>
<div class="gcard reveal"><div class="gno">D</div><h3>217+ plugins</h3><p>Prototypes, slides, posters, dashboards — a growing ecosystem, all under Apache-2.0.</p></div>
</div>
<div class="baseline"><span>Capabilities</span><span>08 / 10</span></div>
</div>
</section>
<!-- === SLIDE 9: BAR CHART === -->
<section class="slide">
<div class="slide-inner">
<div class="slide-no">09</div>
<nav class="crumbs"><span>Intro</span><span>Why</span><span class="on">How</span><span>Next</span></nav>
<p class="kicker reveal">Demo data</p>
<h2 class="h2 reveal" style="font-size: 60px;">Where 217+ plugins cluster</h2>
<div class="chart">
<div class="bar-group reveal">
<div class="bar solid" style="height: 78%;"><span class="bar-val">52%</span></div>
<div class="bar-label">Prototypes</div>
</div>
<div class="bar-group reveal">
<div class="bar" style="height: 42%;"><span class="bar-val">28%</span></div>
<div class="bar-label">Slides & decks</div>
</div>
<div class="bar-group reveal">
<div class="bar" style="height: 20%;"><span class="bar-val">13%</span></div>
<div class="bar-label">Posters & images</div>
</div>
<div class="bar-group reveal">
<div class="bar" style="height: 11%;"><span class="bar-val">7%</span></div>
<div class="bar-label">Video & more</div>
</div>
</div>
<div class="baseline"><span>Plugin ecosystem</span><span>09 / 10</span></div>
</div>
</section>
<!-- === SLIDE 10: CLOSING === -->
<section class="slide">
<div class="slide-inner">
<div class="slide-no">10</div>
<nav class="crumbs"><span>Intro</span><span>Why</span><span>How</span><span class="on">Next</span></nav>
<div class="close-card">
<h2 class="display reveal" style="font-size: 150px;">Set design<br><span>free.</span></h2>
<div class="close-links reveal">
<span class="lk"><svg viewBox="0 0 24 24" fill="none" stroke="#FF5722" stroke-width="2"><path d="M12 .5C5.7.5.7 5.6.7 11.9c0 5 3.2 9.3 7.7 10.8.6.1.8-.2.8-.5v-2c-3.1.7-3.8-1.3-3.8-1.3-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.1.1 1.7 1.2 1.7 1.2 1 1.7 2.6 1.2 3.3.9.1-.7.4-1.2.7-1.5-2.5-.3-5.2-1.3-5.2-5.7 0-1.3.4-2.3 1.2-3.1-.1-.3-.5-1.5.1-3 0 0 1-.3 3.1 1.2a10.7 10.7 0 0 1 5.7 0c2.2-1.5 3.1-1.2 3.1-1.2.6 1.5.2 2.7.1 3 .8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.2 5.7.4.3.7 1 .7 2v3c0 .3.2.6.8.5a11.3 11.3 0 0 0 7.7-10.8C23.3 5.6 18.3.5 12 .5z"/></svg>nexu-io/open-design</span>
<span class="lk"><svg viewBox="0 0 24 24" fill="none" stroke="#FF5722" stroke-width="2"><path d="M3 8l9 6 9-6M4 19h16a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1z"/></svg>discord.gg/mHAjSMV6gz</span>
</div>
</div>
<div class="baseline"><span>Thank you</span><span>10 / 10</span></div>
</div>
</section>
</main>
</div>
<!-- Page counter lives outside the scaled stage -->
<div class="deck-controls" aria-hidden="true"><span class="cur" id="pageCur">1</span> / <span id="pageTotal">10</span></div>
<script>
/* ===========================================
SLIDE PRESENTATION CONTROLLER
Fixed 1920×1080 stage, scaled uniformly to the viewport.
Navigation: ← → Space PageUp/PageDown Home/End, wheel,
touch swipe. Current slide is mirrored into the URL hash.
=========================================== */
(function () {
'use strict';
var STAGE_W = 1920;
var STAGE_H = 1080;
var stage = document.getElementById('deckStage');
var slides = Array.prototype.slice.call(document.querySelectorAll('.slide'));
var pageCur = document.getElementById('pageCur');
var pageTotal = document.getElementById('pageTotal');
var current = 0;
var wheelLockUntil = 0;
pageTotal.textContent = String(slides.length);
/* --- Stage scaling: one transform, letterbox as needed --- */
function fitStage() {
var factor = Math.min(window.innerWidth / STAGE_W, window.innerHeight / STAGE_H);
var x = (window.innerWidth - STAGE_W * factor) / 2;
var y = (window.innerHeight - STAGE_H * factor) / 2;
stage.style.transform = 'translate(' + x + 'px, ' + y + 'px) scale(' + factor + ')';
}
/* --- Hash routing: #/3 means slide index 3 (0-based) --- */
function indexFromHash() {
var m = /^#\/(\d+)$/.exec(window.location.hash);
if (!m) return null;
var idx = parseInt(m[1], 10);
return isNaN(idx) ? null : idx;
}
function show(index, skipHash) {
current = Math.max(0, Math.min(index, slides.length - 1));
slides.forEach(function (slide, i) {
slide.classList.toggle('active', i === current);
slide.classList.toggle('visible', i === current);
});
pageCur.textContent = String(current + 1);
if (!skipHash) {
var target = '#/' + current;
if (window.location.hash !== target) {
window.history.replaceState(null, '', target);
}
}
}
function next() { show(current + 1); }
function prev() { show(current - 1); }
/* --- Keyboard --- */
document.addEventListener('keydown', function (e) {
if (e.target && e.target.getAttribute && e.target.getAttribute('contenteditable')) return;
switch (e.key) {
case 'ArrowRight':
case 'ArrowDown':
case ' ':
case 'PageDown':
e.preventDefault(); next(); break;
case 'ArrowLeft':
case 'ArrowUp':
case 'PageUp':
e.preventDefault(); prev(); break;
case 'Home':
e.preventDefault(); show(0); break;
case 'End':
e.preventDefault(); show(slides.length - 1); break;
}
});
/* --- Mouse wheel (debounced) --- */
document.addEventListener('wheel', function (e) {
var now = Date.now();
if (now < wheelLockUntil) return;
if (Math.abs(e.deltaY) < 12) return;
wheelLockUntil = now + 650;
if (e.deltaY > 0) { next(); } else { prev(); }
}, { passive: true });
/* --- Touch swipe --- */
var touchX = null;
document.addEventListener('touchstart', function (e) {
if (e.touches.length === 1) touchX = e.touches[0].clientX;
}, { passive: true });
document.addEventListener('touchend', function (e) {
if (touchX === null) return;
var dx = e.changedTouches[0].clientX - touchX;
touchX = null;
if (Math.abs(dx) < 40) return;
if (dx < 0) { next(); } else { prev(); }
}, { passive: true });
/* --- Hash navigation (back/forward, deep links) --- */
window.addEventListener('hashchange', function () {
var idx = indexFromHash();
if (idx !== null && idx !== current) show(idx, true);
});
window.addEventListener('resize', fitStage);
fitStage();
var initial = indexFromHash();
show(initial !== null ? initial : 0, initial === null);
})();
</script>
</body>
</html>
MIT License
Copyright (c) 2025 Zara Zhang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
{
"$schema": "https://open-design.ai/schemas/plugin.v1.json",
"specVersion": "1.0.0",
"name": "example-frontend-slides",
"title": "Frontend Slides",
"title_i18n": {
"en": "Frontend Slides",
"zh-CN": "Frontend Slides 动效幻灯片"
},
"version": "1.0.0",
"description": "Animation-rich, zero-dependency HTML presentations on a fixed 1920x1080 stage that scales to any viewport. Distinctive typography, committed palettes, choreographed staggered reveals, keyboard navigation, and hash routing - no generic AI-slop aesthetics.",
"description_i18n": {
"en": "Animation-rich, zero-dependency HTML presentations on a fixed 1920x1080 stage that scales to any viewport. Distinctive typography, committed palettes, choreographed staggered reveals, keyboard navigation, and hash routing - no generic AI-slop aesthetics.",
"zh-CN": "动画丰富的零依赖单文件 HTML 幻灯片:固定 1920x1080 舞台等比缩放适配任意屏幕,个性化字体排印、坚定的配色系统、编排式错峰入场动画、键盘导航和 hash 路由,拒绝千篇一律的 AI 味设计。"
},
"license": "MIT",
"author": {
"name": "zarazhangrui",
"url": "https://github.com/zarazhangrui"
},
"homepage": "https://github.com/zarazhangrui/frontend-slides",
"plugin": {
"repo": "https://github.com/zarazhangrui/frontend-slides"
},
"tags": [
"community",
"deck",
"slides",
"presentation",
"animation",
"web",
"ppt"
],
"compat": {
"agentSkills": [
{
"path": "./SKILL.md"
}
]
},
"od": {
"kind": "scenario",
"taskKind": "new-generation",
"mode": "deck",
"scenario": "product",
"surface": "web",
"preview": {
"type": "html",
"entry": "./example.html",
"motion": "deck"
},
"useCase": {
"query": {
"en": "Create an animation-rich {{deckType}} for {{audience}} about {{topic}} with {{slideCount}}, designed as a {{density}} deck in the {{stylePreset}} visual direction. Output one self-contained single-file HTML deck with zero dependencies (all CSS/JS inline, no build step, no external JS libraries). Hard rules, all locked: (1) Fixed 16:9 stage - author every slide on a 1920x1080 canvas inside .deck-viewport > .deck-stage and scale the whole stage uniformly to the viewport with one transform (factor = min(innerWidth/1920, innerHeight/1080), centered with letterbox); never reflow slide content per device and never use responsive breakpoints inside slides. (2) Each page is one <section class=\"slide\">; switch slides only via .active/.visible classes toggling visibility/opacity/pointer-events - never display:none. (3) Keyboard navigation (arrows, Space, PageUp/PageDown, Home/End), debounced wheel, touch swipe, and #/<index> hash routing with deep-link restore; page counter chrome fixed outside the scaled stage. (4) All colors/fonts/sizes/easing live in :root design tokens. (5) Distinctive typography from Google Fonts or Fontshare - never Inter, Roboto, Arial, or system display fonts; no purple-gradient-on-white, no generic indigo #6366f1; commit to a cohesive palette with sharp accents. (6) One signature easing; entrances via .reveal elements staggered with transition-delay when the slide gains .visible; animate only transform and opacity; include prefers-reduced-motion support. (7) No scrolling, no overflow, no overlapping panels; split content into more slides instead of shrinking type; low-density speaker-led decks get one idea per slide with 1-3 bullets, high-density reading-first decks get structured grids with max 4-6 cards. (8) Icons are inline SVG; prefer CSS-generated visuals (gradients, shapes, patterns) over remote images. Start from the example.html seed in this plugin: keep its stage CSS (full viewport-base.css contents), slide shell chrome (slide number top-left, breadcrumbs top-right, baseline rule), and the entire SlidePresentation controller script verbatim - replace only the slide content and, if a different style is requested, the :root tokens and layout skins following references/STYLE_PRESETS.md and references/animation-patterns.md. Compose slides from the layout vocabulary: title card, agenda list, section divider with giant number, bullet points, big stat, quote, two-column comparison, principle grid, CSS bar chart, closing.",
"zh-CN": "用 Frontend Slides 为 {{audience}} 创作一套关于 {{topic}} 的 {{deckType}}({{slideCount}},{{density}},{{stylePreset}} 风格方向):零依赖单文件 HTML,固定 1920x1080 舞台整体等比缩放适配视口(绝不按设备重排内容),每页一个 section.slide、用 .active/.visible 切换,键盘/滚轮/触摸导航 + hash 路由,:root 设计 token,个性化字体与坚定配色(拒绝 AI 味),.reveal 错峰入场动画并支持 prefers-reduced-motion。从插件内 example.html 种子出发,保留舞台系统、导航脚本和页面骨架,只替换内容;换风格时按 references/STYLE_PRESETS.md 调整 token 与版式皮肤。完整硬性规格以英文 prompt 为准。"
},
"exampleOutputs": [
{
"path": "./example.html",
"title": "The Craft of Motion - Bold Signal deck",
"title_i18n": {
"en": "The Craft of Motion - Bold Signal deck",
"zh-CN": "The Craft of Motion · Bold Signal 风格演示"
}
}
]
},
"inputs": [
{
"name": "deckType",
"label": "Deck type",
"type": "select",
"required": true,
"options": [
"conference talk",
"pitch deck",
"teaching / tutorial deck",
"internal presentation",
"product overview"
],
"default": "conference talk"
},
{
"name": "topic",
"label": "Topic",
"type": "string",
"required": true,
"placeholder": "The craft of motion in interface design",
"default": "the user's brief"
},
{
"name": "audience",
"label": "Audience",
"type": "string",
"required": true,
"placeholder": "Design engineers at a frontend meetup",
"default": "a general professional audience"
},
{
"name": "slideCount",
"label": "Length",
"type": "select",
"required": true,
"options": [
"short 5-10 slides",
"medium 10-20 slides",
"long 20+ slides"
],
"default": "short 5-10 slides"
},
{
"name": "density",
"label": "Density",
"type": "select",
"required": true,
"options": [
"low density / speaker-led",
"high density / reading-first"
],
"default": "low density / speaker-led"
},
{
"name": "stylePreset",
"label": "Style direction",
"type": "select",
"options": [
"Bold Signal (seed default)",
"Electric Studio",
"Creative Voltage",
"Dark Botanical",
"Notebook Tabs",
"Pastel Geometry",
"Split Pastel",
"Vintage Editorial",
"Neon Cyber",
"Terminal Green",
"Swiss Modern",
"Paper & Ink",
"let the agent pick a distinctive custom design"
],
"default": "Bold Signal (seed default)"
}
],
"context": {
"skills": [
{
"path": "./SKILL.md"
}
],
"assets": [
"./example.html",
"./references/STYLE_PRESETS.md",
"./references/viewport-base.css",
"./references/html-template.md",
"./references/animation-patterns.md"
]
},
"pipeline": {
"stages": [
{
"id": "discovery",
"atoms": [
"discovery-question-form"
]
},
{
"id": "generate",
"atoms": [
"file-write",
"live-artifact"
]
}
]
},
"capabilities": [
"prompt:inject",
"fs:write"
]
}
}
Animation Patterns Reference
Use this reference when generating presentations. Match animations to the intended feeling.
Effect-to-Feeling Guide
| Feeling | Animations | Visual Cues |
|---|---|---|
| Dramatic / Cinematic | Slow fade-ins (1-1.5s), large scale transitions (0.9 to 1), parallax scrolling | Dark backgrounds, spotlight effects, full-bleed images |
| Techy / Futuristic | Neon glow (box-shadow), glitch/scramble text, grid reveals | Particle systems (canvas), grid patterns, monospace accents, cyan/magenta/electric blue |
| Playful / Friendly | Bouncy easing (spring physics), floating/bobbing | Rounded corners, pastel/bright colors, hand-drawn elements |
| Professional / Corporate | Subtle fast animations (200-300ms), clean slides | Navy/slate/charcoal, precise spacing, data visualization focus |
| Calm / Minimal | Very slow subtle motion, gentle fades | High whitespace, muted palette, serif typography, generous padding |
| Editorial / Magazine | Staggered text reveals, image-text interplay | Strong type hierarchy, pull quotes, grid-breaking layouts, serif headlines + sans body |
Entrance Animations
/* Fade + Slide Up (most versatile) */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: opacity 0.6s var(--ease-out-expo),
transform 0.6s var(--ease-out-expo);
}
.visible .reveal {
opacity: 1;
transform: translateY(0);
}
/* Scale In */
.reveal-scale {
opacity: 0;
transform: scale(0.9);
transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
}
/* Slide from Left */
.reveal-left {
opacity: 0;
transform: translateX(-50px);
transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
}
/* Blur In */
.reveal-blur {
opacity: 0;
filter: blur(10px);
transition: opacity 0.8s, filter 0.8s var(--ease-out-expo);
}Background Effects
/* Gradient Mesh — layered radial gradients for depth */
.gradient-bg {
background:
radial-gradient(ellipse at 20% 80%, rgba(120, 0, 255, 0.3) 0%, transparent 50%),
radial-gradient(ellipse at 80% 20%, rgba(0, 255, 200, 0.2) 0%, transparent 50%),
var(--bg-primary);
}
/* Noise Texture — inline SVG for grain */
.noise-bg {
background-image: url("data:image/svg+xml,..."); /* Inline SVG noise */
}
/* Grid Pattern — subtle structural lines */
.grid-bg {
background-image:
linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 50px 50px;
}Interactive Effects
/* 3D Tilt on Hover — adds depth to cards/panels */
class TiltEffect {
constructor(element) {
this.element = element;
this.element.style.transformStyle = 'preserve-3d';
this.element.style.perspective = '1000px';
this.element.addEventListener('mousemove', (e) => {
const rect = this.element.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width - 0.5;
const y = (e.clientY - rect.top) / rect.height - 0.5;
this.element.style.transform = `rotateY(${x * 10}deg) rotateX(${-y * 10}deg)`;
});
this.element.addEventListener('mouseleave', () => {
this.element.style.transform = 'rotateY(0) rotateX(0)';
});
}
}Troubleshooting
| Problem | Fix |
|---|---|
| Fonts not loading | Check Fontshare/Google Fonts URL; ensure font names match in CSS |
| Animations not triggering | Verify Intersection Observer is running; check .visible class is being added |
| Scroll snap not working | Ensure scroll-snap-type: y mandatory on html; each slide needs scroll-snap-align: start |
| Mobile issues | Disable heavy effects at 768px breakpoint; test touch events; reduce particle count |
| Performance issues | Use will-change sparingly; prefer transform/opacity animations; throttle scroll handlers |
HTML Presentation Template
Reference architecture for generating slide presentations. Every presentation follows a fixed 16:9 stage model: slides are authored at 1920×1080 and the whole stage scales to fit the browser window.
Base HTML 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>
<!-- Fonts: use Fontshare or Google Fonts — never system fonts -->
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=...">
<style>
/* ===========================================
CSS CUSTOM PROPERTIES (THEME)
Change these to change the whole look
=========================================== */
:root {
/* Colors — from chosen style preset */
--bg-primary: #0a0f1c;
--bg-secondary: #111827;
--text-primary: #ffffff;
--text-secondary: #9ca3af;
--accent: #00ffcc;
--accent-glow: rgba(0, 255, 204, 0.3);
/* Typography — authored at 1920×1080 stage size */
--font-display: 'Clash Display', sans-serif;
--font-body: 'Satoshi', sans-serif;
--title-size: 112px;
--subtitle-size: 34px;
--body-size: 28px;
/* Spacing — authored at 1920×1080 stage size */
--slide-padding: 72px;
--content-gap: 32px;
/* Animation */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--duration-normal: 0.6s;
}
/* ===========================================
BASE STYLES
=========================================== */
* { margin: 0; padding: 0; box-sizing: border-box; }
/* --- PASTE viewport-base.css CONTENTS HERE --- */
/* ===========================================
ANIMATIONS
Trigger via .visible class on the active slide
=========================================== */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: opacity var(--duration-normal) var(--ease-out-expo),
transform var(--duration-normal) var(--ease-out-expo);
}
.slide.visible .reveal {
opacity: 1;
transform: translateY(0);
}
/* Stagger children for sequential reveal */
.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; }
/* ... preset-specific styles ... */
</style>
</head>
<body>
<div class="deck-viewport">
<main class="deck-stage" id="deckStage">
<section class="slide title-slide active">
<h1 class="reveal">Presentation Title</h1>
<p class="reveal">Subtitle or author</p>
</section>
<section class="slide">
<div class="slide-content">
<h2 class="reveal">Slide Title</h2>
<p class="reveal">Content...</p>
</div>
</section>
<!-- More slides... -->
</main>
</div>
<script>
/* ===========================================
SLIDE PRESENTATION CONTROLLER
=========================================== */
class SlidePresentation {
constructor() {
this.slides = document.querySelectorAll('.slide');
this.currentSlide = 0;
this.stage = document.getElementById('deckStage');
this.setupStageScale();
this.setupKeyboardNav();
this.setupTouchNav();
this.showSlide(0);
}
setupStageScale() {
const scale = () => {
const factor = Math.min(window.innerWidth / 1920, window.innerHeight / 1080);
const x = (window.innerWidth - 1920 * factor) / 2;
const y = (window.innerHeight - 1080 * factor) / 2;
this.stage.style.transform = `translate(${x}px, ${y}px) scale(${factor})`;
};
scale();
window.addEventListener('resize', scale);
}
setupKeyboardNav() {
// Arrow keys, Space, Page Up/Down
}
setupTouchNav() {
// Touch/swipe support for mobile
}
showSlide(index) {
this.currentSlide = Math.max(0, Math.min(index, this.slides.length - 1));
this.slides.forEach((slide, i) => {
slide.classList.toggle('active', i === this.currentSlide);
slide.classList.toggle('visible', i === this.currentSlide);
});
}
}
new SlidePresentation();
</script>
</body>
</html>Required JavaScript Features
Every presentation must include:
1. SlidePresentation Class — Main controller with:
- Keyboard navigation (arrows, space, page up/down)
- Touch/swipe support
- Mouse wheel navigation
- Optional progress indicator or page count, kept outside the slide stage
2. Stage Scaling — For fixed 16:9 presentation behavior:
- Keep all slides at 1920×1080 inside
.deck-stage - Scale the whole stage with one transform
- Letterbox/pillarbox as needed; never reflow slide content per device
3. Optional Enhancements (match to chosen style):
- Custom cursor with trail
- Particle system background (canvas)
- Parallax effects
- 3D tilt on hover
- Magnetic buttons
- Counter animations
4. Inline Editing (included by default after draft generation):
- Edit toggle button (hidden by default, revealed via hover hotzone or
Ekey) - Auto-save to localStorage
- Export/save file functionality
- See "Inline Editing Implementation" section below
Inline Editing Implementation
Inline editing is a lightweight post-draft affordance. Do not ask the user whether they want it during the pre-generation Q&A. Include it by default unless the user explicitly asks for a locked/export-only presentation or no editing controls.
Do NOT use CSS `~` sibling selector for hover-based show/hide. The CSS-only approach (edit-hotzone:hover ~ .edit-toggle) fails because pointer-events: none on the toggle button breaks the hover chain: user hovers hotzone -> button becomes visible -> mouse moves toward button -> leaves hotzone -> button disappears before click.
Required approach: JS-based hover with 400ms delay timeout.
HTML:
<div class="edit-hotzone"></div>
<button class="edit-toggle" id="editToggle" title="Edit mode (E)">✏️</button>CSS (visibility controlled by JS classes only):
/* Do NOT use CSS ~ sibling selector for this!
pointer-events: none breaks the hover chain.
Must use JS with delay timeout. */
.edit-hotzone {
position: fixed; top: 0; left: 0;
width: 80px; height: 80px;
z-index: 10000;
cursor: pointer;
}
.edit-toggle {
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
z-index: 10001;
}
.edit-toggle.show,
.edit-toggle.active {
opacity: 1;
pointer-events: auto;
}JS (three interaction methods):
// 1. Click handler on the toggle button
document.getElementById('editToggle').addEventListener('click', () => {
editor.toggleEditMode();
});
// 2. Hotzone hover with 400ms grace period
const hotzone = document.querySelector('.edit-hotzone');
const editToggle = document.getElementById('editToggle');
let hideTimeout = null;
hotzone.addEventListener('mouseenter', () => {
clearTimeout(hideTimeout);
editToggle.classList.add('show');
});
hotzone.addEventListener('mouseleave', () => {
hideTimeout = setTimeout(() => {
if (!editor.isActive) editToggle.classList.remove('show');
}, 400);
});
editToggle.addEventListener('mouseenter', () => {
clearTimeout(hideTimeout);
});
editToggle.addEventListener('mouseleave', () => {
hideTimeout = setTimeout(() => {
if (!editor.isActive) editToggle.classList.remove('show');
}, 400);
});
// 3. Hotzone direct click
hotzone.addEventListener('click', () => {
editor.toggleEditMode();
});
// 4. Keyboard shortcut (E key, skip when editing text)
document.addEventListener('keydown', (e) => {
if ((e.key === 'e' || e.key === 'E') && !e.target.getAttribute('contenteditable')) {
editor.toggleEditMode();
}
});Image Pipeline (Skip If No Images)
If user chose "No images" in Phase 1, skip this entirely. If images were provided, process them before generating HTML.
Dependency: pip install Pillow
Image Processing
from PIL import Image, ImageDraw
# Circular crop (for logos on modern/clean styles)
def crop_circle(input_path, output_path):
img = Image.open(input_path).convert('RGBA')
w, h = img.size
size = min(w, h)
left, top = (w - size) // 2, (h - size) // 2
img = img.crop((left, top, left + size, top + size))
mask = Image.new('L', (size, size), 0)
ImageDraw.Draw(mask).ellipse([0, 0, size, size], fill=255)
img.putalpha(mask)
img.save(output_path, 'PNG')
# Resize (for oversized images that inflate HTML)
def resize_max(input_path, output_path, max_dim=1200):
img = Image.open(input_path)
img.thumbnail((max_dim, max_dim), Image.LANCZOS)
img.save(output_path, quality=85)| Situation | Operation |
|---|---|
| Square logo on rounded aesthetic | crop_circle() |
| Image > 1MB | resize_max(max_dim=1200) |
| Wrong aspect ratio | Manual crop with img.crop() |
Save processed images with _processed suffix. Never overwrite originals.
Image Placement
Use direct file paths (not base64) — presentations are viewed locally:
<img src="assets/logo_round.png" alt="Logo" class="slide-image logo">
<img src="assets/screenshot.png" alt="Screenshot" class="slide-image screenshot">.slide-image {
max-width: 100%;
max-height: min(50vh, 400px);
object-fit: contain;
border-radius: 8px;
}
.slide-image.screenshot {
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.slide-image.logo {
max-height: min(30vh, 200px);
}Adapt border/shadow colors to match the chosen style's accent. Never repeat the same image on multiple slides (except logos on title + closing).
Placement patterns: Logo centered on title slide. Screenshots in two-column layouts with text. Full-bleed images as slide backgrounds with text overlay (use sparingly).
---
Code Quality
Comments: Every section needs clear comments explaining what it does and how to modify it.
Accessibility:
- Semantic HTML (
<section>,<nav>,<main>) - Keyboard navigation works fully
- ARIA labels where needed
prefers-reduced-motionsupport (included in viewport-base.css)
File Structure
Single presentations:
presentation.html # Self-contained, all CSS/JS inline
assets/ # Images only, if anyMultiple presentations in one project:
[name].html
[name]-assets/Style Presets Reference
Curated visual styles for Frontend Slides. Each preset is inspired by real design references — no generic "AI slop" aesthetics. Abstract shapes only — no illustrations.
Viewport CSS: For mandatory base styles, see viewport-base.css. Include in every presentation.
---
Dark Themes
1. Bold Signal
Vibe: Confident, bold, modern, high-impact
Layout: Colored card on dark gradient. Number top-left, navigation top-right, title bottom-left.
Typography:
- Display:
Archivo Black(900) - Body:
Space Grotesk(400/500)
Colors:
:root {
--bg-primary: #1a1a1a;
--bg-gradient: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 50%, #1a1a1a 100%);
--card-bg: #FF5722;
--text-primary: #ffffff;
--text-on-card: #1a1a1a;
}Signature Elements:
- Bold colored card as focal point (orange, coral, or vibrant accent)
- Large section numbers (01, 02, etc.)
- Navigation breadcrumbs with active/inactive opacity states
- Grid-based layout for precise alignment
---
2. Electric Studio
Vibe: Bold, clean, professional, high contrast
Layout: Split panel—white top, blue bottom. Brand marks in corners.
Typography:
- Display:
Manrope(800) - Body:
Manrope(400/500)
Colors:
:root {
--bg-dark: #0a0a0a;
--bg-white: #ffffff;
--accent-blue: #4361ee;
--text-dark: #0a0a0a;
--text-light: #ffffff;
}Signature Elements:
- Two-panel vertical split
- Accent bar on panel edge
- Quote typography as hero element
- Minimal, confident spacing
---
3. Creative Voltage
Vibe: Bold, creative, energetic, retro-modern
Layout: Split panels—electric blue left, dark right. Script accents.
Typography:
- Display:
Syne(700/800) - Mono:
Space Mono(400/700)
Colors:
:root {
--bg-primary: #0066ff;
--bg-dark: #1a1a2e;
--accent-neon: #d4ff00;
--text-light: #ffffff;
}Signature Elements:
- Electric blue + neon yellow contrast
- Halftone texture patterns
- Neon badges/callouts
- Script typography for creative flair
---
4. Dark Botanical
Vibe: Elegant, sophisticated, artistic, premium
Layout: Centered content on dark. Abstract soft shapes in corner.
Typography:
- Display:
Cormorant(400/600) — elegant serif - Body:
IBM Plex Sans(300/400)
Colors:
:root {
--bg-primary: #0f0f0f;
--text-primary: #e8e4df;
--text-secondary: #9a9590;
--accent-warm: #d4a574;
--accent-pink: #e8b4b8;
--accent-gold: #c9b896;
}Signature Elements:
- Abstract soft gradient circles (blurred, overlapping)
- Warm color accents (pink, gold, terracotta)
- Thin vertical accent lines
- Italic signature typography
- No illustrations—only abstract CSS shapes
---
Light Themes
5. Notebook Tabs
Vibe: Editorial, organized, elegant, tactile
Layout: Cream paper card on dark background. Colorful tabs on right edge.
Typography:
- Display:
Bodoni Moda(400/700) — classic editorial - Body:
DM Sans(400/500)
Colors:
:root {
--bg-outer: #2d2d2d;
--bg-page: #f8f6f1;
--text-primary: #1a1a1a;
--tab-1: #98d4bb; /* Mint */
--tab-2: #c7b8ea; /* Lavender */
--tab-3: #f4b8c5; /* Pink */
--tab-4: #a8d8ea; /* Sky */
--tab-5: #ffe6a7; /* Cream */
}Signature Elements:
- Paper container with subtle shadow
- Colorful section tabs on right edge (vertical text)
- Binder hole decorations on left
- Tab text must scale with viewport:
font-size: clamp(0.5rem, 1vh, 0.7rem)
---
6. Pastel Geometry
Vibe: Friendly, organized, modern, approachable
Layout: White card on pastel background. Vertical pills on right edge.
Typography:
- Display:
Plus Jakarta Sans(700/800) - Body:
Plus Jakarta Sans(400/500)
Colors:
:root {
--bg-primary: #c8d9e6;
--card-bg: #faf9f7;
--pill-pink: #f0b4d4;
--pill-mint: #a8d4c4;
--pill-sage: #5a7c6a;
--pill-lavender: #9b8dc4;
--pill-violet: #7c6aad;
}Signature Elements:
- Rounded card with soft shadow
- Vertical pills on right edge with varying heights (like tabs)
- Consistent pill width, heights: short → medium → tall → medium → short
- Download/action icon in corner
---
7. Split Pastel
Vibe: Playful, modern, friendly, creative
Layout: Two-color vertical split (peach left, lavender right).
Typography:
- Display:
Outfit(700/800) - Body:
Outfit(400/500)
Colors:
:root {
--bg-peach: #f5e6dc;
--bg-lavender: #e4dff0;
--text-dark: #1a1a1a;
--badge-mint: #c8f0d8;
--badge-yellow: #f0f0c8;
--badge-pink: #f0d4e0;
}Signature Elements:
- Split background colors
- Playful badge pills with icons
- Grid pattern overlay on right panel
- Rounded CTA buttons
---
8. Vintage Editorial
Vibe: Witty, confident, editorial, personality-driven
Layout: Centered content on cream. Abstract geometric shapes as accent.
Typography:
- Display:
Fraunces(700/900) — distinctive serif - Body:
Work Sans(400/500)
Colors:
:root {
--bg-cream: #f5f3ee;
--text-primary: #1a1a1a;
--text-secondary: #555;
--accent-warm: #e8d4c0;
}Signature Elements:
- Abstract geometric shapes (circle outline + line + dot)
- Bold bordered CTA boxes
- Witty, conversational copy style
- No illustrations—only geometric CSS shapes
---
Specialty Themes
9. Neon Cyber
Vibe: Futuristic, techy, confident
Typography: Clash Display + Satoshi (Fontshare)
Colors: Deep navy (#0a0f1c), cyan accent (#00ffcc), magenta (#ff00aa)
Signature: Particle backgrounds, neon glow, grid patterns
---
10. Terminal Green
Vibe: Developer-focused, hacker aesthetic
Typography: JetBrains Mono (monospace only)
Colors: GitHub dark (#0d1117), terminal green (#39d353)
Signature: Scan lines, blinking cursor, code syntax styling
---
11. Swiss Modern
Vibe: Clean, precise, Bauhaus-inspired
Typography: Archivo (800) + Nunito (400)
Colors: Pure white, pure black, red accent (#ff3300)
Signature: Visible grid, asymmetric layouts, geometric shapes
---
12. Paper & Ink
Vibe: Editorial, literary, thoughtful
Typography: Cormorant Garamond + Source Serif 4
Colors: Warm cream (#faf9f7), charcoal (#1a1a1a), crimson accent (#c41e3a)
Signature: Drop caps, pull quotes, elegant horizontal rules
---
Font Pairing Quick Reference
| Preset | Display Font | Body Font | Source |
|---|---|---|---|
| Bold Signal | Archivo Black | Space Grotesk | |
| Electric Studio | Manrope | Manrope | |
| Creative Voltage | Syne | Space Mono | |
| Dark Botanical | Cormorant | IBM Plex Sans | |
| Notebook Tabs | Bodoni Moda | DM Sans | |
| Pastel Geometry | Plus Jakarta Sans | Plus Jakarta Sans | |
| Split Pastel | Outfit | Outfit | |
| Vintage Editorial | Fraunces | Work Sans | |
| Neon Cyber | Clash Display | Satoshi | Fontshare |
| Terminal Green | JetBrains Mono | JetBrains Mono | JetBrains |
---
DO NOT USE (Generic AI Patterns)
Fonts: Inter, Roboto, Arial, system fonts as display
Colors: #6366f1 (generic indigo), purple gradients on white
Layouts: Everything centered, generic hero sections, identical card grids
Decorations: Realistic illustrations, gratuitous glassmorphism, drop shadows without purpose
---
CSS Gotchas
Negating CSS Functions
WRONG — silently ignored by browsers (no console error):
right: -clamp(28px, 3.5vw, 44px); /* Browser ignores this */
margin-left: -min(10vw, 100px); /* Browser ignores this */CORRECT — wrap in `calc()`:
right: calc(-1 * clamp(28px, 3.5vw, 44px)); /* Works */
margin-left: calc(-1 * min(10vw, 100px)); /* Works */CSS does not allow a leading - before function names. The browser silently discards the entire declaration — no error, the element just appears in the wrong position. *Always use `calc(-1 ...)` to negate CSS function values.**
/* ===========================================
FIXED 16:9 STAGE: MANDATORY BASE STYLES
Include this ENTIRE file in every presentation.
Slides are authored at 1920×1080 and scaled as a whole.
=========================================== */
/* 1. Lock the browser viewport */
html,
body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
background: var(--stage-bg, #000);
}
/* 2. Full-window deck viewport */
.deck-viewport {
position: fixed;
inset: 0;
overflow: hidden;
background: var(--stage-bg, #000);
}
/* 3. Fixed 16:9 design canvas.
JavaScript sets transform: translate(...) scale(...). */
.deck-stage {
position: absolute;
left: 0;
top: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
transform-origin: 0 0;
background: var(--slide-bg, #fff);
}
/* 4. Slides stack inside the fixed stage.
Content must be laid out at 1920×1080, not reflowed per device. */
.slide {
position: absolute;
inset: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
display: block;
visibility: hidden;
opacity: 0;
pointer-events: none;
background: var(--slide-bg, #fff);
}
.slide.active,
.slide.visible {
visibility: visible;
opacity: 1;
pointer-events: auto;
z-index: 1;
}
/* 5. Keep media inside authored slide bounds */
img,
video,
canvas,
svg {
max-width: 100%;
max-height: 100%;
}
/* 6. Presentation chrome stays outside the slide design system */
.deck-controls {
position: fixed;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
z-index: 1000;
}
/* 7. Print one fixed-size slide per page */
@media print {
html,
body {
width: 1920px;
height: auto;
overflow: visible;
background: #fff;
}
.deck-viewport {
position: static;
overflow: visible;
background: #fff;
}
.deck-stage {
position: static;
width: auto;
height: auto;
transform: none !important;
background: none;
}
.slide {
position: relative;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
pointer-events: auto !important;
width: 1920px;
height: 1080px;
break-after: page;
page-break-after: always;
}
.slide:last-child {
break-after: auto;
page-break-after: auto;
}
.deck-controls {
display: none !important;
}
}
/* 8. Reduced motion */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.2s !important;
}
}
Related skills
How it compares
Choose frontend-slides when the deliverable must be animated HTML embeddable in a web app rather than a document-first slide export.
FAQ
Canvas size?
Fixed 1920x1080 uniform scale.
Seed file?
example.html with SlidePresentation script.
Breakpoints inside slides?
No never reflow per device.