Skip to content

Instantly share code, notes, and snippets.

@LadyNaggaga
Created February 9, 2026 19:52
Show Gist options
  • Select an option

  • Save LadyNaggaga/e30091de2bda6f317e6f62ec605d239c to your computer and use it in GitHub Desktop.

Select an option

Save LadyNaggaga/e30091de2bda6f317e6f62ec605d239c to your computer and use it in GitHub Desktop.

Claude Skill: Pitch Deck Generator

Use this skill to generate a full interactive pitch deck from a markdown file or document. The user provides their presentation content in markdown, and this skill generates a complete Next.js application with slide components, navigation, speaker notes, search, exports (PDF + PPTX), light/dark theme toggle, an executive one-pager, and an executive email.


How to Use

Prerequisites

  • Node.js 18+
  • GitHub CLI (gh) installed and authenticated

Quick Start with GitHub CLI

# 1. Create a new Next.js project with shadcn/ui
npx create-next-app@latest my-pitch-deck --typescript --tailwind --eslint --app --src-dir=false
cd my-pitch-deck

# 2. Initialize shadcn/ui
npx shadcn@latest init

# 3. Place your markdown content file in the root
#    (see "Input Format" below for structure)

# 4. Use Claude to generate the deck
#    Pass this skill file + your markdown to Claude:
claude --skill ./CLAUDE-PITCH-DECK-SKILL.md "Generate a pitch deck from ./my-presentation.md"

# 5. Install additional dependencies
npm install html2canvas jspdf pptxgenjs

# 6. Run the dev server
npm run dev

# 7. Push to GitHub
gh repo create my-pitch-deck --public --push --source=.

Input Format

The user provides a markdown file structured with ## Slide N -- Title headings. Each slide should contain:

# [Project Name] Pitch Deck

## Slide 1 -- Title Slide
**[Project Name]**
Tagline or subtitle here
- Team: Person A, Person B
- Contact: email@company.com

## Slide 2 -- The Problem
**[Section Label]**
> Key talking point or thesis statement

- Bullet point 1
- Bullet point 2
- Bullet point 3

## Slide 3 -- The Solution
...continue for each slide...

Supported Slide Types

Parse the markdown and map each slide to one of these layout patterns:

Slide Type When to Use Layout Pattern
Title Opening slide Centered, large title + subtitle, decorative grid background, accent glow
Overview Executive summary Left-aligned stats/metrics + right-aligned key points
Stat Cards Market data, metrics Grid of cards with large numbers and labels
Problem Pain points Cards with colored labels + descriptions
Timeline How it works, process Numbered vertical timeline with connector lines
Architecture Technical layers Stacked horizontal bars showing layers
Comparison Table Competitive landscape Table with check/partial/x indicators + legend
Grid Cards Features, differentiators 2-3 column grid of bordered cards
Partners/Logos Traction, customers Status badges (Active/Proposed) + logo grid
Personas Target audience Table with role, description, success metrics
Roadmap Timeline milestones Horizontal cards with phase labels
Closing CTA / Ask Centered title + links to one-pager and email

Architecture

File Structure

app/
  layout.tsx              # Root layout with fonts (Inter + Space Grotesk)
  page.tsx                # Renders <DeckNavigator />
  globals.css             # Design tokens (dark + light themes)
  one-pager/
    page.tsx              # Executive one-pager (print-friendly)
  executive-email/
    page.tsx              # Copyable executive email

components/
  deck/
    slide-layout.tsx      # SlideLayout, SlideLabel, SlideTitle, SlideSubtitle
    deck-navigator.tsx    # Main deck shell: navigation, keyboard, exports, search
    slide-search.tsx      # Cmd+K search overlay with keyword index
    speaker-notes.ts      # Record<number, string> of per-slide notes
    theme-toggle.tsx      # Light/dark toggle with localStorage persistence
    slide-01-title.tsx    # Each slide is its own component
    slide-02-*.tsx
    ...
  one-pager-download.tsx  # PDF download button for the one-pager

Core Components

1. slide-layout.tsx -- Shared Layout Primitives

import { cn } from "@/lib/utils"

interface SlideLayoutProps {
  children: React.ReactNode
  className?: string
}

export function SlideLayout({ children, className }: SlideLayoutProps) {
  return (
    <div className={cn(
      "relative flex h-screen w-full flex-col justify-center overflow-hidden px-10 py-8 md:px-20 lg:px-28",
      className
    )}>
      {children}
    </div>
  )
}

export function SlideLabel({ children }: { children: React.ReactNode }) {
  return (
    <span className="mb-4 inline-block text-xs font-semibold uppercase tracking-[0.2em] text-primary">
      {children}
    </span>
  )
}

