LinkedIn Carousel Creator

Create LinkedIn carousel posts as high-quality PNG images.

How to use it

  1. Hit Copy SKILL.md — or use the Claude Code line below to get every file.
  2. Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
    ChatGPT: make a Project and paste it into Instructions.
    Neither? Paste it at the top of a new chat — it works for that chat.
  3. Describe your job in plain words. The AI follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit gooseworks-ai/goose-skills/skills/design/capabilities/create-html-carousel#main ~/.claude/skills/create-html-carousel

For one project only, change the path to .claude/skills/create-html-carousel. This skill also uses STYLE_PRESETS.md, Node.js, screenshot-slides.js — copying SKILL.md alone won't be enough. See the folder on GitHub.

Not working?
  • Check which app you pasted it into — the steps above name the right one.
  • Some skills need the paid tier of Claude or ChatGPT.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Show the full text710 lines
create-html-carousel/SKILL.md710 lines18.2 KBpushed 96d agoRawView on GitHub

Deprecated: This skill is superseded by goose-graphics. See skills/composites/goose-graphics/ (install with npx goose-skills install goose-graphics). The carousel format is one of seven formats in the newer skill and supports 36 style presets plus image sourcing and PNG export. This skill is retained for one release cycle before removal.

LinkedIn Carousel Creator

Create stunning LinkedIn carousel posts as PNG images. This skill generates styled HTML slides optimized for square format (1080×1080px), then automatically screenshots each slide for direct upload to LinkedIn.

Core Philosophy

  1. LinkedIn-First Design — Square format (1080×1080px), optimized for mobile feed viewing
  2. Informational Content — Tips, workflows, lists, frameworks (not presentations)
  3. Consistent Styling — Reuse proven design systems from frontend-slides
  4. Automated Export — Generate HTML → Screenshot → PNG files ready for LinkedIn
  5. Viewport Perfect — Every slide must fit exactly in 1080×1080px without scrolling

LinkedIn Carousel Specs

Format: Square (1080×1080px)

  • Aspect ratio: 1:1
  • File format: PNG (recommended) or JPG
  • File size: Under 10MB per image
  • Max slides: 10 images per carousel
  • Ideal slide count: 5-8 slides (best engagement)

Content Structure:

  1. Cover slide — Hook + title + your brand
  2. Content slides — One key point per slide (3-6 slides)
  3. Closing slide — CTA / summary / follow prompt

When to Use This Skill

Use for LinkedIn carousel posts like:

  • "5 AI GTM workflows you should be using"
  • "How to build X: A step-by-step guide"
  • "7 mistakes founders make with Y"
  • "The complete framework for Z"
  • "Before & After: How we 10x'd our metrics"

NOT for:

  • Long-form presentations (use frontend-slides)
  • Video content
  • Single-image posts

Workflow Overview

1. Content Input → User provides topic/outline
2. Style Selection → Choose visual style (or preview options)
3. HTML Generation → Create 1080×1080px HTML slides
4. Screenshot → Auto-capture each slide as PNG
5. Delivery → Folder of PNG files ready for LinkedIn upload

Phase 1: Content Discovery

Step 1.1: Get Topic & Structure

Ask the user:

Question 1: What's the topic?

  • Header: "Topic"
  • Question: "What's the main topic of this carousel?"
  • (Free text input)

Question 2: Content Type

  • Header: "Format"
  • Question: "What type of post is this?"
  • Options:
    • "Numbered list" — "5 ways to...", "7 mistakes...", "3 steps to..."
    • "How-to guide" — Step-by-step tutorial or process
    • "Framework" — Concept explanation with structure
    • "Before/After" — Transformation or case study
    • "Insights/Tips" — Collection of advice or learnings

Question 3: Slide Count

  • Header: "Length"
  • Question: "How many slides?"
  • Options:
    • "Short (5-6)" — Quick, punchy (best for mobile scrolling)
    • "Medium (7-8)" — Standard carousel length
    • "Long (9-10)" — Maximum LinkedIn allows

Question 4: Branding Handle

  • Header: "Brand"
  • Question: "What handle or name should appear on each slide?"
  • (Free text input — e.g., "@yourhandle", "Acme Inc", or leave blank for none)

Question 5: Content Ready?

  • Header: "Content"
  • Question: "Do you have the content written?"
  • Options:
    • "Yes, I have all content" — Paste it in
    • "I have bullet points" — Need light formatting
    • "Just the topic" — Need help outlining

If user has content, ask them to share it.

Content Density Rules for LinkedIn

Each slide should be scannable in 2-3 seconds on mobile:

Slide Type Max Content
Cover Title (1 line) + subtitle (1 line) + branding
List item Number/icon + heading (2 lines max) + body (3 lines max)
Framework Diagram/visual + 2-4 labels
Quote/Stat 1 large stat or quote + context
CTA 1 action + visual element

If content exceeds limits: Break into multiple slides or simplify.


Phase 2: Style Selection

Users can choose styles two ways:

Option A: Direct Selection (Faster)

Show preset picker:

