Zenith Reborn

Building a Custom Cover Image System for Blog Posts

How we eliminated stock photos and built a reusable, branded cover image generation system with HTML/CSS templates and Playwright automation.

#feature#devops#performance
Building a Custom Cover Image System for Blog Posts

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):

SkillQuest (4):

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:

  1. Write blog post in content/posts/your-post.mdx
  2. Open template: tools/cover-generators/cover-template.html
  3. 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)
  4. Click "Update Preview" to see changes
  5. Generate PNG:
    • Method 1: Playwright MCP (recommended)
    • Method 2: Browser screenshot (DevTools)
  6. Save to public/blog/{project}-{theme}-{slug}.png
  7. Update frontmatter:
    coverImage: "/blog/{project}-{theme}-{slug}.png"
    
  8. Commit both files (blog post + cover image)
  9. 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:

RouteFirst Load JSStatus
Homepage116 kB✅ 42% under target
Blog listing114 kB✅ 43% under target
Blog posts112 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.