export function SlideTitle({ children, className }: { children: React.ReactNode; className?: string }) {
  return (
    <h1 className={cn(
      "text-balance font-serif text-4xl font-bold leading-tight tracking-tight text-foreground md:text-5xl lg:text-6xl",
      className
    )}>
      {children}
    </h1>
  )
}

export function SlideSubtitle({ children }: { children: React.ReactNode }) {
  return (
    <p className="mt-4 max-w-2xl text-pretty text-lg leading-relaxed text-muted-foreground md:text-xl">
      {children}
    </p>
  )
}

2. deck-navigator.tsx -- Main Navigation Shell

The DeckNavigator component provides:

  • Keyboard navigation: Arrow keys, spacebar for next/prev
  • Cmd+K search: Fuzzy search across slide titles and keywords
  • PDF export: Uses html2canvas + jspdf to capture each slide
  • PPTX export: Uses html2canvas + pptxgenjs to generate PowerPoint
  • Theme toggle: Light/dark mode via CSS class toggle
  • Speaker notes: Hover tooltip in top-right corner per slide
  • Slide indicators: Dot navigation at bottom center
  • Slide counter: "X / Y" display

Key implementation patterns:

"use client"

const slides = [Slide01, Slide02, ...] // Import all slide components

export function DeckNavigator() {
  const [current, setCurrent] = useState(0)
  const CurrentSlide = slides[current]

  return (
    <div className="relative h-screen w-full overflow-hidden bg-background">
      <div ref={slideRef}><CurrentSlide /></div>
      {/* Bottom nav bar with prev/next/search/export/theme */}
    </div>
  )
}

3. slide-search.tsx -- Search Index

Each slide gets an entry with keywords for fuzzy matching:

const slideIndex = [
  {
    index: 0,
    title: "Project Title",
    label: "Title",
    keywords: ["project name", "tagline", "team members"],
  },
  // ...one entry per slide
]

4. speaker-notes.ts -- Speaker Notes

export const speakerNotes: Record<number, string> = {
  0: "Open with confidence. Set the frame...",
  1: "Executive summary slide. Key points are...",
  // ...one entry per slide
}

5. theme-toggle.tsx -- Light/Dark Theme

Uses CSS class toggle on <html> element. Dark is default. Persists to localStorage.


Design System

Color Tokens (globals.css)

Dark theme (default -- :root):

--background: 220 15% 6%;      /* Near-black */
--foreground: 210 20% 95%;     /* Near-white */
--card: 220 15% 9%;            /* Slightly lighter than bg */
--primary: 207 90% 54%;        /* Blue */
--accent: 168 70% 45%;         /* Teal/green */
--secondary: 220 14% 14%;      /* Dark gray */
--muted-foreground: 215 15% 55%; /* Mid gray */
--border: 220 13% 18%;         /* Subtle border */

Light theme (.light class):

--background: 210 20% 98%;     /* Near-white */
--foreground: 220 15% 10%;     /* Near-black */
--card: 0 0% 100%;             /* Pure white */
--primary: 207 90% 44%;        /* Deeper blue */
--accent: 168 70% 35%;         /* Deeper teal */
--secondary: 215 20% 93%;      /* Light gray */
--muted-foreground: 215 15% 45%; /* Mid gray */
--border: 215 20% 88%;         /* Light border */

Typography

  • Headings font: Space Grotesk (mapped to font-serif in Tailwind)
  • Body font: Inter (mapped to font-sans in Tailwind)
  • Both loaded via next/font/google in layout.tsx

Tailwind Config Key Extensions

fontFamily: {
  sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
  serif: ['var(--font-space-grotesk)', 'system-ui', 'sans-serif'],
},

Slide Pattern Library

Pattern 1: Title Slide (Centered Hero)

<SlideLayout className="items-center justify-center text-center">
  {/* Decorative grid overlay */}
  <div className="pointer-events-none absolute inset-0 opacity-[0.03]">
    <svg width="100%" height="100%">
      <defs>
        <pattern id="grid" width="60" height="60" patternUnits="userSpaceOnUse">
          <path d="M 60 0 L 0 0 0 60" fill="none" stroke="currentColor" strokeWidth="1" />
        </pattern>
      </defs>
      <rect width="100%" height="100%" fill="url(#grid)" />
    </svg>
  </div>
  {/* Accent glow */}
  <div className="pointer-events-none absolute left-1/2 top-1/2 h-[600px] w-[600px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary/5 blur-[120px]" />
  <div className="relative z-10 flex flex-col items-center gap-6">
    <h1 className="text-balance font-serif text-5xl font-bold">
      Main Title <br /><span className="text-primary">Accent Line</span>
    </h1>
    <p className="text-lg text-muted-foreground">Subtitle / team info</p>
    {/* Protocol badges */}
    <div className="mt-8 flex gap-3">
      {["Tag1", "Tag2", "Tag3"].map(t => (
        <div key={t} className="rounded-full border border-border bg-secondary px-4 py-1.5 text-xs font-medium text-secondary-foreground">{t}</div>
      ))}
    </div>
  </div>