Question: Pick a Style

  • Header: "Style"
  • Question: "Which visual style works best for your content?"
  • Options:
    • "Bold Signal" — High-contrast card on dark, confident
    • "Dark Botanical" — Elegant dark with soft abstract shapes
    • "Notebook Tabs" — Editorial cream paper with colorful tabs
    • "Pastel Geometry" — Friendly pastels with decorative pills
    • "Neon Cyber" — Futuristic tech aesthetic
    • "Split Pastel" — Playful two-tone split design

(See STYLE_PRESETS.md for full details on each style)

Option B: Guided Discovery

If user isn't sure, ask:

Question: Audience & Tone

  • Header: "Vibe"
  • Question: "Who's your audience and what tone?"
  • Options:
    • "Professional/Corporate" → Recommend: Bold Signal, Dark Botanical
    • "Creative/Playful" → Recommend: Split Pastel, Pastel Geometry
    • "Technical/Dev-focused" → Recommend: Neon Cyber, Terminal Green
    • "Elegant/Premium" → Recommend: Dark Botanical, Paper & Ink

Then generate 2-3 preview slides and let user pick.


Phase 3: Generate HTML Carousel

File Structure

All carousel files (HTML source and PNG exports) are saved to the shared assets directory.

[carousel-name]/
├── index.html              # Full carousel (all slides)
├── slides/
│   ├── slide-01.html       # Individual slide pages
│   ├── slide-02.html
│   └── ...
└── exports/
    ├── slide-01.png        # Screenshots (generated in Phase 4)
    ├── slide-02.png
    └── ...

HTML Architecture for 1080×1080px

CRITICAL: LinkedIn carousel slides are SQUARE (1:1 ratio), not widescreen.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Slide 01</title>

    <!-- Fonts -->
    <link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=..." />

    <style>
      /* ===========================================
           LINKEDIN CAROUSEL: SQUARE FORMAT
           Fixed 1080×1080px for screenshot
           =========================================== */
      :root {
        /* Fixed size for LinkedIn */
        --slide-width: 1080px;
        --slide-height: 1080px;

        /* Colors (from chosen preset) */
        --bg-primary: #0a0f1c;
        --text-primary: #ffffff;
        --accent: #00ffcc;

        /* Typography - scaled for square format */
        --title-size: 72px;
        --subtitle-size: 36px;
        --body-size: 28px;
        --small-size: 20px;

        /* Spacing */
        --slide-padding: 80px;
        --content-gap: 40px;

        /* Animation */
        --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
      }

      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      html,
      body {
        width: var(--slide-width);
        height: var(--slide-height);
        overflow: hidden;
      }

      body {
        font-family: var(--font-body);
        background: var(--bg-primary);
        color: var(--text-primary);
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        padding: var(--slide-padding);
      }

      /* Content container */
      .slide-content {
        width: 100%;
        max-width: 100%;
        display: flex;
        flex-direction: column;
        gap: var(--content-gap);
      }

      /* Typography hierarchy */
      h1 {
        font-size: var(--title-size);
        font-weight: 800;
        line-height: 1.1;
        margin-bottom: 20px;
      }

      h2 {
        font-size: var(--subtitle-size);
        font-weight: 700;
        line-height: 1.2;
      }

      p,
      li {
        font-size: var(--body-size);
        line-height: 1.4;
      }

      /* List styling */
      ul {
        list-style: none;
      }

      li {
        padding-left: 40px;
        position: relative;
        margin-bottom: 20px;
      }

      li::before {
        content: "→";
        position: absolute;
        left: 0;
        color: var(--accent);
        font-weight: bold;
      }

      /* Number badge (for list items) */
      .number {
        font-size: 120px;
        font-weight: 900;
        color: var(--accent);
        opacity: 0.15;
        position: absolute;
        top: -40px;
        left: -20px;
        z-index: 0;
      }

      /* Branding footer */
      .brand {
        position: absolute;
        bottom: var(--slide-padding);
        right: var(--slide-padding);
        font-size: var(--small-size);
        opacity: 0.7;
      }

      /* ===========================================
           STYLE-SPECIFIC OVERRIDES
           Inject preset styles here
           =========================================== */
      /* ... preset-specific CSS ... */
    </style>
  </head>
  <body>
    <div class="slide-content">
      <!-- Slide content goes here -->
      <h1>Your Title Here</h1>
      <p>Your content here</p>
    </div>

    <div class="brand">@yourbrand</div>
  </body>
</html>

Content Slide Templates

Cover Slide:

<div class="slide-content">
  <h1>5 AI GTM Workflows<br />You Should Be Using</h1>
  <p>Scale your outbound without scaling your team</p>
</div>
<div class="brand">@yourhandle</div>

Numbered Item (e.g., Slide 2/6):

<div class="slide-content">
  <div class="number">01</div>
  <h2>Signal-Based Outbound</h2>
  <p>
    Monitor job postings, funding announcements, and tech stack changes to find
    companies actively solving your problem.
  </p>
</div>
<div class="brand">@yourhandle • 1/5</div>

Framework Slide:

<div class="slide-content">
  <h2>The GTM Engineering Stack</h2>
  <div class="framework-grid">
    <div class="box">Research</div>
    <div class="box">Personalization</div>
    <div class="box">Outreach</div>
    <div class="box">Tracking</div>
  </div>
</div>
<div class="brand">@yourhandle • 3/5</div>

CTA Slide:

<div class="slide-content">
  <h2>Want more like this?</h2>
  <p>Follow me for more tips and workflows.</p>
  <div class="cta">Hit that follow button →</div>
</div>
<div class="brand">@yourhandle</div>

Phase 4: Screenshot Generation

After generating HTML, automatically capture screenshots.

Using Playwright (Recommended)

Create a Node.js script to screenshot each slide:

// screenshot-slides.js
const { chromium } = require("playwright");
const path = require("path");
const fs = require("fs");

async function screenshotSlides(slidesDir, outputDir) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // Set viewport to LinkedIn carousel size
  await page.setViewportSize({ width: 1080, height: 1080 });

  // Ensure output directory exists
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // Find all HTML files in slides directory
  const slideFiles = fs
    .readdirSync(slidesDir)
    .filter((f) => f.endsWith(".html"))
    .sort();

  console.log(`Found ${slideFiles.length} slides to screenshot`);

  for (const slideFile of slideFiles) {
    const slidePath = path.join(slidesDir, slideFile);
    const outputName = slideFile.replace(".html", ".png");
    const outputPath = path.join(outputDir, outputName);

    console.log(`Capturing ${slideFile}...`);

    await page.goto(`file://${path.resolve(slidePath)}`);

    // Wait for fonts and animations
    await page.waitForTimeout(500);

    // Take screenshot
    await page.screenshot({
      path: outputPath,
      type: "png",
      fullPage: false,
    });

    console.log(`✓ Saved ${outputName}`);
  }

  await browser.close();
  console.log("\n✨ All slides captured!");
}

// Usage
const carouselName = process.argv[2];
if (!carouselName) {
  console.error("Usage: node screenshot-slides.js <carousel-name>");
  process.exit(1);
}

const slidesDir = path.join(__dirname, carouselName, "slides");
const outputDir = path.join(__dirname, carouselName, "exports");

screenshotSlides(slidesDir, outputDir);

Installation

The skill directory needs these dependencies:

{
  "name": "linkedin-carousel-screenshots",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "playwright": "^1.40.0"
  }
}

First time setup:

cd /path/to/skills/create-html-carousel
npm install

Running Screenshot Script

After generating HTML slides:

node screenshot-slides.js carousel-name

This will:

  1. Open each slide HTML in a headless browser
  2. Set viewport to 1080×1080px
  3. Wait for fonts/animations to load
  4. Capture PNG screenshot
  5. Save to [carousel-name]/exports/

Phase 5: Delivery

After screenshots are generated, present to user:

✨ Your LinkedIn carousel is ready!

📁 Location: /assets/carousel-name/

**Slides:**
- 6 HTML slides in slides/ folder
- 6 PNG images in exports/ folder (1080×1080px)

**Preview:**
Open index.html to see all slides with navigation.

**Upload to LinkedIn:**
1. Create new post on LinkedIn
2. Click "Add media"
3. Upload all PNGs from exports/ folder in order
4. Add your post copy
5. Publish!

**File sizes:**
- slide-01.png: 234 KB ✓
- slide-02.png: 198 KB ✓
- slide-03.png: 256 KB ✓
(All under 10MB limit)

Want to make any changes to the slides?

Style Adaptation for Square Format

All styles from frontend-slides work for carousels, but require these adjustments:

Typography Scaling

Square format has less horizontal space, so scale fonts:

Element Presentation (16:9) Carousel (1:1)
Title clamp(2rem, 6vw, 5rem) 72px (fixed)
Subtitle clamp(1.25rem, 3vw, 2.5rem) 36px (fixed)
Body clamp(0.875rem, 1.5vw, 1.125rem) 28px (fixed)
Small clamp(0.75rem, 1vw, 0.875rem) 20px (fixed)

Why fixed sizes? We're targeting a single export size (1080×1080px), not responsive web viewing.

Layout Adjustments

Vertical space is precious:

  • Reduce top/bottom padding (80px instead of 4rem)
  • Tighter line-height (1.2-1.4 instead of 1.5-1.6)
  • Fewer list items per slide (max 3-4)
  • Smaller decorative elements

Mobile-first mindset:

  • Most LinkedIn users view on phones
  • Text must be readable at thumbnail size
  • High contrast is critical
  • Bold, simple layouts beat intricate designs

Content Best Practices

Hook Formula (Cover Slide)

Strong hooks for LinkedIn carousels:

  • Number + Promise: "5 workflows that 10x'd our outbound"
  • Contrarian: "Stop doing X. Do this instead."
  • Before/After: "How we went from X to Y in 30 days"
  • Question: "Why are only 3% of founders doing this?"
  • Curiosity gap: "The GTM strategy nobody talks about"

Body Slides (Items 2-9)

Each slide should:

  1. One clear point — Don't cram multiple concepts
  2. Visual hierarchy — Large number/icon + heading + body
  3. Concrete, not abstract — "Use job postings to find intent" not "Leverage signals"
  4. Scannable — 2-3 second read time max

Closing Slide

