Something went wrong. Try again.
Central platform for European atproto.<cc> country community websites
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384// Generate the Open Graph share card (1200x630) in the atproto.nl design system.// One-off / manual tooling, not part of `npm run build`. Regenerate with:// node scripts/og-card.mjs// Renders an HTML template with Playwright (already a dev dep via @axe-core/playwright),// then downscales 2x -> 1200x630 with sharp for crisp text. Output: public/brand/og.png.import { chromium } from 'playwright';import sharp from 'sharp';import { readFileSync } from 'node:fs';import { fileURLToPath } from 'node:url';import { dirname, resolve } from 'node:path';
const here = dirname(fileURLToPath(import.meta.url));const root = resolve(here, '..');const W = 1200;const H = 630;
// Gruvbox dark tokens (from src/styles/global.css [data-theme='dark']).const PAPER = '#282828';const INK = '#ebdbb2';const INKSTRONG = '#fbf1c7';const ACCENT = '#fe8019';const RULE = '#504945';
const avatar = readFileSync(resolve(root, 'public/brand/atproto-nl-avatar-1000.png')).toString('base64');
const html = `<!doctype html><html><head><meta charset="utf-8"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:wght@400;600&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet"><style> * { margin: 0; box-sizing: border-box; } html, body { width: ${W}px; height: ${H}px; } body { background: ${PAPER}; color: ${INK}; font-family: 'Atkinson Hyperlegible Next', sans-serif; padding: 88px 96px; display: flex; flex-direction: column; justify-content: space-between; position: relative; overflow: hidden; } .avatar { position: absolute; top: 72px; right: 96px; width: 132px; height: 132px; border-radius: 20px; } .wordmark { font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 108px; letter-spacing: -0.01em; color: ${INKSTRONG}; line-height: 1; } .wordmark .a { color: ${ACCENT}; } .tagline { font-size: 46px; font-weight: 400; color: ${INK}; max-width: 900px; line-height: 1.25; } .rule { height: 6px; width: 128px; background: ${ACCENT}; border-radius: 3px; margin-bottom: 40px; } .foot { font-family: 'JetBrains Mono', monospace; font-weight: 500; font-size: 26px; letter-spacing: 0.08em; color: ${INK}; opacity: 0.85; text-transform: uppercase; } .foot .sep { color: ${RULE}; padding: 0 14px; }</style></head><body> <img class="avatar" src="data:image/png;base64,${avatar}" alt=""> <div> <div class="wordmark"><span class="a">//</span>atproto<span class="a">.</span>nl</div> </div> <div> <div class="rule"></div> <div class="tagline">The AT Protocol community in the Netherlands</div> </div> <div class="foot">Meetups<span class="sep">/</span>Members<span class="sep">/</span>Tools<span class="sep">/</span>Open protocol</div></body></html>`;
const browser = await chromium.launch();const page = await browser.newPage({ viewport: { width: W, height: H }, deviceScaleFactor: 2 });await page.setContent(html, { waitUntil: 'networkidle' });await page.evaluate(() => document.fonts.ready);const buf = await page.screenshot({ type: 'png' });await browser.close();
await sharp(buf).resize(W, H).png({ compressionLevel: 9 }).toFile(resolve(root, 'public/brand/og.png'));console.log(`Wrote public/brand/og.png (${W}x${H})`);