</SlideLayout>

Pattern 2: Problem / Challenge Cards

<SlideLayout>
  <SlideLabel>The Problem</SlideLabel>
  <SlideTitle>Title Here</SlideTitle>
  <div className="mt-8 flex flex-col gap-3">
    {challenges.map(item => (
      <div key={item.label} className="flex gap-3 rounded-lg border border-border bg-card p-4">
        <span className="shrink-0 text-xs font-bold text-accent">{item.label}</span>
        <span className="text-sm text-muted-foreground">{item.desc}</span>
      </div>
    ))}
  </div>
</SlideLayout>

Pattern 3: Timeline / Process Steps

<SlideLayout>
  <SlideLabel>How It Works</SlideLabel>
  <SlideTitle>Step-by-Step Process</SlideTitle>
  <div className="mt-12 flex flex-col gap-0">
    {steps.map((step, i) => (
      <div key={step.num} className="flex items-stretch gap-6">
        <div className="flex flex-col items-center">
          <div className="flex h-10 w-10 items-center justify-center rounded-full border border-primary/30 bg-primary/10 font-serif text-sm font-bold text-primary">
            {step.num}
          </div>
          {i < steps.length - 1 && <div className="w-px flex-1 bg-border" />}
        </div>
        <div className="pb-8">
          <h3 className="font-serif text-lg font-semibold text-foreground">{step.title}</h3>
          <p className="mt-1 max-w-md text-sm text-muted-foreground">{step.description}</p>
        </div>
      </div>
    ))}
  </div>
</SlideLayout>

Pattern 4: Comparison Table

