Instagram Carousel Generator
Generates fully self-contained, swipeable HTML carousels where every slide is designed to be exported as an individual 1080×1350px PNG for Instagram.
Step 1: Collect Brand Details
Before generating, ask the user for the following (if not already provided):
- Brand name — displayed on first and last slides
- Instagram handle — shown in the IG frame header
- Primary brand color — hex code, or describe and Claude picks one
- Logo — SVG path, brand initial, or skip
- Font preference — see typography table below, or specific Google Fonts
- Tone — professional, casual, playful, bold, minimal, etc.
- Images — profile photo, screenshots, product images, etc.
- Idioma dos slides — default: Português (BR) unless specified otherwise
- Carousel format — standard (7 slides) or alternate sequence (see sequences section)
If the user provides a website URL or brand assets, derive colors and style from those.
If the user says "make me a carousel about X" without brand details, ask before generating. Don't assume defaults.
Handling User-Provided Images
This section applies from the very first HTML generation — not only during export.
When the user provides an image file path (e.g., /home/user/gestante.png, /mnt/user-data/uploads/foto.jpg):
⚠️ Critical Rules
- NEVER use relative paths (
gestante.png) — they break in every browser context except the exact folder the HTML lives in. - NEVER use
background: url(filepath)— leads to 1.5MB+ base64 inline strings that crash the browser parser. - ALWAYS embed as base64
data:URI — works in preview, export, and any environment. - ALWAYS generate the HTML via Python (
Path.write_text()) — shell heredocs interpolate$and backticks, corrupting base64 strings.
Step-by-step: embed an image
Image as slide background (most common use)
For dark slides, use rgba(0,0,0,0.45) as the overlay instead.
Common image mistakes to avoid
Step 2: Derive the Full Color System
From the user's single primary brand color, generate the full 6-token palette:
Rules for deriving colors:
- LIGHT_BG: tinted off-white complementing the primary (warm → warm cream, cool → cool gray-white)
- DARK_BG: near-black with subtle brand tint (warm → #1A1918, cool → #0F172A)
- LIGHT_BORDER: always ~1 shade darker than LIGHT_BG
- Brand gradient:
linear-gradient(165deg, BRAND_DARK 0%, BRAND_PRIMARY 50%, BRAND_LIGHT 100%)
Step 3: Set Up Typography
Based on the user's font preference, pick a heading font and body font from Google Fonts.
Font size scale (fixed across all brands):
- Headings: 28–34px, weight 600, letter-spacing -0.3 to -0.5px, line-height 1.1–1.15
- Body: 14px, weight 400, line-height 1.5–1.55
- Tags/labels: 10px, weight 600, letter-spacing 2px, uppercase
- Step numbers: heading font, 26px, weight 300
- Small text: 11–12px
Apply via CSS classes .serif (heading font) and .sans (body font) throughout all slides.
Slide 1 — Hook Rules
The first slide must stop the scroll in under 1 second. Prioritize these formats:
Rules:
- Never start with the brand name as headline
- Visual proof on Slide 1 whenever possible (screenshot, result, real number)
- Hook must promise value that the following slides deliver
Slide Sequences
Standard (7 slides — default)
Listicle (5–10 slides)
Use for: "X ferramentas", "X erros", "X dicas"
Tutorial (7 slides)
Comparação (5 slides)
General rules for all sequences:
- Start with a hook — first slide must stop the scroll
- End CTA on brand gradient — no swipe arrow, progress bar at 100%
- Alternate light and dark backgrounds for visual rhythm
- Adapt sequence to topic — not every carousel needs all slides
Slide Architecture
Format
- Aspect ratio: 4:5 (Instagram carousel standard)
- Each slide is self-contained — all UI elements baked into the image
- Alternate LIGHT_BG and DARK_BG backgrounds for visual rhythm
Required Elements on Every Slide
1. Progress Bar (bottom of every slide)
Shows position in the carousel. Fills as user swipes.
- Position: absolute bottom, full width, 28px horizontal padding, 20px bottom padding
- Track: 3px height, rounded corners
- Fill width:
((slideIndex + 1) / totalSlides) * 100% - Light slides:
rgba(0,0,0,0.08)track,BRAND_PRIMARYfill,rgba(0,0,0,0.3)counter - Dark slides:
rgba(255,255,255,0.12)track,#ffffill,rgba(255,255,255,0.4)counter - Counter label beside the bar: "1/7" format, 11px, weight 500
⚠️ Important: Always replace BRAND_PRIMARY with the actual hex value before rendering. Never leave it as a variable name in the HTML output.
2. Swipe Arrow (right edge — every slide EXCEPT the last)
Subtle chevron guiding the user to keep swiping. Removed on the last slide.
- Position: absolute right, full height, 48px wide
- Background: gradient fade transparent → subtle tint
- Chevron: 24×24 SVG, rounded strokes
- Light slides:
rgba(0,0,0,0.06)bg,rgba(0,0,0,0.25)stroke - Dark slides:
rgba(255,255,255,0.08)bg,rgba(255,255,255,0.35)stroke
Reusable Components
Strikethrough pills
Tag pills
Prompt / quote box
Feature list
Numbered steps
Color swatches
CTA button (final slide only)
Tag / Category Label
- Light slides:
BRAND_PRIMARY - Dark slides:
BRAND_LIGHT - Brand gradient slides:
rgba(255,255,255,0.6)
Logo Lockup (first and last slides)
- If logo icon: 40px circle (BRAND_PRIMARY bg) + icon centered + brand name beside
- If initials: 40px circle with first letter in white
- Brand name: 13px, weight 600, letter-spacing 0.5px
Layout Rules
- Content padding:
0 36pxstandard - Bottom-aligned slides with progress bar:
0 36px 52pxto clear the bar - Hero/CTA slides:
justify-content: center - Content-heavy slides:
justify-content: flex-end - Content must never overlap the progress bar — use
padding-bottom: 52px
Instagram Frame (Preview Wrapper)
When displaying in chat, wrap in an Instagram-style frame:
- Header: Avatar (BRAND_PRIMARY circle + logo) + handle + subtitle
- Viewport: 4:5 aspect ratio, swipeable/draggable track with all slides
- Dots: Small dot indicators below the viewport
- Actions: Heart, comment, share, bookmark SVG icons
- Caption: Handle + short description + "2 HOURS AGO" timestamp
Include pointer-based swipe/drag interaction for preview. Slides are still standalone export-ready images.
Important: .ig-frame must be exactly 420px wide. The carousel viewport is 420×525px. Do NOT change this width — export depends on it.
Review Flow
Always follow this flow. Never skip to export without approval.
- Generate the HTML preview first — never jump directly to export
- Show the preview and ask: "Quais slides precisam de ajuste antes de exportar?"
- Fix only the mentioned slides — never regenerate the entire carousel unless the direction fundamentally changes
- Only proceed to export when the user explicitly confirms approval (e.g., "pode exportar", "aprovado", "ok")
Exporting Slides as Instagram-Ready PNGs
After the user approves the carousel preview, export each slide as an individual 1080×1350px PNG.
Critical Export Rules
-
Use Python for HTML generation — never use shell scripts with variable interpolation. Always use
Path.write_text()oropen().write(). -
Embed images as base64 — all user-uploaded images must be base64-encoded as
data:image/jpeg;base64,...URIs. Check actual file format with thefilecommand — a.pngextension may contain a JPEG. -
Keep the 420px layout width — use Playwright's
device_scale_factorto scale up to 1080px output WITHOUT changing the layout viewport.
Install Playwright (only if needed)
Before running the export script, check and install only if missing:
Export Script
Why This Works
device_scale_factor=2.5714renders at high DPI — a 420px element becomes 1080px in the output. Layout stays at 420px.clipcaptures only the carousel viewport, not browser chrome.wait_for_timeout(3000)gives Google Fonts time to load.track.style.transition = 'none'disables swipe animation so slides snap instantly.
Common Export Mistakes to Avoid
Design Principles
- Every slide is export-ready — arrow and progress bar are part of the slide image
- Light/dark alternation — creates visual rhythm across swipes
- Heading + body font pairing — display font for impact, body for readability
- Brand-derived palette — all colors stem from one primary, keeping everything cohesive
- Progressive disclosure — progress bar fills and arrow guides forward
- Last slide is special — no arrow, full progress bar, clear CTA
- Consistent components — same tag style, list style, spacing across all slides
- Content padding clears UI — body text never overlaps progress bar or arrow
- Hook-first copy — Slide 1 exists to stop the scroll, not to introduce the brand
- Iterate fast — show preview, fix specific slides, don't rebuild from scratch
