SnapAPI/public/blog.html
OpenClaw 740c70f905
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 9m27s
Add status route tests, OG images blog post, and blog tests
- Create src/routes/__tests__/status.test.ts (GET /status and /status.html)
- Add blog post: public/blog/automating-og-images.html (~1000 words)
- Update public/blog.html with new post entry
- Update public/sitemap.xml with new URL
- Add blog tests for automating-og-images post
- Update existing blog tests for new post references

Tests: 332 passed, 1 skipped
2026-03-03 18:06:56 +01:00

153 lines
9.4 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Blog — SnapAPI Developer Blog</title>
<meta name="description" content="Technical articles about screenshot APIs, web rendering, performance optimization, and building developer tools. From the SnapAPI engineering team.">
<link rel="canonical" href="https://snapapi.eu/blog">
<meta property="og:title" content="Blog — SnapAPI Developer Blog">
<meta property="og:description" content="Technical articles about screenshot APIs, web rendering, performance optimization, and building developer tools.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://snapapi.eu/blog">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Blog — SnapAPI Developer Blog">
<meta name="twitter:description" content="Technical articles about screenshot APIs, web rendering, and performance.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📸</text></svg>">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{--bg:#0a0e17;--bg2:#0f1420;--card:#141a28;--card-hover:#1a2235;--border:#1e2a3f;--border-light:#2a3752;--text:#f0f2f7;--text-secondary:#94a3c0;--muted:#6b7a96;--primary:#4f8fff;--primary-light:#6da3ff;--primary-dark:#3a6fd8;--primary-glow:rgba(79,143,255,0.15);--accent:#10b981;--purple:#a78bfa;--orange:#f59e0b;--pink:#ec4899;--gradient:linear-gradient(135deg,#4f8fff 0%,#a78bfa 50%,#ec4899 100%);--radius:12px;--radius-lg:16px}
html{scroll-behavior:smooth}
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.6;-webkit-font-smoothing:antialiased}
a{color:var(--primary-light);text-decoration:none;transition:color .2s}
a:hover{color:var(--primary)}
.container{max-width:900px;margin:0 auto;padding:0 24px}
nav{position:sticky;top:0;z-index:100;background:rgba(10,14,23,0.85);backdrop-filter:blur(20px);border-bottom:1px solid rgba(30,42,63,0.5);padding:0 24px}
.nav-inner{max-width:1180px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;height:64px}
.nav-logo{font-size:1.15rem;font-weight:800;display:flex;align-items:center;gap:8px;color:var(--text)}
.nav-logo span{background:var(--gradient);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.nav-links{display:flex;gap:32px;align-items:center}
.nav-links a{color:var(--muted);font-size:.9rem;font-weight:500;transition:color .2s}
.nav-links a:hover{color:var(--text)}
.btn{display:inline-flex;align-items:center;gap:8px;padding:12px 28px;border-radius:10px;font-weight:600;font-size:.95rem;border:none;cursor:pointer;transition:all .2s;font-family:inherit}
.btn-primary{background:var(--primary);color:#fff;box-shadow:0 4px 20px rgba(79,143,255,0.3)}
.btn-primary:hover{background:var(--primary-dark);transform:translateY(-1px);color:#fff}
.btn-sm{padding:8px 18px;font-size:.85rem}
.nav-mobile{display:none;background:none;border:none;color:var(--text);font-size:1.5rem;cursor:pointer}
@media(max-width:768px){.nav-links{display:none;flex-direction:column;position:absolute;top:64px;left:0;right:0;background:rgba(10,14,23,0.97);backdrop-filter:blur(20px);border-bottom:1px solid var(--border);padding:16px 24px;gap:16px;z-index:99}.nav-links.show{display:flex}.nav-mobile{display:block}}
.page-header{padding:80px 0 40px;text-align:center}
.page-header h1{font-size:3rem;font-weight:900;margin-bottom:16px}
.page-header p{font-size:1.15rem;color:var(--text-secondary);max-width:600px;margin:0 auto}
.blog-grid{display:grid;gap:32px;padding:0 0 100px}
.blog-card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:36px;transition:all .2s}
.blog-card:hover{background:var(--card-hover);border-color:var(--border-light);transform:translateY(-2px)}
.blog-card .meta{font-size:.82rem;color:var(--muted);margin-bottom:12px;display:flex;gap:16px}
.blog-card h2{font-size:1.5rem;font-weight:700;margin-bottom:12px}
.blog-card h2 a{color:var(--text)}
.blog-card h2 a:hover{color:var(--primary-light)}
.blog-card p{color:var(--text-secondary);line-height:1.7;margin-bottom:16px}
.blog-card .read-more{font-weight:600;font-size:.9rem;color:var(--primary-light)}
footer{border-top:1px solid var(--border);padding:48px 24px 32px;background:var(--bg2)}
.footer-grid{max-width:1180px;margin:0 auto;display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:40px;margin-bottom:40px}
.footer-brand h4{font-size:1.1rem;font-weight:800;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.footer-brand p{color:var(--muted);font-size:.85rem;line-height:1.6;max-width:280px}
.footer-col h5{font-size:.8rem;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--muted);margin-bottom:16px}
.footer-col a{display:block;color:var(--text-secondary);font-size:.88rem;padding:4px 0;transition:color .2s}
.footer-col a:hover{color:var(--text)}
.footer-bottom{max-width:1180px;margin:0 auto;padding-top:24px;border-top:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px;font-size:.8rem;color:var(--muted)}
@media(max-width:768px){.footer-grid{grid-template-columns:1fr}}
.skip-link{position:absolute;top:-100%;left:50%;transform:translateX(-50%);background:var(--primary);color:#fff;padding:12px 24px;border-radius:0 0 8px 8px;font-weight:600;font-size:.9rem;z-index:1000;transition:top .2s}
.skip-link:focus{top:0}
</style>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"Blog","name":"SnapAPI Developer Blog","description":"Technical articles about screenshot APIs, web rendering, and performance optimization.","url":"https://snapapi.eu/blog","publisher":{"@type":"Organization","name":"SnapAPI","url":"https://snapapi.eu"}}</script>
</head>
<body>
<a href="#main-content" class="skip-link">Skip to content</a>
<header>
<nav>
<div class="nav-inner">
<a href="/" class="nav-logo">📸 <span>SnapAPI</span></a>
<div class="nav-links">
<a href="/#features">Features</a>
<a href="/pricing">Pricing</a>
<a href="/docs">API Docs</a>
<a href="/blog">Blog</a>
<a href="/#pricing" class="btn btn-primary btn-sm">Get API Key</a>
</div>
<button class="nav-mobile" onclick="document.querySelector('.nav-links').classList.toggle('show')" aria-label="Menu"></button>
</div>
</nav>
</header>
<main id="main-content">
<div class="container">
<div class="page-header">
<h1>Developer Blog</h1>
<p>Technical deep-dives on screenshot APIs, web rendering, caching strategies, and building reliable developer tools.</p>
</div>
<div class="blog-grid">
<article class="blog-card">
<div class="meta"><span>March 3, 2026</span><span>7 min read</span></div>
<h2><a href="/blog/automating-og-images">Automating OG Image Generation with Screenshot APIs</a></h2>
<p>Stop designing Open Graph images by hand. Learn how to use screenshot APIs to automatically generate beautiful, dynamic OG images for every page on your site.</p>
<a href="/blog/automating-og-images" class="read-more">Read article →</a>
</article>
<article class="blog-card">
<div class="meta"><span>March 2, 2026</span><span>8 min read</span></div>
<h2><a href="/blog/why-screenshot-api">Why You Need a Screenshot API (And Why Building Your Own Is Harder Than You Think)</a></h2>
<p>Self-hosting Puppeteer sounds easy until you're debugging zombie Chrome processes at 3 AM. Here's why dedicated screenshot APIs exist and when they make sense for your project.</p>
<a href="/blog/why-screenshot-api" class="read-more">Read article →</a>
</article>
<article class="blog-card">
<div class="meta"><span>March 2, 2026</span><span>5 min read</span></div>
<h2><a href="/blog/screenshot-api-performance">Screenshot API Performance: Caching Strategies That Actually Work</a></h2>
<p>How we reduced p95 response times by 10x with intelligent caching, CDN integration, and browser pool management. A practical guide to making screenshot APIs fast.</p>
<a href="/blog/screenshot-api-performance" class="read-more">Read article →</a>
</article>
</div>
</div>
</main>
<footer>
<div class="footer-grid">
<div class="footer-brand">
<h4>📸 SnapAPI</h4>
<p>The EU-hosted screenshot API for developers. Convert any URL to a pixel-perfect image with a simple API call.</p>
</div>
<div class="footer-col">
<h5>Product</h5>
<a href="/#features">Features</a>
<a href="/pricing">Pricing</a>
<a href="/docs">API Docs</a>
<a href="/blog">Blog</a>
</div>
<div class="footer-col">
<h5>Developers</h5>
<a href="/docs">Swagger / OpenAPI</a>
<a href="/guides/quick-start">Quick Start</a>
<a href="/changelog">Changelog</a>
</div>
<div class="footer-col">
<h5>Legal</h5>
<a href="/impressum.html">Impressum</a>
<a href="/privacy.html">Privacy Policy</a>
<a href="/terms.html">Terms of Service</a>
</div>
</div>
<div class="footer-bottom">
<span>© 2026 Cloonar Technologies GmbH · FN 631089y · ATU81280034</span>
<span>EU-hosted 🇪🇺 · All data stays in Europe</span>
</div>
</footer>
</body>
</html>