<SlideLayout>
  <SlideLabel>Competitive Landscape</SlideLabel>
  <SlideTitle>Comparison Title</SlideTitle>
  <div className="mt-8 overflow-hidden rounded-xl border border-border">
    <table className="w-full text-left">
      <thead>
        <tr className="border-b border-border bg-secondary/50">
          <th className="p-4 font-serif text-sm font-semibold">Platform</th>
          {features.map(f => <th key={f} className="p-4 text-xs font-medium text-muted-foreground">{f}</th>)}
        </tr>
      </thead>
      <tbody>
        {competitors.map(c => (
          <tr key={c.name} className={`border-b border-border ${c.highlight ? "bg-primary/5" : ""}`}>
            <td className={`p-4 font-serif text-sm font-semibold ${c.highlight ? "text-primary" : "text-foreground"}`}>{c.name}</td>
            {c.scores.map((s, i) => (
              <td key={features[i]} className="p-4 text-center">
                {/* true = green check, "partial" = amber circle, false = gray dash */}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  </div>
  {/* Legend: Full / Partial / Not available */}
</SlideLayout>

Pattern 5: Grid Cards (Features / Differentiators)

<SlideLayout>
  <SlideLabel>Section Label</SlideLabel>
  <SlideTitle>Grid Title</SlideTitle>
  <div className="mt-10 grid grid-cols-2 gap-4 lg:grid-cols-3">
    {items.map(item => (
      <div key={item.title} className="rounded-xl border border-border bg-card p-6">
        <h3 className="font-serif text-base font-semibold text-foreground">{item.title}</h3>
        <p className="mt-2 text-sm text-muted-foreground">{item.desc}</p>
      </div>
    ))}
  </div>
</SlideLayout>

Pattern 6: Traction / Partners

<SlideLayout>
  <SlideLabel>Traction</SlideLabel>
  <SlideTitle>Design Partners</SlideTitle>
  <div className="mt-10 flex gap-12">
    <div className="flex-1">
      <h3 className="text-xs font-semibold uppercase tracking-[0.15em] text-muted-foreground">Internal</h3>
      <div className="mt-4 flex flex-col gap-2">
        {partners.map(p => (
          <div key={p.name} className="flex items-center justify-between rounded-lg border border-border bg-secondary/30 px-5 py-3.5">
            <span className="font-serif text-sm font-semibold">{p.name}</span>
            <span className={`rounded-full px-3 py-0.5 text-[10px] font-semibold uppercase ${
              p.status === "Active" ? "bg-accent/15 text-accent" : "bg-muted-foreground/10 text-muted-foreground"
            }`}>{p.status}</span>
          </div>
        ))}
      </div>
    </div>
  </div>
</SlideLayout>

Pattern 7: Architecture Layers

<SlideLayout>
  <SlideLabel>Architecture</SlideLabel>
  <SlideTitle>Technical Architecture</SlideTitle>
  <div className="mt-10 flex flex-col gap-2">
    {layers.map(layer => (
      <div key={layer.name} className="flex items-center gap-3 rounded-lg border border-border bg-card px-6 py-4">
        <span className="w-48 shrink-0 font-serif text-sm font-bold">{layer.name}</span>
        <span className="text-sm text-muted-foreground">{layer.items}</span>
      </div>
    ))}
  </div>
</SlideLayout>

Pattern 8: Personas Table

<SlideLayout>
  <SlideLabel>Target Audience</SlideLabel>
  <SlideTitle>Who We Are Building For</SlideTitle>
  <div className="mt-8 overflow-hidden rounded-xl border border-border">
    <table className="w-full text-left">
      <thead>
        <tr className="border-b border-border bg-secondary/50">
          <th className="p-4 text-xs font-semibold">Role</th>
          <th className="p-4 text-xs font-medium text-muted-foreground">Description</th>
          <th className="p-4 text-xs font-medium text-muted-foreground">Success Metrics</th>
        </tr>
      </thead>
      <tbody>
        {personas.map(p => (
          <tr key={p.role} className="border-b border-border last:border-b-0">
            <td className="p-4"><span className="font-serif text-sm font-bold text-foreground">{p.role}</span><br /><span className="text-xs text-muted-foreground">{p.subtitle}</span></td>
            <td className="p-4 text-xs text-muted-foreground">{p.desc}</td>
            <td className="p-4 text-xs text-muted-foreground">{p.metrics}</td>
          </tr>
        ))}
      </tbody>
    </table>
  </div>
</SlideLayout>

Generation Steps

When a user provides a markdown file, follow these steps:

Step 1: Parse the Markdown

  • Extract each ## Slide N -- Title block
  • Identify the slide type from keywords (problem, solution, architecture, competitive, roadmap, etc.)
  • Extract bullet points, stats, and structured data

Step 2: Generate Foundation Files

  1. app/globals.css -- Copy the dark + light theme tokens above
  2. app/layout.tsx -- Set up Inter + Space Grotesk fonts, metadata from the project name
  3. tailwind.config.ts -- Add the font family and color token mappings
  4. app/page.tsx -- Simply renders <DeckNavigator />

Step 3: Generate Slide Components

For each slide in the markdown:

  1. Create components/deck/slide-{NN}-{slug}.tsx
  2. Map the content to the appropriate pattern from the Pattern Library
  3. Export a named function component (e.g., Slide01Title)

Step 4: Generate Infrastructure Components

  1. slide-layout.tsx -- The four layout primitives
  2. deck-navigator.tsx -- Import all slides, wire up navigation, keyboard, exports, search, notes, theme
  3. slide-search.tsx -- Generate keyword index from slide content
  4. speaker-notes.ts -- Generate 2-3 sentence speaking notes per slide from the markdown content
  5. theme-toggle.tsx -- Light/dark toggle component

Step 5: Generate Companion Pages

  1. app/one-pager/page.tsx -- Two-column executive summary pulling key content from all slides
  2. app/executive-email/page.tsx -- Copyable email summarizing the deck narrative

Step 6: Install Dependencies

npm install html2canvas jspdf pptxgenjs

Customization Points

Users can customize these aspects:

Aspect How
Brand color Change --primary HSL values in globals.css
Accent color Change --accent HSL values in globals.css
Fonts Swap Inter/Space Grotesk in layout.tsx and tailwind.config.ts
Slide count Add/remove slide components and update the slides array in deck-navigator.tsx
Speaker notes Edit speaker-notes.ts
Search keywords Edit slideIndex array in slide-search.tsx
Export filename Change the filename string in generatePDF / generatePPTX functions

Dependencies

{
  "html2canvas": "^1.4.1",
  "jspdf": "^4.1.0",
  "pptxgenjs": "^4.0.1",
  "next": "^16",
  "react": "^19",
  "tailwindcss": "^3.4",
  "tailwindcss-animate": "^1.0.7"
}

Example Prompt for Claude

I have a markdown file with my presentation content. Please generate a full
interactive pitch deck following the CLAUDE-PITCH-DECK-SKILL.md patterns.

Here is my content:
[paste markdown or attach file]

Generate:
1. All slide components mapped to the right patterns
2. The deck navigator with keyboard nav, search, PDF/PPTX export
3. Speaker notes for each slide
4. An executive one-pager
5. An executive email
6. Light/dark theme support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment