Overwatch Slide Deck Designer
Build interactive, presentation-grade slide decks as live SPAs—not static PDFs. Vite + React 19 + TanStack Router, 1920x1080 native resolution, WebGPU shader backgrounds, Framer Motion orchestration, collapsible sidebar navigation, keyboard controls, an optional access gate, and deploy to Cloudflare Workers / Vercel / Netlify.
When to Use This Skill
- Live presentations (investor pitches, product demos, conference talks)
- Interaction-rich decks with hover states, content swaps, tooltips
- WebGPU shader backgrounds for dramatic cover slides
- Decks that need an access gate (client-side; see Access Gate section for its limits)
- Keyboard-navigated presentations with sidebar navigation
Domains Supported
| Domain |
Use Cases |
| Business & Finance |
KPI dashboards, revenue charts, growth trends, pricing |
| Healthcare |
Patient metrics, clinical outcomes, treatment timelines |
| Wellness & Coaching |
Transformation journeys, milestone celebrations, quotes |
| AI/ML Research |
Model architecture, training metrics, STT/TTS pipelines |
Quick Start Workflow
1. Audit Reference Screenshots
Review existing example screenshots for aesthetic consistency before designing.
open ~/.claude/skills/overwatch-slidedeck/assets/examples/
15 reference screenshots demonstrate the canonical aesthetic: dark backgrounds, orange accent (#ff6e41), Playfair Display headings, interactive hover states, WebGPU shader covers.
2. Scaffold New Project
cp -r ~/.claude/skills/overwatch-slidedeck/assets/scaffold/ ./my-deck
cd my-deck
npm install
npm run dev # Opens at http://localhost:5173
# Navigate to /deck/1
3. Review Iconography Options
| Domain |
Key Icons |
| AI/ML |
Brain, Cpu, Database, Layers, Network, Waveform |
| Business |
TrendingUp, DollarSign, PieChart, Target, Users |
| Healthcare |
Heart, Activity, Stethoscope, ShieldCheck |
| Parenting |
Baby, Users, HandHeart, Shield, Brain, Puzzle |
| Spirituality |
Compass, Flame, Mountain, Sunrise, Lightbulb |
Icon libraries (not included in scaffold—install as needed):
4. Plan Content Structure
- Define sections — Group content into 4-8 logical sections
- Outline slides — 3-5 slides per section
- Identify slide types — Cover, social proof, split text, feature grid, timeline, quote, CLI demo, etc. (14 types available—see
references/slide-templates.md)
- Gather assets — Screenshots, diagrams, logos, expert photos
- Select icons — Map icons to sections/concepts
What separates a hand-crafted deck from AI slop. VCs and executives now read dozens of generated decks a week and can spot them instantly — same stock layouts, same buzzword density, same fabricated polish. The failure modes to design against:
- No unsourced numbers. "47% growth" with no traceable source is the signature tell of a generated deck. Every statistic on a slide comes from the research phase (step 6) with a source the presenter can name. If the number can't be sourced, cut the slide, not the sourcing.
- Headlines carry the insight. Title the data slide "Churn dropped 31% after the onboarding fix," never "Churn Data." A slide whose headline could sit on any company's deck says nothing.
- Specificity over vocabulary. "Reviews 1,324 documents in 40 seconds" beats "leverages cutting-edge AI to transform document workflows." When a sentence would survive on a competitor's deck unchanged, rewrite it.
- One motion grammar per deck. Pick one entrance vocabulary (which AnimatedItem variants, one duration scale, one easing) and hold it across every slide — motion consistency is what makes 20 slides read as one designed object rather than a template assembly.
- Decks travel without the presenter. Boards forward slides; executives read them on phones at night. Every slide must be self-explanatory — hover-revealed content needs its key point visible before the hover.
5. Brand Color Extraction
Extract and map brand colors from the client/company site before building slides.
# Scrape the client/company site for brand identity
python3 ~/.claude/skills/firecrawl/scripts/firecrawl_api.py scrape "https://client-site.com" --formats branding
# Or take a screenshot for visual reference
agent-browser open https://client-site.com
agent-browser screenshot /tmp/brand-reference.png
Override the primary accent in src/styles/globals.css:
:root {
--color-orange: #your-brand-color;
--color-orange-muted: #your-darker-variant;
}
Anti-patterns:
- Never use bright reds (
#DC2626, #E11D48)—they read as error/danger
- Never use saturated yellows (
#fbbf24) on light backgrounds—they disappear
- Darken to amber-800/900 range (
#92400E, #B45309) for readability
6. Research & Content Gathering
Available research tools (separate skills/CLIs):
| Tool |
Skill/CLI |
Purpose |
| Firecrawl |
firecrawl CLI or Firecrawl skill |
Web scraping, convert URLs to markdown |
| Exa Search |
exa-search skill |
AI-powered neural search, code examples |
| Reddit JSON |
Native curl (no auth) |
User feedback, pain points, discussions |
# Reddit JSON API (no auth required)
curl "https://www.reddit.com/r/{SUBREDDIT}/search.json?q={QUERY}&limit=25&sort=relevance"
# Exa Search (AI-powered)
exa-search "{domain} best practices" --type article
# Firecrawl - competitor decks, methodology pages
firecrawl scrape https://example.com/methodology
7. Build Slides
Each slide lives in src/slides/ and uses the SlideWrapper:
import { SlideWrapper } from "../components/layout/SlideWrapper";
import { Headline } from "../components/layout/Headline";
import { BodyText } from "../components/layout/BodyText";
export default function ProblemSlide() {
return (
<SlideWrapper mode="dark">
<Headline>The Problem</Headline>
<BodyText className="mt-8">Your content here</BodyText>
</SlideWrapper>
);
}
Register each slide in src/config.ts:
export const slides: SlideEntry[] = [
{ id: "cover", fileKey: "01-cover", title: "Cover", shortTitle: "Cover" },
{ id: "problem", fileKey: "02-problem", title: "The Problem", shortTitle: "Problem" },
];
const slideModules = {
"01-cover": () => import("./slides/01-cover"),
"02-problem": () => import("./slides/02-problem"),
};
Slide modes: dark (deep charcoal, default), white (off-white, text-heavy analytical), orange (accent background, emphasis/quotes). Most slides use dark; white for analytical content; orange sparingly.
Slide transitions: the shell animates between slides via AnimatePresence, direction-aware (forward enters from the right, backward from the left). Deck-level default in config.transition (none | fade | slide | scale, default fade) with config.transitionDuration in ms; per-slide overrides via the same fields on a SlideEntry. One transition type per deck is the rule — per-slide overrides exist for section dividers and the closing slide, not variety. prefers-reduced-motion and ?static=1 both force none.
Speaker notes: notes?: string on any SlideEntry (or notes: in the YAML spec). Notes render only in presenter mode — the audience window never shows them.
8. Design QA Checklist
Before deploying, verify with agent-browser screenshots at http://localhost:5173:
| Check |
Requirement |
| ✅ Slide modes |
SlideWrapper mode matches content type (dark/white/orange) |
| ✅ Sidebar |
Auto-collapses after 3s, hover expands, nav indicator tracks |
| ✅ Shader fallback |
WebGPU cover degrades to animated CSS gradient |
| ✅ Keyboard nav |
ArrowRight/Space (next), ArrowLeft (prev), Home/End |
| ✅ Access gate |
If configured, blocks without correct password |
| ✅ Typography |
Playfair Display for headlines, Inter for body, IBM Plex Mono for labels |
| ✅ Hover states |
HoverLift, GlowBorder, ExpandableCard respond correctly |
| ✅ Animations |
StaggeredAnimation entrance on each slide, no layout thrash |
| ✅ Mobile block |
Shows "Desktop Required" below 375px viewport |
| ✅ Slide counter |
Bottom-right counter shows correct 01/NN |
| ✅ Transitions |
Forward/backward navigation animates in opposite directions; one grammar deck-wide |
| ✅ Reduced motion |
With prefers-reduced-motion, transitions and auto-cycling stop, content renders final-state |
| ✅ Presenter sync |
/presenter/1 shows notes + next slide; navigation syncs both windows |
| ✅ Deep links |
Invalid /deck/N clamps to a valid slide; deployed deep links don't 404 |
# Visual verification workflow — slides change by URL, not scroll
npm run dev &
agent-browser open http://localhost:5173/deck/1
agent-browser screenshot /tmp/slide-01.png
agent-browser open http://localhost:5173/deck/2
agent-browser screenshot /tmp/slide-02.png
9. Present
Open /presenter/1 in a second window (or second screen) alongside the audience deck:
- Current slide, next-slide preview, speaker notes, elapsed timer, slide counter
- Keyboard navigation works in either window; a
BroadcastChannel('overwatch-deck') keeps both in sync — navigate in the presenter, the audience follows, and vice versa
- Same-origin windows only (two tabs/windows of the same deployment); for remote presenting, share the audience window over the call and drive from the presenter
10. Export (PNG / PDF)
The deck stays live-first; export is derived output for board packets and email:
cd ~/.claude/skills/overwatch-slidedeck/scripts && npm install # once: playwright, pdf-lib, yaml
npm run dev & # deck must be serving
node ~/.claude/skills/overwatch-slidedeck/scripts/export-deck.mjs --url http://localhost:5173 --out ./export --pdf
Each slide renders at 1920x1080 with ?static=1 and reduced-motion emulation so entrances land in their final state — no mid-animation frames. --pdf merges the PNGs into deck.pdf. Shader covers export as their current frame; expect the live deck to look better than its export, by design.
11. Deploy
# Build static SPA
npm run build
# Output: dist/
# Deploy to Cloudflare Workers (wrangler.jsonc included in scaffold)
npx wrangler deploy
# Deploy to Vercel (vercel.json included)
npx vercel
# Deploy to Netlify (public/_redirects included)
npx netlify deploy --prod
SPA rewrites for all three hosts ship in the scaffold, so /deck/7 opened directly on a deployed URL resolves instead of 404ing.
Component Library
Layout Components (10)
| Component |
Import |
Purpose |
SlideWrapper |
layout/SlideWrapper |
Full-slide container with mode prop (dark/white/orange) |
Headline |
layout/Headline |
140px display title |
SubHeadline |
layout/SubHeadline |
72px secondary title |
Eyebrow |
layout/Eyebrow |
Small caps category label |
BodyText |
layout/BodyText |
Body copy (sm/md/lg) |
MonoLabel |
layout/MonoLabel |
Monospace label (sm/md/lg) |
Divider |
layout/Divider |
Configurable hr (thin/medium/thick) |
SplitLayout |
layout/SplitLayout |
Two-column with ratio (1:1, 2:1, 1:2, 3:2, 2:3) |
CenterLayout |
layout/CenterLayout |
Centered flex container |
GridLayout |
layout/GridLayout |
2/3/4 column grid |
Interaction Components (18)
| Component |
Import |
Purpose |
AnimatedItem |
interactions/AnimatedItem |
Entrance variants: fade/slideUp/slideLeft/scale |
StaggeredAnimation |
interactions/StaggeredAnimation |
Parent container with stagger timing |
HoverLift |
interactions/HoverLift |
Hover elevation (sm/md/lg) |
GlowBorder |
interactions/GlowBorder |
Mouse-tracking gradient border |
ExpandableCard |
interactions/ExpandableCard |
Click-to-expand with layout animation |
Accordion |
interactions/Accordion |
Collapsible sections |
TabGroup |
interactions/TabGroup |
Tabbed content panels |
QuoteRotator |
interactions/QuoteRotator |
Auto-cycling quotes with dot indicators |
ContentRotator |
interactions/ContentRotator |
Auto-cycling arbitrary ReactNode children with dots |
SocialProofCard |
interactions/SocialProofCard |
Platform-styled testimonial (twitter/linkedin/testimonial) |
TerminalTyper |
interactions/TerminalTyper |
Typewriter CLI demo with macOS terminal chrome |
TimelineConnector |
interactions/TimelineConnector |
Horizontal roadmap with animated SVG connectors |
InfiniteScrollTicker |
interactions/InfiniteScrollTicker |
Vertical marquee with gradient masks |
ProgressBar |
interactions/ProgressBar |
Animated horizontal fill bar with label |
RevealCaption |
interactions/RevealCaption |
Hover caption overlay |
Tooltip |
interactions/Tooltip |
Position-aware tooltip |
PulseIndicator |
interactions/PulseIndicator |
Pulsing dot + expanding ring |
Skeleton |
interactions/Skeleton |
Loading placeholder |
Graphics Components (4)
| Component |
Import |
Purpose |
WebGPUCanvas |
graphics/WebGPUCanvas |
WebGPU shader host + CSS gradient fallback |
ParticleField |
graphics/ParticleField |
Floating particle animation |
NetworkGraph |
graphics/NetworkGraph |
Pulsing node-ring visualization |
SVGRadarChart |
graphics/SVGRadarChart |
Zero-dependency SVG radar chart with pathLength animation |
Utility Hooks (3)
| Hook |
Import |
Purpose |
useAutoCycle |
hooks/useAutoCycle |
Generic auto-advancing timer: [currentItem, index, setIndex]. Pauses while the tab is hidden; no-ops under reduced motion |
useTypewriter |
hooks/useTypewriter |
Character-by-character text reveal: { displayText, isComplete }. Pauses while hidden; renders full text under reduced motion |
useReducedMotion |
hooks/useReducedMotion |
Live prefers-reduced-motion subscription — every animated component reads it |
Every auto-cycling and entrance component honors prefers-reduced-motion (final state, no cycling) and interactive components (TabGroup, Accordion, Tooltip) carry ARIA roles and keyboard navigation. Global keyboard shortcuts ignore keypresses on interactive targets.
Navigation
- Sidebar: Auto-collapses after 3s, hover to expand, spring-animated (
stiffness: 400, damping: 30)
- Keyboard: ArrowRight/Space (next), ArrowLeft (prev), Home/End
- URL-based:
/deck/1, /deck/2, etc. via TanStack Router
- Preloading: Adjacent slides (n-1, n+1, n+2) are preloaded for instant navigation
Access Gate
Set config.auth.password in src/config.ts:
auth: { password: "your-password" } // Empty string = no gate
Supports ?pw=your-password URL param for direct access. State persists via sessionStorage.
This is an access gate, not security: it's client-side, so the password and all slide content ship in the JavaScript bundle to anyone who requests the URL. It keeps casual link-forwards from opening the deck — the right tool for "don't let this circulate ahead of the meeting." For a deck that genuinely cannot leak (unannounced financials, M&A), put real authorization in front of the assets — Cloudflare Access or Vercel deployment protection — and keep the gate for UX.
Data-Driven Authoring
For faster deck creation, provide a YAML spec file describing each slide's type, content, and mode:
node ~/.claude/skills/overwatch-slidedeck/scripts/init-deck-from-spec.mjs deck-spec.yaml ./my-deck
The script copies the scaffold, generates config.ts, and creates empty slide files. Fill in each slide using the spec + references/slide-templates.md.
See references/deck-schema.md for the full YAML schema covering all 14 slide types.
Custom Asset Generation
Generate custom icons and graphics matching the Overwatch aesthetic:
Pipeline: Nano Banana Pro → ImageMagick → Potrace → SVG Cleanup
# Generate an icon matching the dark/orange aesthetic
nano-banana-pro "Minimalist neural network icon, flat design,
3 solid colors, orange #ff6e41 on dark #0c0c0e, geometric"
# Vectorize
magick output.png -posterize 4 -colors 4 processed.png
potrace processed.pbm -s -o icon.svg
# Optimize
svgo icon.svg -o icon-optimized.svg
Required tools:
nano-banana-pro skill (Gemini 3 Pro image generation)
- ImageMagick (
brew install imagemagick)
- Potrace (
brew install potrace)
- SVGO (
npm install -g svgo)
Reference Documentation
| File |
Purpose |
references/design-system.md |
Color tokens, typography, dimensions, 3 slide modes |
references/interactions.md |
Animation patterns, timing, 18 interaction components + the useAutoCycle/useTypewriter hooks |
references/shaders.md |
WebGPU setup, WGSL syntax, custom shaders, fallback |
references/slide-templates.md |
14 slide type templates with component composition |
references/advanced-patterns.md |
6 domain-specific patterns (waterfall, carousel, strikethrough, dual-layer shader) |
references/deck-schema.md |
YAML schema for data-driven deck authoring |
Tips
- One idea per slide — keep content density low; Overwatch decks are visual, not textual
- Slide modes matter — dark for most content, white for analytical/text-heavy, orange for emphasis moments
- Import from
"motion/react" — not "framer-motion" (the package is motion v12+)
- Shader fallback — always test with WebGPU disabled; the CSS gradient fallback must look intentional
- Test keyboard nav — ArrowRight/Space/ArrowLeft/Home/End should work on every slide
- Preload wisely — the scaffold preloads n-1, n+1, n+2; adjust in
routes/deck.$slide.tsx if needed
- Deploy early — test on Cloudflare/Vercel before the presentation; local dev can mask font loading issues
Related Skills
- aldea-slidedeck — static single-file HTML decks (Blueprint Mode). If the deck ships as a file rather than a URL, use aldea.
- conductor-motion — self-contained behavioral animation demos (typewriter, progress, streaming text). Borrow its timing constants for in-slide motion; don't rebuild its patterns as React components.
- component-gallery — pattern research for slide content (real product UI, tool-call displays, dashboards). Query it before inventing an interface mock.
- minoan-frontend-design — creative direction and typography once the deck structure is set.
1---2name: overwatch-slidedeck3description: Build interactive live slide decks with Vite + React 19, TanStack Router, WebGPU shaders, Framer Motion orchestration, 1920x1080, access-gated SPA. 39 components (layout, interaction, graphics, chrome, navigation), direction-aware slide transitions, presenter mode with speaker notes and BroadcastChannel sync, Playwright PNG/PDF export, YAML-driven authoring, Cloudflare/Vercel/Netlify deploy. Triggers on: slide deck, presentation, pitch deck, investor deck, product demo, conference talk, live slides, WebGPU slides, interactive presentation, presenter mode, speaker notes, overwatch deck.4---5
6# Overwatch Slide Deck Designer
7
8Build interactive, presentation-grade slide decks as live SPAs—not static PDFs. Vite + React 19 + TanStack Router, 1920x1080 native resolution, WebGPU shader backgrounds, Framer Motion orchestration, collapsible sidebar navigation, keyboard controls, an optional access gate, and deploy to Cloudflare Workers / Vercel / Netlify.
9
10## When to Use This Skill
11
12- Live presentations (investor pitches, product demos, conference talks)
13- Interaction-rich decks with hover states, content swaps, tooltips
14- WebGPU shader backgrounds for dramatic cover slides
15- Decks that need an access gate (client-side; see Access Gate section for its limits)
16- Keyboard-navigated presentations with sidebar navigation
17
18## Domains Supported
19
20| Domain | Use Cases |
21|--------|-----------|
22| **Business & Finance** | KPI dashboards, revenue charts, growth trends, pricing |
23| **Healthcare** | Patient metrics, clinical outcomes, treatment timelines |
24| **Wellness & Coaching** | Transformation journeys, milestone celebrations, quotes |
25| **AI/ML Research** | Model architecture, training metrics, STT/TTS pipelines |
26
27---
28
29## Quick Start Workflow
30
31### 1. Audit Reference Screenshots
32
33Review existing example screenshots for aesthetic consistency before designing.
34
35```bash
36open ~/.claude/skills/overwatch-slidedeck/assets/examples/
37```
38
3915 reference screenshots demonstrate the canonical aesthetic: dark backgrounds, orange accent (#ff6e41), Playfair Display headings, interactive hover states, WebGPU shader covers.
40
41### 2. Scaffold New Project
42
43```bash
44cp -r ~/.claude/skills/overwatch-slidedeck/assets/scaffold/ ./my-deck
45cd my-deck
46npm install
47npm run dev # Opens at http://localhost:5173
48# Navigate to /deck/1
49```
50
51### 3. Review Iconography Options
52
53| Domain | Key Icons |
54|--------|-----------|
55| **AI/ML** | Brain, Cpu, Database, Layers, Network, Waveform |
56| **Business** | TrendingUp, DollarSign, PieChart, Target, Users |
57| **Healthcare** | Heart, Activity, Stethoscope, ShieldCheck |
58| **Parenting** | Baby, Users, HandHeart, Shield, Brain, Puzzle |
59| **Spirituality** | Compass, Flame, Mountain, Sunrise, Lightbulb |
60
61**Icon libraries (not included in scaffold—install as needed):**
62- **Lucide** (1,500+): `lucide-react` — https://lucide.dev/icons
63- **Tabler** (5,900+): `@tabler/icons-react` — https://tabler.io/icons
64- **Phosphor** (7,000+): `@phosphor-icons/react` — https://phosphoricons.com
65
66### 4. Plan Content Structure
67
681. **Define sections** — Group content into 4-8 logical sections
692. **Outline slides** — 3-5 slides per section
703. **Identify slide types** — Cover, social proof, split text, feature grid, timeline, quote, CLI demo, etc. (14 types available—see `references/slide-templates.md`)
714. **Gather assets** — Screenshots, diagrams, logos, expert photos
725. **Select icons** — Map icons to sections/concepts
73
74**What separates a hand-crafted deck from AI slop.** VCs and executives now read dozens of generated decks a week and can spot them instantly — same stock layouts, same buzzword density, same fabricated polish. The failure modes to design against:
75
76- **No unsourced numbers.** "47% growth" with no traceable source is the signature tell of a generated deck. Every statistic on a slide comes from the research phase (step 6) with a source the presenter can name. If the number can't be sourced, cut the slide, not the sourcing.
77- **Headlines carry the insight.** Title the data slide "Churn dropped 31% after the onboarding fix," never "Churn Data." A slide whose headline could sit on any company's deck says nothing.
78- **Specificity over vocabulary.** "Reviews 1,324 documents in 40 seconds" beats "leverages cutting-edge AI to transform document workflows." When a sentence would survive on a competitor's deck unchanged, rewrite it.
79- **One motion grammar per deck.** Pick one entrance vocabulary (which AnimatedItem variants, one duration scale, one easing) and hold it across every slide — motion consistency is what makes 20 slides read as one designed object rather than a template assembly.
80- **Decks travel without the presenter.** Boards forward slides; executives read them on phones at night. Every slide must be self-explanatory — hover-revealed content needs its key point visible before the hover.
81
82### 5. Brand Color Extraction
83
84Extract and map brand colors from the client/company site before building slides.
85
86```bash
87# Scrape the client/company site for brand identity
88python3 ~/.claude/skills/firecrawl/scripts/firecrawl_api.py scrape "https://client-site.com" --formats branding
89
90# Or take a screenshot for visual reference
91agent-browser open https://client-site.com
92agent-browser screenshot /tmp/brand-reference.png
93```
94
95Override the primary accent in `src/styles/globals.css`:
96```css
97:root {
98 --color-orange: #your-brand-color;
99 --color-orange-muted: #your-darker-variant;
100}
101```
102
103**Anti-patterns:**
104- Never use bright reds (`#DC2626`, `#E11D48`)—they read as error/danger
105- Never use saturated yellows (`#fbbf24`) on light backgrounds—they disappear
106- Darken to amber-800/900 range (`#92400E`, `#B45309`) for readability
107
108### 6. Research & Content Gathering
109
110**Available research tools (separate skills/CLIs):**
111
112| Tool | Skill/CLI | Purpose |
113|------|-----------|---------|
114| **Firecrawl** | `firecrawl` CLI or Firecrawl skill | Web scraping, convert URLs to markdown |
115| **Exa Search** | `exa-search` skill | AI-powered neural search, code examples |
116| **Reddit JSON** | Native curl (no auth) | User feedback, pain points, discussions |
117
118```bash
119# Reddit JSON API (no auth required)
120curl "https://www.reddit.com/r/{SUBREDDIT}/search.json?q={QUERY}&limit=25&sort=relevance"
121
122# Exa Search (AI-powered)
123exa-search "{domain} best practices" --type article
124
125# Firecrawl - competitor decks, methodology pages
126firecrawl scrape https://example.com/methodology
127```
128
129### 7. Build Slides
130
131Each slide lives in `src/slides/` and uses the `SlideWrapper`:
132
133```tsx
134import { SlideWrapper } from "../components/layout/SlideWrapper";
135import { Headline } from "../components/layout/Headline";
136import { BodyText } from "../components/layout/BodyText";
137
138export default function ProblemSlide() {
139 return (
140 <SlideWrapper mode="dark">
141 <Headline>The Problem</Headline>
142 <BodyText className="mt-8">Your content here</BodyText>
143 </SlideWrapper>
144 );
145}
146```
147
148Register each slide in `src/config.ts`:
149```typescript
150export const slides: SlideEntry[] = [
151 { id: "cover", fileKey: "01-cover", title: "Cover", shortTitle: "Cover" },
152 { id: "problem", fileKey: "02-problem", title: "The Problem", shortTitle: "Problem" },
153];
154
155const slideModules = {
156 "01-cover": () => import("./slides/01-cover"),
157 "02-problem": () => import("./slides/02-problem"),
158};
159```
160
161**Slide modes:** `dark` (deep charcoal, default), `white` (off-white, text-heavy analytical), `orange` (accent background, emphasis/quotes). Most slides use dark; white for analytical content; orange sparingly.
162
163**Slide transitions:** the shell animates between slides via AnimatePresence, direction-aware (forward enters from the right, backward from the left). Deck-level default in `config.transition` (`none | fade | slide | scale`, default `fade`) with `config.transitionDuration` in ms; per-slide overrides via the same fields on a `SlideEntry`. One transition type per deck is the rule — per-slide overrides exist for section dividers and the closing slide, not variety. `prefers-reduced-motion` and `?static=1` both force `none`.
164
165**Speaker notes:** `notes?: string` on any `SlideEntry` (or `notes:` in the YAML spec). Notes render only in presenter mode — the audience window never shows them.
166
167### 8. Design QA Checklist
168
169Before deploying, verify with agent-browser screenshots at `http://localhost:5173`:
170
171| Check | Requirement |
172|-------|-------------|
173| ✅ **Slide modes** | SlideWrapper `mode` matches content type (dark/white/orange) |
174| ✅ **Sidebar** | Auto-collapses after 3s, hover expands, nav indicator tracks |
175| ✅ **Shader fallback** | WebGPU cover degrades to animated CSS gradient |
176| ✅ **Keyboard nav** | ArrowRight/Space (next), ArrowLeft (prev), Home/End |
177| ✅ **Access gate** | If configured, blocks without correct password |
178| ✅ **Typography** | Playfair Display for headlines, Inter for body, IBM Plex Mono for labels |
179| ✅ **Hover states** | HoverLift, GlowBorder, ExpandableCard respond correctly |
180| ✅ **Animations** | StaggeredAnimation entrance on each slide, no layout thrash |
181| ✅ **Mobile block** | Shows "Desktop Required" below 375px viewport |
182| ✅ **Slide counter** | Bottom-right counter shows correct `01/NN` |
183| ✅ **Transitions** | Forward/backward navigation animates in opposite directions; one grammar deck-wide |
184| ✅ **Reduced motion** | With `prefers-reduced-motion`, transitions and auto-cycling stop, content renders final-state |
185| ✅ **Presenter sync** | `/presenter/1` shows notes + next slide; navigation syncs both windows |
186| ✅ **Deep links** | Invalid `/deck/N` clamps to a valid slide; deployed deep links don't 404 |
187
188```bash
189# Visual verification workflow — slides change by URL, not scroll
190npm run dev &
191agent-browser open http://localhost:5173/deck/1
192agent-browser screenshot /tmp/slide-01.png
193agent-browser open http://localhost:5173/deck/2
194agent-browser screenshot /tmp/slide-02.png
195```
196
197### 9. Present
198
199Open `/presenter/1` in a second window (or second screen) alongside the audience deck:
200
201- Current slide, next-slide preview, speaker notes, elapsed timer, slide counter
202- Keyboard navigation works in either window; a `BroadcastChannel('overwatch-deck')` keeps both in sync — navigate in the presenter, the audience follows, and vice versa
203- Same-origin windows only (two tabs/windows of the same deployment); for remote presenting, share the audience window over the call and drive from the presenter
204
205### 10. Export (PNG / PDF)
206
207The deck stays live-first; export is derived output for board packets and email:
208
209```bash
210cd ~/.claude/skills/overwatch-slidedeck/scripts && npm install # once: playwright, pdf-lib, yaml
211npm run dev & # deck must be serving
212node ~/.claude/skills/overwatch-slidedeck/scripts/export-deck.mjs --url http://localhost:5173 --out ./export --pdf
213```
214
215Each slide renders at 1920x1080 with `?static=1` and reduced-motion emulation so entrances land in their final state — no mid-animation frames. `--pdf` merges the PNGs into `deck.pdf`. Shader covers export as their current frame; expect the live deck to look better than its export, by design.
216
217### 11. Deploy
218
219```bash
220# Build static SPA
221npm run build
222# Output: dist/
223
224# Deploy to Cloudflare Workers (wrangler.jsonc included in scaffold)
225npx wrangler deploy
226
227# Deploy to Vercel (vercel.json included)
228npx vercel
229
230# Deploy to Netlify (public/_redirects included)
231npx netlify deploy --prod
232```
233
234SPA rewrites for all three hosts ship in the scaffold, so `/deck/7` opened directly on a deployed URL resolves instead of 404ing.
235
236---
237
238## Component Library
239
240### Layout Components (10)
241
242| Component | Import | Purpose |
243|-----------|--------|---------|
244| `SlideWrapper` | `layout/SlideWrapper` | Full-slide container with `mode` prop (dark/white/orange) |
245| `Headline` | `layout/Headline` | 140px display title |
246| `SubHeadline` | `layout/SubHeadline` | 72px secondary title |
247| `Eyebrow` | `layout/Eyebrow` | Small caps category label |
248| `BodyText` | `layout/BodyText` | Body copy (sm/md/lg) |
249| `MonoLabel` | `layout/MonoLabel` | Monospace label (sm/md/lg) |
250| `Divider` | `layout/Divider` | Configurable hr (thin/medium/thick) |
251| `SplitLayout` | `layout/SplitLayout` | Two-column with ratio (1:1, 2:1, 1:2, 3:2, 2:3) |
252| `CenterLayout` | `layout/CenterLayout` | Centered flex container |
253| `GridLayout` | `layout/GridLayout` | 2/3/4 column grid |
254
255### Interaction Components (18)
256
257| Component | Import | Purpose |
258|-----------|--------|---------|
259| `AnimatedItem` | `interactions/AnimatedItem` | Entrance variants: fade/slideUp/slideLeft/scale |
260| `StaggeredAnimation` | `interactions/StaggeredAnimation` | Parent container with stagger timing |
261| `HoverLift` | `interactions/HoverLift` | Hover elevation (sm/md/lg) |
262| `GlowBorder` | `interactions/GlowBorder` | Mouse-tracking gradient border |
263| `ExpandableCard` | `interactions/ExpandableCard` | Click-to-expand with layout animation |
264| `Accordion` | `interactions/Accordion` | Collapsible sections |
265| `TabGroup` | `interactions/TabGroup` | Tabbed content panels |
266| `QuoteRotator` | `interactions/QuoteRotator` | Auto-cycling quotes with dot indicators |
267| `ContentRotator` | `interactions/ContentRotator` | Auto-cycling arbitrary ReactNode children with dots |
268| `SocialProofCard` | `interactions/SocialProofCard` | Platform-styled testimonial (twitter/linkedin/testimonial) |
269| `TerminalTyper` | `interactions/TerminalTyper` | Typewriter CLI demo with macOS terminal chrome |
270| `TimelineConnector` | `interactions/TimelineConnector` | Horizontal roadmap with animated SVG connectors |
271| `InfiniteScrollTicker` | `interactions/InfiniteScrollTicker` | Vertical marquee with gradient masks |
272| `ProgressBar` | `interactions/ProgressBar` | Animated horizontal fill bar with label |
273| `RevealCaption` | `interactions/RevealCaption` | Hover caption overlay |
274| `Tooltip` | `interactions/Tooltip` | Position-aware tooltip |
275| `PulseIndicator` | `interactions/PulseIndicator` | Pulsing dot + expanding ring |
276| `Skeleton` | `interactions/Skeleton` | Loading placeholder |
277
278### Graphics Components (4)
279
280| Component | Import | Purpose |
281|-----------|--------|---------|
282| `WebGPUCanvas` | `graphics/WebGPUCanvas` | WebGPU shader host + CSS gradient fallback |
283| `ParticleField` | `graphics/ParticleField` | Floating particle animation |
284| `NetworkGraph` | `graphics/NetworkGraph` | Pulsing node-ring visualization |
285| `SVGRadarChart` | `graphics/SVGRadarChart` | Zero-dependency SVG radar chart with pathLength animation |
286
287### Utility Hooks (3)
288
289| Hook | Import | Purpose |
290|------|--------|---------|
291| `useAutoCycle` | `hooks/useAutoCycle` | Generic auto-advancing timer: `[currentItem, index, setIndex]`. Pauses while the tab is hidden; no-ops under reduced motion |
292| `useTypewriter` | `hooks/useTypewriter` | Character-by-character text reveal: `{ displayText, isComplete }`. Pauses while hidden; renders full text under reduced motion |
293| `useReducedMotion` | `hooks/useReducedMotion` | Live `prefers-reduced-motion` subscription — every animated component reads it |
294
295Every auto-cycling and entrance component honors `prefers-reduced-motion` (final state, no cycling) and interactive components (TabGroup, Accordion, Tooltip) carry ARIA roles and keyboard navigation. Global keyboard shortcuts ignore keypresses on interactive targets.
296
297---
298
299## Navigation
300
301- **Sidebar:** Auto-collapses after 3s, hover to expand, spring-animated (`stiffness: 400, damping: 30`)
302- **Keyboard:** ArrowRight/Space (next), ArrowLeft (prev), Home/End
303- **URL-based:** `/deck/1`, `/deck/2`, etc. via TanStack Router
304- **Preloading:** Adjacent slides (n-1, n+1, n+2) are preloaded for instant navigation
305
306---
307
308## Access Gate
309
310Set `config.auth.password` in `src/config.ts`:
311```typescript
312auth: { password: "your-password" } // Empty string = no gate
313```
314
315Supports `?pw=your-password` URL param for direct access. State persists via sessionStorage.
316
317This is an access gate, not security: it's client-side, so the password and all slide content ship in the JavaScript bundle to anyone who requests the URL. It keeps casual link-forwards from opening the deck — the right tool for "don't let this circulate ahead of the meeting." For a deck that genuinely cannot leak (unannounced financials, M&A), put real authorization in front of the assets — Cloudflare Access or Vercel deployment protection — and keep the gate for UX.
318
319---
320
321## Data-Driven Authoring
322
323For faster deck creation, provide a YAML spec file describing each slide's type, content, and mode:
324
325```bash
326node ~/.claude/skills/overwatch-slidedeck/scripts/init-deck-from-spec.mjs deck-spec.yaml ./my-deck
327```
328
329The script copies the scaffold, generates `config.ts`, and creates empty slide files. Fill in each slide using the spec + `references/slide-templates.md`.
330
331See `references/deck-schema.md` for the full YAML schema covering all 14 slide types.
332
333---
334
335## Custom Asset Generation
336
337Generate custom icons and graphics matching the Overwatch aesthetic:
338
339**Pipeline:** Nano Banana Pro → ImageMagick → Potrace → SVG Cleanup
340
341```bash
342# Generate an icon matching the dark/orange aesthetic
343nano-banana-pro "Minimalist neural network icon, flat design,
344 3 solid colors, orange #ff6e41 on dark #0c0c0e, geometric"
345
346# Vectorize
347magick output.png -posterize 4 -colors 4 processed.png
348potrace processed.pbm -s -o icon.svg
349
350# Optimize
351svgo icon.svg -o icon-optimized.svg
352```
353
354**Required tools:**
355- `nano-banana-pro` skill (Gemini 3 Pro image generation)
356- ImageMagick (`brew install imagemagick`)
357- Potrace (`brew install potrace`)
358- SVGO (`npm install -g svgo`)
359
360---
361
362## Reference Documentation
363
364| File | Purpose |
365|------|---------|
366| `references/design-system.md` | Color tokens, typography, dimensions, 3 slide modes |
367| `references/interactions.md` | Animation patterns, timing, 18 interaction components + the useAutoCycle/useTypewriter hooks |
368| `references/shaders.md` | WebGPU setup, WGSL syntax, custom shaders, fallback |
369| `references/slide-templates.md` | 14 slide type templates with component composition |
370| `references/advanced-patterns.md` | 6 domain-specific patterns (waterfall, carousel, strikethrough, dual-layer shader) |
371| `references/deck-schema.md` | YAML schema for data-driven deck authoring |
372
373---
374
375## Tips
376
377- **One idea per slide** — keep content density low; Overwatch decks are visual, not textual
378- **Slide modes matter** — dark for most content, white for analytical/text-heavy, orange for emphasis moments
379- **Import from `"motion/react"`** — not `"framer-motion"` (the package is `motion` v12+)
380- **Shader fallback** — always test with WebGPU disabled; the CSS gradient fallback must look intentional
381- **Test keyboard nav** — ArrowRight/Space/ArrowLeft/Home/End should work on every slide
382- **Preload wisely** — the scaffold preloads n-1, n+1, n+2; adjust in `routes/deck.$slide.tsx` if needed
383- **Deploy early** — test on Cloudflare/Vercel before the presentation; local dev can mask font loading issues
384
385## Related Skills
386
387- **aldea-slidedeck** — static single-file HTML decks (Blueprint Mode). If the deck ships as a file rather than a URL, use aldea.
388- **conductor-motion** — self-contained behavioral animation demos (typewriter, progress, streaming text). Borrow its timing constants for in-slide motion; don't rebuild its patterns as React components.
389- **component-gallery** — pattern research for slide content (real product UI, tool-call displays, dashboards). Query it before inventing an interface mock.
390- **minoan-frontend-design** — creative direction and typography once the deck structure is set.