Loopwind
Create videos and images with React, Tailwind and Shadcn classes.
Install / Use
/learn @tomtev/LoopwindREADME
loopwind
The Shadcn for design and marketing. A template-based CLI tool for generating images and videos using React + Tailwind CSS + Satori.
Features
- Template-based: Install templates and get full control over code.
- Fast Video Rendering: WASM-based MP4 encoding with optional FFmpeg for 2x speed.
- Tailwind CSS & shadcn/ui styling: Style templates with Tailwind & shadcn/ui utility classes (
text-foreground bg-primary/50) - Animate with classes: Tailwind-style animation classes for video templates. (enter-fade-in/0/500, exit-fade-out/2500/500, loop-ping/500)
- Built-in Helpers: QR codes, image embedding, video backgrounds, template composition
- Agent-friendly: File-based editing perfect for code agents
Quick Start
Installation
curl -fsSL https://loopwind.dev/install.sh | bash
This installs loopwind to ~/.loopwind/ and adds the loopwind command to your PATH. Requires Node.js 18+.
Install Agent Skill
Give your AI coding agent expertise in loopwind:
npx skills add https://github.com/tomtev/loopwind
Or install via OpenClaw:
npx clawhub@latest install loopwind
Install a Template
1. Officials
loopwind add image-template
loopwind add video-template
Templates are installed to: .loopwind/<template>/.
Benefits:
- Templates are local to your project (like npm packages)
- Different projects can use different template versions
- Version controlled with your project
- Easy to share within your team
- Works offline once installed
Render a Video
loopwind render my-video '{"title":"Hello World"}'
Videos are rendered to MP4 by default and saved to the current directory.
Example video template (.loopwind/my-video/template.tsx):
export const meta = {
name: 'my-video',
type: 'video',
size: { width: 1920, height: 1080 },
video: { fps: 30, duration: 3 },
props: { title: 'string' }
};
export default function Template({ tw, title }) {
return (
<div style={tw('flex items-center justify-center w-full h-full bg-black')}>
{/* Fade in from 0ms to 800ms */}
<h1 style={tw('text-8xl font-bold text-white ease-out enter-fade-in/0/800')}>
{title}
</h1>
</div>
);
}
Render an Image
loopwind render banner-hero \
--props '{"title":"Hello World","subtitle":"Welcome to loopwind"}'
By default, images are saved to the current directory.
You can specify a custom output path:
loopwind render banner-hero \
--props '{"title":"Hello World","subtitle":"Welcome to loopwind"}' \
--out custom/path/banner.png
Or use a props file:
# props.json
{
"title": "Hello World",
"subtitle": "Welcome to loopwind"
}
loopwind render banner-hero --props props.json --out banner.png
Output formats:
# PNG (default) - best for photos and complex gradients
loopwind render banner-hero --props props.json --format png -o banner.png
# SVG - scalable vector, smaller file size, perfect for template composition
loopwind render banner-hero --props props.json --format svg -o banner.svg
# WebP - modern format, smaller than PNG with similar quality
loopwind render banner-hero --props props.json --format webp -o banner.webp
# JPEG - smallest file size, good for photos
loopwind render banner-hero --props props.json --format jpg -o banner.jpg
SVG benefits with template composition:
- Smaller file size (typically 3-4x smaller than PNG)
- Infinitely scalable without quality loss
- Embedded templates remain as native SVG elements
- Can be edited in design tools (Figma, Illustrator)
List Installed Templates
loopwind list
Validate Templates
loopwind validate
loopwind validate banner-hero
shadcn/ui Design System
loopwind uses shadcn/ui's design system by default! All templates have access to semantic color tokens:
// Use shadcn colors in templates
<div style={tw('bg-card border rounded-xl p-16')}>
<h1 style={tw('text-foreground font-bold')}>Title</h1>
<p style={tw('text-muted-foreground')}>Subtitle</p>
</div>
Supported shadcn classes:
text-primary,bg-secondary,text-muted-foreground,bg-card,border,bg-destructive- Opacity modifiers:
bg-primary/50,text-muted-foreground/75,border/30 - Dark mode ready: Override colors in
loopwind.json
See SHADCN_INTEGRATION.md for complete documentation.
Configuration (loopwind.json)
Templates automatically use your project's design tokens defined in .loopwind/loopwind.json. This allows generated images to match your brand colors and design system.
Initialize Config
loopwind init
This creates a .loopwind/loopwind.json file in your project:
{
"colors": {
"primary": "#667eea",
"secondary": "#764ba2",
"background": "#ffffff",
"foreground": "#000000",
"muted": "#f3f4f6",
"accent": "#3b82f6",
"destructive": "#ef4444",
"border": "#e5e7eb"
},
"fonts": {
"sans": ["Inter", "system-ui", "sans-serif"],
"mono": ["JetBrains Mono", "monospace"]
},
"tokens": {
"borderRadius": {
"sm": "0.25rem",
"md": "0.5rem",
"lg": "1rem",
"xl": "1.5rem"
}
},
"defaults": {
"width": 1200,
"height": 630
}
}
Using Config in Templates
Templates receive the config via props:
export default function MyTemplate({ title, config }) {
const primaryColor = config?.colors?.primary || '#000';
return (
<div style={{ background: primaryColor }}>
{title}
</div>
);
}
This ensures your generated images use the same colors as your Tailwind/Shadcn setup!
Using Tailwind Classes
Templates can use Tailwind utility classes for styling! The tw() function is automatically provided:
export default function MyTemplate({ title, tw }) {
return (
<div style={tw('flex items-center justify-center w-full h-full bg-primary text-white')}>
<h1 style={tw('text-6xl font-bold')}>
{title}
</h1>
</div>
);
}
Combine with custom styles:
<div
style={{
...tw('flex flex-col p-20 text-white'),
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
}}
>
<h1 style={tw('text-8xl font-bold')}>{title}</h1>
</div>
Automatic Tailwind Config Support
loopwind automatically detects and uses your tailwind.config.js!
If you have a Tailwind project:
// tailwind.config.js
export default {
theme: {
extend: {
colors: {
primary: '#667eea',
brand: {
500: '#0ea5e9',
900: '#0c4a6e',
},
},
spacing: {
'72': '18rem',
},
},
},
};
Your templates can use these values:
<div style={tw('bg-primary text-white p-72')}>
<div style={tw('bg-brand-500')}>Custom brand color!</div>
</div>
Config priority:
tailwind.config.js(if exists)loopwind.json(fallback)- Default Tailwind values
Supported classes:
- Layout:
flex,flex-col,items-center,justify-between, etc. - Spacing:
p-4,px-8,m-6,gap-4, etc. - Typography:
text-6xl,font-bold,text-center, etc. - Colors:
bg-primary,text-brand-500, etc. (from your config!) - Effects:
opacity-90,rounded-xl, etc. - shadcn/ui colors:
bg-card,text-foreground,text-muted-foreground - Opacity modifiers:
bg-primary/50,text-muted-foreground/75
Note: Tailwind v4 uses CSS variables instead of JS config. We're working on CSS parsing support. For now, use loopwind.json or tailwind.config.js (v3 format).
Fonts
loopwind includes Inter (Regular 400, Bold 700) bundled with the CLI. Fonts work out of the box with no configuration required.
Default Fonts
By default, templates use the bundled Inter font. No setup required - works offline!
Custom Fonts
Configure custom fonts in loopwind.json:
{
"fonts": {
"sans": {
"family": ["CustomFont", "sans-serif"],
"files": [
{ "path": "fonts/CustomFont-Regular.woff", "weight": 400 },
{ "path": "fonts/CustomFont-Bold.woff", "weight": 700 }
]
}
}
}
Then use font classes in templates:
export default function Template({ title, tw }) {
return (
<h1 style={tw('font-sans font-bold text-6xl')}>
{title}
</h1>
);
}
Font paths are relative to your loopwind.json location.
Available classes:
font-sans- Usesfonts.sansfrom loopwind.jsonfont-serif- Usesfonts.seriffrom loopwind.jsonfont-mono- Usesfonts.monofrom loopwind.json
Supported formats: WOFF, WOFF2, TTF, OTF
Template Helpers
Templates have access to built-in helper functions:
tw(classes)- Convert Tailwind classes to inline stylesqr(text, options?)- Generate QR codes from text/URLsimage(propKey)- Embed images as data URIsvideo(propKey)- Embed video frames (for video templates)template(name, props)- Embed other templatesconfig- Access loopwind.json configuration
QR Code Helper
Generate QR codes from any text or URL using the qr() helper:
export default function Template({ title, url, qr, tw }) {
return (
<div style={tw('flex flex-col items-center p-16')}>
<h1 style={tw('text-5xl font-bold mb-8')}>{title}</h1>
{/* QR code auto-generated from url prop */}
<img src={qr(url)} width={200} height={200} alt="QR Code" />
<p style={tw('text-xl mt-4')}>{url}</p>
</div>
);
}
Usage:
loopwind render qr-card '{
"title": "Visit Our Website",
"url": "https://loopwind.dev"
}'
Customization:
// Custom size and error correction
<img src={qr(url, {
width: 300,
margin: 2,
errorCorrectionLevel: 'H', // L, M, Q, H (higher = more error correction)
color: {
dark: '#000000',
light: '#FFFFFF'
}
})} />
How it works:
