The Problem with Stock Photos
When we launched the Zenith Reborn blog, we used Unsplash for cover images. While Unsplash offers beautiful, free photos, they had several drawbacks:
- Generic appearance - Stock photos don't reflect our brand identity
- Licensing concerns - Need to track attribution and usage rights
- Inconsistent style - Each photo has different aesthetics
- No project distinction - Can't tell SkillQuest posts from Zenith posts at a glance
- SEO limitations - No branding in social media previews
We needed something better: custom, branded cover images that instantly communicate project identity and content type.
The Solution: HTML/CSS Cover Generator
Instead of using design tools like Figma or Photoshop (which would require manual work for each post), we built a web-based cover generator using pure HTML and CSS. This approach offers several advantages:
- ✅ Version controlled - Cover designs live in the git repository
- ✅ Reusable - One template generates infinite variations
- ✅ Automated - Playwright MCP can screenshot programmatically
- ✅ Customizable - Live preview with form controls
- ✅ Consistent - Every cover follows the same design system
What We Built
1. Master Template with Live Preview
The core of the system is cover-template.html - a self-contained HTML file with:
Form Controls:
- Project selector - Zenith Reborn or SkillQuest (changes logo)
- Theme selector - Feature, Bug Fix, Performance, DevOps, Announcement
- Main text - 2 lines, 1-3 words each (e.g., "NEW FEATURE" / "RELEASED")
- Subtitle - 2-4 words (e.g., "Production Ready")
- Update preview button - Instant visual feedback
CSS Architecture:
/* Base cover: 1200x630px OpenGraph standard */
.cover {
width: 1200px;
height: 630px;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1210 50%, #0a0a0a 100%);
position: relative;
overflow: hidden;
}
/* Theme variations */
.cover.theme-feature {
background: linear-gradient(135deg, #0a0a0a 0%, #1a1210 50%, #0a0a0a 100%);
/* Gold tint for new features */
}
.cover.theme-bugfix {
background: linear-gradient(135deg, #0a0a0a 0%, #1a0a14 50%, #0a0a0a 100%);
/* Purple tint for bug fixes */
}
.cover.theme-performance {
background: linear-gradient(135deg, #0a0a0a 0%, #0a140a 50%, #0a0a0a 100%);
/* Green tint for performance */
}
.cover.theme-devops {
background: linear-gradient(135deg, #0a0a0a 0%, #0a0e14 50%, #0a0a0a 100%);
/* Blue tint for DevOps */
}
Logo System:
<!-- Phoenix logo for Zenith Reborn -->
<img src="../public/phoenix-logo-transparent.png"
class="logo"
style="width: 280px; filter: drop-shadow(0 0 40px rgba(255, 215, 0, 0.6));">
<!-- SkillQuest logo for SkillQuest posts -->
<img src="../public/skillquest-logo.png"
class="logo"
style="width: 300px; filter: drop-shadow(0 0 40px rgba(74, 144, 226, 0.6));">
2. Batch Generators
For efficiency, we created batch generator files:
generate-zenith-covers.html - Generates all Zenith covers in one page:
- zenith-feature-comments.png (Giscus Comments)
- zenith-announcement-welcome.png (Welcome Post)
- zenith-feature-blog.png (Blog System Launch)
- zenith-mvp-cover.png (MVP Complete)
generate-skillquest-covers.html - Generates all SkillQuest covers:
- skillquest-bugfix-version.png (Version Management)
- skillquest-feature-production.png (Production Readiness)
- skillquest-bugfix-theme.png (Theme System Fix)
- skillquest-feature-setup.png (Week 1 Setup)
3. Automated Screenshot Workflow
We use Playwright MCP to automate screenshot generation:
// 1. Navigate to generator HTML
await page.goto('file:///path/to/cover-template.html');
// 2. Screenshot the .cover element (1200x630px)
await page.locator('.cover').first().screenshot({
path: 'public/blog/zenith-feature-example.png',
type: 'png',
scale: 'css'
});
// 3. Done! Image saved to public/blog/
This workflow takes seconds per image compared to minutes in a design tool.
Theme Guidelines
Each theme has a specific purpose and color scheme:
Feature Theme (Gold)
- Use for: New features, major updates, releases
- Color: Gold gradient (#FFD700 tint)
- Examples: "NEW FEATURE", "MVP COMPLETE", "VERSION 2.0"
Bug Fix Theme (Purple)
- Use for: Bug fixes, critical fixes, patches
- Color: Purple-tinted background (#1a0a14)
- Examples: "BUG FIX", "CRITICAL FIX", "HOTFIX"
Performance Theme (Green)
- Use for: Performance improvements, optimizations
- Color: Green-tinted background (#0a140a)
- Examples: "PERFORMANCE", "OPTIMIZED", "2X FASTER"
DevOps Theme (Blue)
- Use for: Deployment, infrastructure, monitoring, CI/CD
- Color: Blue-tinted background (#0a0e14)
- Examples: "DEPLOYED", "CI/CD READY", "ERROR MONITORING"
Announcement Theme (Yellow)
- Use for: Announcements, launches, milestones
- Color: Yellow-gold gradient
- Examples: "ANNOUNCING", "LAUNCHED", "NOW LIVE"
Implementation Results
Before: Stock Photos
Every blog post used Unsplash URLs like:
coverImage: "https://images.unsplash.com/photo-xyz?w=1200&h=630&fit=crop"
Issues:
- ❌ No branding or project identity
- ❌ Unpredictable visual style
- ❌ External dependency on Unsplash
- ❌ Attribution requirements
After: Custom Branded Covers
Every blog post now uses custom covers:
coverImage: "/blog/zenith-feature-comments.png"
Benefits:
- ✅ Instant brand recognition (Phoenix = Zenith, SkillQuest logo = SkillQuest)
- ✅ Consistent design system across all posts
- ✅ Self-hosted (no external dependencies)
- ✅ Theme-based visual categorization
- ✅ Professional appearance on Twitter/LinkedIn
- ✅ SEO benefits (branded OG images)
Generated Images (9 total)
Zenith Reborn (4):
- zenith-feature-comments.png - 429 KB
- zenith-announcement-welcome.png - 382 KB
- zenith-feature-blog.png - 427 KB
- zenith-mvp-cover.png - 435 KB
- zenith-devops-monitoring.png - 295 KB
SkillQuest (4):
- skillquest-bugfix-version.png - 396 KB
- skillquest-feature-production.png - 428 KB
- skillquest-bugfix-theme.png - 385 KB
- skillquest-feature-setup.png - 421 KB
All images are OpenGraph compliant (1200x630px) and optimized for web (200-500 KB range).
Technical Details
File Structure
tools/
cover-generators/
README.md # Documentation
cover-template.html # Master template
generate-zenith-covers.html # Zenith batch generator
generate-skillquest-covers.html # SkillQuest batch generator
generate-mvp-cover-final.html # Example: MVP cover
public/
blog/
zenith-*.png # Zenith covers
skillquest-*.png # SkillQuest covers
Naming Convention
{project}-{theme}-{description}.png
Examples:
- zenith-feature-comments.png
- zenith-announcement-welcome.png
- zenith-devops-monitoring.png
- skillquest-bugfix-version.png
- skillquest-feature-production.png
Image Specifications
- Format: PNG (lossless, transparent background support)
- Dimensions: 1200x630px (OpenGraph/Twitter Card standard)
- Color depth: 24-bit RGB
- File size: 200-500 KB (acceptable for web, optimized by Vercel CDN)
- DPI: 72 (screen resolution)
Workflow for New Blog Posts
When creating a new blog post, the cover generation workflow is:
- Write blog post in
content/posts/your-post.mdx - Open template:
tools/cover-generators/cover-template.html - Customize:
- Select project (Zenith Reborn or SkillQuest)
- Select theme (Feature, Bug Fix, Performance, DevOps, Announcement)
- Enter main text (2 lines, 1-3 words each)
- Enter subtitle (2-4 words)
- Click "Update Preview" to see changes
- Generate PNG:
- Method 1: Playwright MCP (recommended)
- Method 2: Browser screenshot (DevTools)
- Save to
public/blog/{project}-{theme}-{slug}.png - Update frontmatter:
coverImage: "/blog/{project}-{theme}-{slug}.png" - Commit both files (blog post + cover image)
- Deploy to Vercel
Total time: 2-3 minutes per cover (vs 15-30 minutes in Figma/Photoshop).
Integration with /blog-post Command
The cover generation system is integrated into the global /blog-post command workflow:
### Step 6: Generate Custom Branded Cover Image
**REQUIRED: Create custom branded cover image using the cover generator system**
Instead of using stock Unsplash photos, generate a professional branded cover image.
**Cover Image System Location:**
- Generator templates: `tools/cover-generators/`
- Documentation: `tools/cover-generators/README.md`
- Output directory: `public/blog/`
This ensures every new blog post automatically gets a custom cover image.
Performance Impact
Despite adding custom images, bundle sizes remain excellent:
| Route | First Load JS | Status |
|---|---|---|
| Homepage | 116 kB | ✅ 42% under target |
| Blog listing | 114 kB | ✅ 43% under target |
| Blog posts | 112 kB | ✅ 44% under target |
Why no impact?
- Images are static assets (not bundled in JS)
- Vercel CDN optimizes PNG compression
- Next.js Image component lazy loads covers
- 1200x630px is already optimized for web
Lessons Learned
1. HTML/CSS Beats Design Tools for Templates
Using HTML/CSS instead of Figma/Photoshop offers massive advantages:
- Version control - Git tracks design changes
- Automation - Playwright can screenshot programmatically
- Reusability - One template = infinite variations
- Speed - 2 minutes vs 15 minutes per cover
2. Gradients Create Visual Depth
Subtle gradients (135deg, dark → tinted → dark) create visual interest without overwhelming the content:
background: linear-gradient(135deg, #0a0a0a 0%, #1a1210 50%, #0a0a0a 100%);
The 50% midpoint with tint creates a "spotlight" effect on the center.
3. Consistent Branding Matters
Before custom covers, blog posts looked generic. Now:
- Phoenix logo = Instant Zenith recognition
- SkillQuest logo = Instant SkillQuest recognition
- Theme colors = Visual categorization (gold = feature, purple = bugfix)
This improves brand recall and user experience.
4. Batch Generators Save Time
Creating separate batch generators (generate-zenith-covers.html, generate-skillquest-covers.html) allowed us to:
- Generate all covers in one session
- Ensure consistency across posts
- Quickly update all covers if design changes
5. Documentation Enables Reuse
The comprehensive README.md ensures anyone can:
- Understand the system
- Generate new covers
- Customize themes
- Follow best practices
This makes the system sustainable long-term.
Future Enhancements
Ideas for improving the cover system:
- More themes - Security (red), UI (purple), API (orange)
- Animated gradients - Subtle background animations
- Icon/badge system - Visual tags for categories
- Dark/light mode variants - Adapt to user preference
- CLI automation -
npm run generate-cover -- --project zenith --theme feature --text "NEW FEATURE" - AI-powered text - Suggest main text based on blog title
Conclusion
Building a custom cover image system transformed our blog from using generic stock photos to having a professional, branded visual identity. The HTML/CSS approach combined with Playwright automation created a workflow that's:
- ⚡ Fast - 2-3 minutes per cover
- 🎨 Consistent - Same design system across all posts
- 🔧 Maintainable - Version controlled and documented
- ♻️ Reusable - One template, infinite variations
- 🚀 Scalable - Works for Zenith, SkillQuest, and future projects
Total Results:
- 🎨 9 custom covers generated
- 📝 9 blog posts updated with branded images
- 🔄 100% migration from stock photos to custom covers
- ⏱️ ~20 minutes total time investment
- ♾️ Infinite future covers using the same templates
The phoenix-themed covers now instantly communicate "Zenith Reborn" while SkillQuest covers establish that brand identity. Social media previews look professional, and the entire system is ready to scale with future blog posts.
Files Changed:
- 16 files modified (commit 7d89fe1)
- 9 PNG images added (~3.5 MB total)
- 2 batch generators created
- 1 master template with live preview
- 1 comprehensive README
From concept to production in one afternoon - a testament to the power of simple, well-documented systems.
Geschreven door Hans
Comments
Sign in with GitHub to leave a comment. Comments are powered by Giscus.
You might also like

Adding Professional Error Monitoring to Zenith Reborn
How we integrated Sentry for real-time error tracking, session replays, and production monitoring in under 2 hours.

SkillQuest Production Readiness - Enterprise Error Monitoring & Critical Fixes
Implemented Sentry error monitoring, resolved database synchronization issues, and completed critical production fixes across 19 commits.

Zenith Reborn MVP Complete: From Concept to Production in Record Time
The complete journey of building and launching a production-ready marketing website with working forms, legal compliance, comprehensive testing, and zero security vulnerabilities.

Building Community: Adding Comments to the Zenith Blog
Implementing a GitHub Discussions-based commenting system using Giscus to enable community engagement on blog posts while maintaining privacy and performance.