Always include a CTA:

  • "Follow for more [topic]"
  • "Repost if this helped"
  • "Comment your biggest takeaway"
  • "DM me if you want the full playbook"

Avoid:

  • "Link in comments" (often gets buried)
  • "Check out my website" (feels salesy)
  • No CTA at all (wasted opportunity)

Troubleshooting

Fonts Not Loading in Screenshots

Symptom: Screenshots show default system fonts

Solution:

  1. Use web-safe fonts (Arial, Georgia) OR
  2. Add await page.waitForLoadState('networkidle') before screenshot
  3. Increase wait timeout: await page.waitForTimeout(1000)

Screenshots Are Blurry

Symptom: Text looks fuzzy or low-res

Solution:

  1. Set device scale factor in Playwright:
    await page.setViewportSize({
      width: 1080,
      height: 1080,
      deviceScaleFactor: 2, // Retina-quality
    });
    

Content Overflows the Slide

Symptom: Text or elements cut off in screenshot

Solution:

  1. Reduce font sizes
  2. Decrease padding
  3. Split into multiple slides
  4. Simplify content (fewer bullets, shorter text)

Colors Look Different in Export

Symptom: PNG colors don't match HTML preview

Solution:

  • Ensure browser color profile matches sRGB
  • Use hex colors, avoid CSS filters that may render differently
  • Test screenshot script before generating all slides

Preset Quick Reference

Preset Best For Vibe
Bold Signal Confident, high-impact Professional
Dark Botanical Elegant, premium Sophisticated
Notebook Tabs Editorial, organized Friendly-professional
Pastel Geometry Friendly, approachable Playful
Neon Cyber Tech, innovation Futuristic
Split Pastel Creative, fun Energetic

See STYLE_PRESETS.md for complete styling details.


Related Skills

  • frontend-slides — For full presentations (not carousels)
  • personalized-email — For outreach content to pair with LinkedIn posts
  • deep-web-research — For researching topics/stats for carousel content

Example Session Flow

  1. User: "Create a LinkedIn carousel about 5 AI GTM workflows"
  2. Skill asks: content type, slide count, have content ready?
  3. User provides bullet points of the 5 workflows
  4. Skill asks: style preference
  5. User picks "Bold Signal"
  6. Skill generates 7 HTML slides (cover + 5 workflows + CTA)
  7. Skill runs screenshot script automatically
  8. Skill delivers folder with HTML + PNG exports
  9. User uploads PNGs to LinkedIn and publishes

Total time: 5-10 minutes from idea to ready-to-publish carousel.

