mit einem Klick
carousel-design
// Render Instagram carousel slides as PNGs from structured slide data using your branded template.
// Render Instagram carousel slides as PNGs from structured slide data using your branded template.
Generate structured carousel slide content in your voice for Instagram carousels.
Generate 5-10 Instagram carousel ideas tailored to your niche and brand.
Generate complete Instagram carousels — from topic to AirDrop-ready PNGs. Includes content writing, slide rendering, and delivery.
| name | carousel-design |
| description | Render Instagram carousel slides as PNGs from structured slide data using your branded template. |
Render Instagram carousel slides as PNGs from structured slide data using your branded Puppeteer template.
Read config from ~/.claude/skills/carousel/config.json. If it doesn't exist, tell the user:
You haven't set up your carousel template yet. Run /carousel first — it takes about 10 minutes and only happens once.
Then stop.
Also verify:
~/.claude/skills/carousel/slide-template.html exists (the approved HTML template)~/.claude/skills/carousel/node_modules/puppeteer exists (Puppeteer installed)If template missing: "Your slide template is missing. Run /carousel to rebuild it."
If Puppeteer missing: Run cd ~/.claude/skills/carousel && npm install puppeteer
Accepts a JSON array of slide objects (from /carousel-copy). Each slide has:
name — filename (e.g. "slide1-hook")slideNum — e.g. "01 / 07" (empty string for CTA slide)stepLabel — e.g. "STEP 01", "THE METHOD"headline — HTML string with <br> for line breaks and <span class="accent"> for accent-colored textsubline — all caps description textactivateLabel — text above terminal (empty string to hide)terminal — HTML for terminal body using .t-line, .prompt, .cmd, .check, .arrow classescharacter — character name from config (e.g. "pointing") or empty if noneisPill (optional, boolean) — render stepLabel as a pill badgeisHook (optional, boolean) — add corner accents to the slideisCTA (optional, boolean) — centered layout with keyword box, no terminal/characterheadlineSize (optional) — override headline font sizectaKeyword (optional) — the keyword for CTA slide boxIf no JSON input is provided, ask the user for it or check if /carousel-copy output exists.
Read all values from config. The template was built and approved during setup:
config.slideSize (default 1080x1440, 4:5 Instagram carousel)config.colors.bg with optional pattern overlayconfig.fonts.headline and config.fonts.mono (loaded via Google Fonts)config.colors — text, accent, bgconfig.handleconfig.profilePic (or handle text if none)config.characters array — map character name to file pathThe main content area uses display: flex; flex-direction: column; justify-content: space-evenly to distribute headline/subline and terminal sections with EQUAL spacing. This ensures short and long headlines both look properly spaced. NEVER use absolute positioning for the subline or terminal — they must flow inside the flex container.
isHook: true, isPill: true): Corner accents in accent color, pill badge for category, profile pic + arrow at bottomisCTA: true): Everything centered, keyword box with accent border, "FOLLOW [HANDLE]", no arrow, no terminal, no character~/.claude/skills/carousel/slide-template.htmlcharacter name to actual file path from config
c. Apply slide-type-specific modifications (hook corners, CTA centered layout, etc.)
d. Handle headlineSize overrides
e. Hide activate label if empty~/.claude/skills/carousel/output/Generate a Node.js script similar to this pattern:
const puppeteer = require('puppeteer');
const path = require('path');
const fs = require('fs');
const template = fs.readFileSync(path.resolve(__dirname, 'slide-template.html'), 'utf8');
const config = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'config.json'), 'utf8'));
const slides = [/* slide data injected here */];
(async () => {
const browser = await puppeteer.launch({ headless: true });
const outputDir = path.resolve(__dirname, 'output');
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
for (const slide of slides) {
let html = template
.replace('{{SLIDE_NUM}}', slide.slideNum)
.replace('{{STEP_LABEL}}', slide.stepLabel)
.replace('{{HEADLINE}}', slide.headline)
.replace('{{SUBLINE}}', slide.subline)
.replace('{{ACTIVATE_LABEL}}', slide.activateLabel)
.replace('{{TERMINAL}}', slide.terminal);
// Map character name to path from config
if (slide.character && config.characters) {
const charConfig = config.characters.find(c => c.name === slide.character);
if (charConfig) {
html = html.replace('{{CHARACTER}}', charConfig.path);
}
}
// Apply hook, pill, CTA, headlineSize modifications...
// (same logic as the template build from setup)
const page = await browser.newPage();
await page.setViewport({
width: config.slideSize?.width || 1080,
height: config.slideSize?.height || 1440
});
const htmlPath = path.resolve(outputDir, slide.name + '.html');
fs.writeFileSync(htmlPath, html);
await page.goto('file://' + htmlPath, { waitUntil: 'networkidle0', timeout: 15000 });
await page.screenshot({ path: path.resolve(outputDir, slide.name + '.png'), type: 'png' });
console.log('Done: ' + slide.name);
await page.close();
}
await browser.close();
console.log('All slides rendered!');
})();
The actual build script should include the full slide-type logic (hook corners, pill badges, CTA override) as established during the setup phase.
~/.claude/skills/carousel/assets/headlineSize if needed~/.claude/skills/carousel/Built by @tenfoldmarc. Follow for daily AI automation builds — real systems, not theory.