1---
2name: create-html-carousel
3description: Create LinkedIn carousel posts as high-quality PNG images. Design informational multi-slide posts like "5 AI GTM workflows" with consistent styling, then automatically screenshot each slide at LinkedIn's optimal 1080x1080px format.
4---
5 
6> **Deprecated:** This skill is superseded by `goose-graphics`. See `skills/composites/goose-graphics/` (install with `npx goose-skills install goose-graphics`). The carousel format is one of seven formats in the newer skill and supports 36 style presets plus image sourcing and PNG export. This skill is retained for one release cycle before removal.
7 
8# LinkedIn Carousel Creator
9 
10Create stunning LinkedIn carousel posts as PNG images. This skill generates styled HTML slides optimized for square format (1080×1080px), then automatically screenshots each slide for direct upload to LinkedIn.
11 
12## Core Philosophy
13 
141. **LinkedIn-First Design** — Square format (1080×1080px), optimized for mobile feed viewing
152. **Informational Content** — Tips, workflows, lists, frameworks (not presentations)
163. **Consistent Styling** — Reuse proven design systems from frontend-slides
174. **Automated Export** — Generate HTML → Screenshot → PNG files ready for LinkedIn
185. **Viewport Perfect** — Every slide must fit exactly in 1080×1080px without scrolling
19 
20---
21 
22## LinkedIn Carousel Specs
23 
24**Format:** Square (1080×1080px)
25 
26- **Aspect ratio:** 1:1
27- **File format:** PNG (recommended) or JPG
28- **File size:** Under 10MB per image
29- **Max slides:** 10 images per carousel
30- **Ideal slide count:** 5-8 slides (best engagement)
31 
32**Content Structure:**
33 
341. **Cover slide** — Hook + title + your brand
352. **Content slides** — One key point per slide (3-6 slides)
363. **Closing slide** — CTA / summary / follow prompt
37 
38---
39 
40## When to Use This Skill
41 
42Use for LinkedIn carousel posts like:
43 
44- "5 AI GTM workflows you should be using"
45- "How to build X: A step-by-step guide"
46- "7 mistakes founders make with Y"
47- "The complete framework for Z"
48- "Before & After: How we 10x'd our metrics"
49 
50**NOT for:**
51 
52- Long-form presentations (use frontend-slides)
53- Video content
54- Single-image posts
55 
56---
57 
58## Workflow Overview
59 
60```
611. Content Input → User provides topic/outline
622. Style Selection → Choose visual style (or preview options)
633. HTML Generation → Create 1080×1080px HTML slides
644. Screenshot → Auto-capture each slide as PNG
655. Delivery → Folder of PNG files ready for LinkedIn upload
66```
67 
68---
69 
70## Phase 1: Content Discovery
71 
72### Step 1.1: Get Topic & Structure
73 
74Ask the user:
75 
76**Question 1: What's the topic?**
77 
78- Header: "Topic"
79- Question: "What's the main topic of this carousel?"
80- (Free text input)
81 
82**Question 2: Content Type**
83 
84- Header: "Format"
85- Question: "What type of post is this?"
86- Options:
87 - "Numbered list" — "5 ways to...", "7 mistakes...", "3 steps to..."
88 - "How-to guide" — Step-by-step tutorial or process
89 - "Framework" — Concept explanation with structure
90 - "Before/After" — Transformation or case study
91 - "Insights/Tips" — Collection of advice or learnings
92 
93**Question 3: Slide Count**
94 
95- Header: "Length"
96- Question: "How many slides?"
97- Options:
98 - "Short (5-6)" — Quick, punchy (best for mobile scrolling)
99 - "Medium (7-8)" — Standard carousel length
100 - "Long (9-10)" — Maximum LinkedIn allows
101 
102**Question 4: Branding Handle**
103 
104- Header: "Brand"
105- Question: "What handle or name should appear on each slide?"
106- (Free text input — e.g., "@yourhandle", "Acme Inc", or leave blank for none)
107 
108**Question 5: Content Ready?**
109 
110- Header: "Content"
111- Question: "Do you have the content written?"
112- Options:
113 - "Yes, I have all content" — Paste it in
114 - "I have bullet points" — Need light formatting
115 - "Just the topic" — Need help outlining
116 
117If user has content, ask them to share it.
118 
119### Content Density Rules for LinkedIn
120 
121Each slide should be **scannable in 2-3 seconds** on mobile:
122 
123| Slide Type | Max Content |
124| ---------- | -------------------------------------------------------- |
125| Cover | Title (1 line) + subtitle (1 line) + branding |
126| List item | Number/icon + heading (2 lines max) + body (3 lines max) |
127| Framework | Diagram/visual + 2-4 labels |
128| Quote/Stat | 1 large stat or quote + context |
129| CTA | 1 action + visual element |
130 
131**If content exceeds limits:** Break into multiple slides or simplify.
132 
133---
134 
135## Phase 2: Style Selection
136 
137Users can choose styles two ways:
138 
139### Option A: Direct Selection (Faster)
140 
141Show preset picker:
142 
143**Question: Pick a Style**
144 
145- Header: "Style"
146- Question: "Which visual style works best for your content?"
147- Options:
148 - "Bold Signal" — High-contrast card on dark, confident
149 - "Dark Botanical" — Elegant dark with soft abstract shapes
150 - "Notebook Tabs" — Editorial cream paper with colorful tabs
151 - "Pastel Geometry" — Friendly pastels with decorative pills
152 - "Neon Cyber" — Futuristic tech aesthetic
153 - "Split Pastel" — Playful two-tone split design
154 
155(See STYLE_PRESETS.md for full details on each style)
156 
157### Option B: Guided Discovery
158 
159If user isn't sure, ask:
160 
161**Question: Audience & Tone**
162 
163- Header: "Vibe"
164- Question: "Who's your audience and what tone?"
165- Options:
166 - "Professional/Corporate" → Recommend: Bold Signal, Dark Botanical
167 - "Creative/Playful" → Recommend: Split Pastel, Pastel Geometry
168 - "Technical/Dev-focused" → Recommend: Neon Cyber, Terminal Green
169 - "Elegant/Premium" → Recommend: Dark Botanical, Paper & Ink
170 
171Then generate 2-3 preview slides and let user pick.
172 
173---
174 
175## Phase 3: Generate HTML Carousel
176 
177### File Structure
178 
179All carousel files (HTML source and PNG exports) are saved to the shared assets directory.
180 
181```
182[carousel-name]/
183├── index.html # Full carousel (all slides)
184├── slides/
185│ ├── slide-01.html # Individual slide pages
186│ ├── slide-02.html
187│ └── ...
188└── exports/
189 ├── slide-01.png # Screenshots (generated in Phase 4)
190 ├── slide-02.png
191 └── ...
192```
193 
194### HTML Architecture for 1080×1080px
195 
196**CRITICAL: LinkedIn carousel slides are SQUARE (1:1 ratio), not widescreen.**
197 
198```html
199<!DOCTYPE html>
200<html lang="en">
201 <head>
202 <meta charset="UTF-8" />
203 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
204 <title>Slide 01</title>
205 
206 <!-- Fonts -->
207 <link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=..." />
208 
209 <style>
210 /* ===========================================
211 LINKEDIN CAROUSEL: SQUARE FORMAT
212 Fixed 1080×1080px for screenshot
213 =========================================== */
214 :root {
215 /* Fixed size for LinkedIn */
216 --slide-width: 1080px;
217 --slide-height: 1080px;
218 
219 /* Colors (from chosen preset) */
220 --bg-primary: #0a0f1c;
221 --text-primary: #ffffff;
222 --accent: #00ffcc;
223 
224 /* Typography - scaled for square format */
225 --title-size: 72px;
226 --subtitle-size: 36px;
227 --body-size: 28px;
228 --small-size: 20px;
229 
230 /* Spacing */
231 --slide-padding: 80px;
232 --content-gap: 40px;
233 
234 /* Animation */
235 --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
236 }
237 
238 * {
239 margin: 0;
240 padding: 0;
241 box-sizing: border-box;
242 }
243 
244 html,
245 body {
246 width: var(--slide-width);
247 height: var(--slide-height);
248 overflow: hidden;
249 }
250 
251 body {
252 font-family: var(--font-body);
253 background: var(--bg-primary);
254 color: var(--text-primary);
255 display: flex;
256 flex-direction: column;
257 justify-content: center;
258 align-items: center;
259 padding: var(--slide-padding);
260 }
261 
262 /* Content container */
263 .slide-content {
264 width: 100%;
265 max-width: 100%;
266 display: flex;
267 flex-direction: column;
268 gap: var(--content-gap);
269 }
270 
271 /* Typography hierarchy */
272 h1 {
273 font-size: var(--title-size);
274 font-weight: 800;
275 line-height: 1.1;
276 margin-bottom: 20px;
277 }
278 
279 h2 {
280 font-size: var(--subtitle-size);
281 font-weight: 700;
282 line-height: 1.2;
283 }
284 
285 p,
286 li {
287 font-size: var(--body-size);
288 line-height: 1.4;
289 }
290 
291 /* List styling */
292 ul {
293 list-style: none;
294 }
295 
296 li {
297 padding-left: 40px;
298 position: relative;
299 margin-bottom: 20px;
300 }
301 
302 li::before {
303 content: "→";
304 position: absolute;
305 left: 0;
306 color: var(--accent);
307 font-weight: bold;
308 }
309 
310 /* Number badge (for list items) */
311 .number {
312 font-size: 120px;
313 font-weight: 900;
314 color: var(--accent);
315 opacity: 0.15;
316 position: absolute;
317 top: -40px;
318 left: -20px;
319 z-index: 0;
320 }
321 
322 /* Branding footer */
323 .brand {
324 position: absolute;
325 bottom: var(--slide-padding);
326 right: var(--slide-padding);
327 font-size: var(--small-size);
328 opacity: 0.7;
329 }
330 
331 /* ===========================================
332 STYLE-SPECIFIC OVERRIDES
333 Inject preset styles here
334 =========================================== */
335 /* ... preset-specific CSS ... */
336 </style>
337 </head>
338 <body>
339 <div class="slide-content">
340 <!-- Slide content goes here -->
341 <h1>Your Title Here</h1>
342 <p>Your content here</p>
343 </div>
344 
345 <div class="brand">@yourbrand</div>
346 </body>
347</html>
348```
349 
350### Content Slide Templates
351 
352**Cover Slide:**
353 
354```html
355<div class="slide-content">
356 <h1>5 AI GTM Workflows<br />You Should Be Using</h1>
357 <p>Scale your outbound without scaling your team</p>
358</div>
359<div class="brand">@yourhandle</div>
360```
361 
362**Numbered Item (e.g., Slide 2/6):**
363 
364```html
365<div class="slide-content">
366 <div class="number">01</div>
367 <h2>Signal-Based Outbound</h2>
368 <p>
369 Monitor job postings, funding announcements, and tech stack changes to find
370 companies actively solving your problem.
371 </p>
372</div>
373<div class="brand">@yourhandle • 1/5</div>
374```
375 
376**Framework Slide:**
377 
378```html
379<div class="slide-content">
380 <h2>The GTM Engineering Stack</h2>
381 <div class="framework-grid">
382 <div class="box">Research</div>
383 <div class="box">Personalization</div>
384 <div class="box">Outreach</div>
385 <div class="box">Tracking</div>
386 </div>
387</div>
388<div class="brand">@yourhandle • 3/5</div>
389```
390 
391**CTA Slide:**
392 
393```html
394<div class="slide-content">
395 <h2>Want more like this?</h2>
396 <p>Follow me for more tips and workflows.</p>
397 <div class="cta">Hit that follow button →</div>
398</div>
399<div class="brand">@yourhandle</div>
400```
401 
402---
403 
404## Phase 4: Screenshot Generation
405 
406After generating HTML, automatically capture screenshots.
407 
408### Using Playwright (Recommended)
409 
410Create a Node.js script to screenshot each slide:
411 
412```javascript
413// screenshot-slides.js
414const { chromium } = require("playwright");
415const path = require("path");
416const fs = require("fs");
417 
418async function screenshotSlides(slidesDir, outputDir) {
419 const browser = await chromium.launch();
420 const page = await browser.newPage();
421 
422 // Set viewport to LinkedIn carousel size
423 await page.setViewportSize({ width: 1080, height: 1080 });
424 
425 // Ensure output directory exists
426 if (!fs.existsSync(outputDir)) {
427 fs.mkdirSync(outputDir, { recursive: true });
428 }
429 
430 // Find all HTML files in slides directory
431 const slideFiles = fs
432 .readdirSync(slidesDir)
433 .filter((f) => f.endsWith(".html"))
434 .sort();
435 
436 console.log(`Found ${slideFiles.length} slides to screenshot`);
437 
438 for (const slideFile of slideFiles) {
439 const slidePath = path.join(slidesDir, slideFile);
440 const outputName = slideFile.replace(".html", ".png");
441 const outputPath = path.join(outputDir, outputName);
442 
443 console.log(`Capturing ${slideFile}...`);
444 
445 await page.goto(`file://${path.resolve(slidePath)}`);
446 
447 // Wait for fonts and animations
448 await page.waitForTimeout(500);
449 
450 // Take screenshot
451 await page.screenshot({
452 path: outputPath,
453 type: "png",
454 fullPage: false,
455 });
456 
457 console.log(`✓ Saved ${outputName}`);
458 }
459 
460 await browser.close();
461 console.log("\n✨ All slides captured!");
462}
463 
464// Usage
465const carouselName = process.argv[2];
466if (!carouselName) {
467 console.error("Usage: node screenshot-slides.js <carousel-name>");
468 process.exit(1);
469}
470 
471const slidesDir = path.join(__dirname, carouselName, "slides");
472const outputDir = path.join(__dirname, carouselName, "exports");
473 
474screenshotSlides(slidesDir, outputDir);
475```
476 
477### Installation
478 
479The skill directory needs these dependencies:
480 
481```json
482{
483 "name": "linkedin-carousel-screenshots",
484 "version": "1.0.0",
485 "private": true,
486 "dependencies": {
487 "playwright": "^1.40.0"
488 }
489}
490```
491 
492First time setup:
493 
494```bash
495cd /path/to/skills/create-html-carousel
496npm install
497```
498 
499### Running Screenshot Script
500 
501After generating HTML slides:
502 
503```bash
504node screenshot-slides.js carousel-name
505```
506 
507This will:
508 
5091. Open each slide HTML in a headless browser
5102. Set viewport to 1080×1080px
5113. Wait for fonts/animations to load
5124. Capture PNG screenshot
5135. Save to `[carousel-name]/exports/`
514 
515---
516 
517## Phase 5: Delivery
518 
519After screenshots are generated, present to user:
520 
521```
522✨ Your LinkedIn carousel is ready!
523 
524📁 Location: /assets/carousel-name/
525 
526**Slides:**
527- 6 HTML slides in slides/ folder
528- 6 PNG images in exports/ folder (1080×1080px)
529 
530**Preview:**
531Open index.html to see all slides with navigation.
532 
533**Upload to LinkedIn:**
5341. Create new post on LinkedIn
5352. Click "Add media"
5363. Upload all PNGs from exports/ folder in order
5374. Add your post copy
5385. Publish!
539 
540**File sizes:**
541- slide-01.png: 234 KB ✓
542- slide-02.png: 198 KB ✓
543- slide-03.png: 256 KB ✓
544(All under 10MB limit)
545 
546Want to make any changes to the slides?
547```
548 
549---
550 
551## Style Adaptation for Square Format
552 
553All styles from frontend-slides work for carousels, but require these adjustments:
554 
555### Typography Scaling
556 
557Square format has less horizontal space, so scale fonts:
558 
559| Element | Presentation (16:9) | Carousel (1:1) |
560| -------- | -------------------------------- | -------------- |
561| Title | clamp(2rem, 6vw, 5rem) | 72px (fixed) |
562| Subtitle | clamp(1.25rem, 3vw, 2.5rem) | 36px (fixed) |
563| Body | clamp(0.875rem, 1.5vw, 1.125rem) | 28px (fixed) |
564| Small | clamp(0.75rem, 1vw, 0.875rem) | 20px (fixed) |
565 
566**Why fixed sizes?** We're targeting a single export size (1080×1080px), not responsive web viewing.
567 
568### Layout Adjustments
569 
570**Vertical space is precious:**
571 
572- Reduce top/bottom padding (80px instead of 4rem)
573- Tighter line-height (1.2-1.4 instead of 1.5-1.6)
574- Fewer list items per slide (max 3-4)
575- Smaller decorative elements
576 
577**Mobile-first mindset:**
578 
579- Most LinkedIn users view on phones
580- Text must be readable at thumbnail size
581- High contrast is critical
582- Bold, simple layouts beat intricate designs
583 
584---
585 
586## Content Best Practices
587 
588### Hook Formula (Cover Slide)
589 
590Strong hooks for LinkedIn carousels:
591 
592- Number + Promise: "5 workflows that 10x'd our outbound"
593- Contrarian: "Stop doing X. Do this instead."
594- Before/After: "How we went from X to Y in 30 days"
595- Question: "Why are only 3% of founders doing this?"
596- Curiosity gap: "The GTM strategy nobody talks about"
597 
598### Body Slides (Items 2-9)
599 
600Each slide should:
601 
6021. **One clear point** — Don't cram multiple concepts
6032. **Visual hierarchy** — Large number/icon + heading + body
6043. **Concrete, not abstract** — "Use job postings to find intent" not "Leverage signals"
6054. **Scannable** — 2-3 second read time max
606 
607### Closing Slide
608 
609Always include a CTA:
610 
611- "Follow for more [topic]"
612- "Repost if this helped"
613- "Comment your biggest takeaway"
614- "DM me if you want the full playbook"
615 
616Avoid:
617 
618- "Link in comments" (often gets buried)
619- "Check out my website" (feels salesy)
620- No CTA at all (wasted opportunity)
621 
622---
623 
624## Troubleshooting
625 
626### Fonts Not Loading in Screenshots
627 
628**Symptom:** Screenshots show default system fonts
629 
630**Solution:**
631 
6321. Use web-safe fonts (Arial, Georgia) OR
6332. Add `await page.waitForLoadState('networkidle')` before screenshot
6343. Increase wait timeout: `await page.waitForTimeout(1000)`
635 
636### Screenshots Are Blurry
637 
638**Symptom:** Text looks fuzzy or low-res
639 
640**Solution:**
641 
6421. Set device scale factor in Playwright:
643 ```javascript
644 await page.setViewportSize({
645 width: 1080,
646 height: 1080,
647 deviceScaleFactor: 2, // Retina-quality
648 });
649 ```
650 
651### Content Overflows the Slide
652 
653**Symptom:** Text or elements cut off in screenshot
654 
655**Solution:**
656 
6571. Reduce font sizes
6582. Decrease padding
6593. Split into multiple slides
6604. Simplify content (fewer bullets, shorter text)
661 
662### Colors Look Different in Export
663 
664**Symptom:** PNG colors don't match HTML preview
665 
666**Solution:**
667 
668- Ensure browser color profile matches sRGB
669- Use hex colors, avoid CSS filters that may render differently
670- Test screenshot script before generating all slides
671 
672---
673 
674## Preset Quick Reference
675 
676| Preset | Best For | Vibe |
677| --------------- | ---------------------- | --------------------- |
678| Bold Signal | Confident, high-impact | Professional |
679| Dark Botanical | Elegant, premium | Sophisticated |
680| Notebook Tabs | Editorial, organized | Friendly-professional |
681| Pastel Geometry | Friendly, approachable | Playful |
682| Neon Cyber | Tech, innovation | Futuristic |
683| Split Pastel | Creative, fun | Energetic |
684 
685See STYLE_PRESETS.md for complete styling details.
686 
687---
688 
689## Related Skills
690 
691- **frontend-slides** — For full presentations (not carousels)
692- **personalized-email** — For outreach content to pair with LinkedIn posts
693- **deep-web-research** — For researching topics/stats for carousel content
694 
695---
696 
697## Example Session Flow
698 
6991. User: "Create a LinkedIn carousel about 5 AI GTM workflows"
7002. Skill asks: content type, slide count, have content ready?
7013. User provides bullet points of the 5 workflows
7024. Skill asks: style preference
7035. User picks "Bold Signal"
7046. Skill generates 7 HTML slides (cover + 5 workflows + CTA)
7057. Skill runs screenshot script automatically
7068. Skill delivers folder with HTML + PNG exports
7079. User uploads PNGs to LinkedIn and publishes
708 
709Total time: 5-10 minutes from idea to ready-to-publish carousel.
710 

Discussion

Alternatives

Also in Platform posts
Instagram Caption WriterDraft an Instagram caption with the hook in the first 125 characters, a skimmable body, and one clear CTA, using a 2026 hook formula chosen by goal (saves, shares, comments, follows). Runs the humanizer pass and, on approval, publishes with the image or video you supply via the Publora media flow. Use to caption a single image or a Reel. Not for slide-by-slide carousels (use ig-carousel-planner) or auditing a draft (use ig-humanizer --mode audit).Creator · MITInstagram Carousel PlannerPlan an Instagram carousel slide by slide, up to 10 slides, with a hook slide that opens a loop, value slides that front-load the payoff, and a payoff slide that earns the save and follow. Picks a 2026 carousel formula (listicle, before/after, myth-buster, framework) by goal (saves, shares, follows), drafts each slide's text plus the caption, then publishes the images you supply via Publora. Use to structure a multi-slide carousel. Not for single-image captions (use ig-caption-writer).Creator · MITInstagram Content PlannerGenerate a weekly Instagram content plan from a theme, audience, and content pillars. Produces a per-day mix of Reels, carousels, single images, and stories, each with a hook formula, angle, posting time, and primary goal, plus a weekly saves-and-shares goal (the 2026 lever), a story cadence, and a balance check across saves, shares, comments, and follows. Use to plan a week instead of ad-hoc posting. Not for drafting one caption (use ig-caption-writer) or one carousel (use ig-carousel-planner).Creator · MITInstagram Hashtag StrategistBuild a sized Instagram hashtag set for a post using the 2026 reality that 3 to 5 well-chosen tags beat 30 random ones. Sizes each tag as niche (under 50k posts), mid (50k to 500k), or broad (500k+), then assembles a 2-3 niche, 1-2 mid, 0-1 broad mix that a small or mid account can actually rank in, matched to the post content and rotated to avoid spam patterns. Use to choose hashtags for a caption, carousel, or Reel. Not for writing the caption itself (use ig-caption-writer).Creator · MIT