Enlaces Enriquecidos para Desarrolladores Perezosos
Cuando comparto un enlace de mi blog en Twitter o Slack, aparece como texto plano. Sin imagen de vista previa. Solo una URL como granda.org/en/2026/01/02/claude-code-on-the-go/.
Necesitaba imágenes Open Graph. El enfoque estándar: crear manualmente una imagen de 1200x630 para cada entrada. Eso es tedioso. Le pedí a Claude que lo automatizara.
flowchart LR
Push[git push] --> GHA[GitHub Actions]
GHA --> Hugo[Hugo Server]
GHA --> PW[Playwright]
PW -->|screenshot| Hugo
PW --> IMG[OG Image]
IMG --> Commit[git commit]
Commit --> Deploy[Deploy]
flowchart TB
Push[git push] --> GHA[GitHub Actions]
GHA --> Hugo[Hugo Server]
GHA --> PW[Playwright]
PW -->|screenshot| Hugo
PW --> IMG[OG Image]
IMG --> Commit[git commit]
Commit --> Deploy[Deploy]
La Configuración
Le expliqué el problema a Claude: las entradas necesitan imágenes de vista previa social, pero no quiero crearlas manualmente. Captura el contenido del artículo, guárdalo como imagen OG, actualiza el frontmatter automáticamente.
Lo Que Claude Construyó
| Componente | Propósito |
|---|---|
generate-og-image.js | Script de Playwright para capturar pantallas de las entradas |
generate-og-images.yml | GitHub Action activada por cambios en las entradas |
Cambios en baseof.html | Etiquetas meta og:image condicionales |
El script de Playwright lanza Chrome sin interfaz, navega a la entrada, fuerza el modo claro, oculta el encabezado y el pie de página, y captura la pantalla a 1200x630:
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1200, height: 630 },
deviceScaleFactor: 2, // Retina for crisp text
});
// Force light mode, hide nav
await page.evaluate(() => {
document.documentElement.setAttribute("data-theme", "light");
document.querySelector("header").style.display = "none";
document.querySelector("footer").style.display = "none";
});
await page.screenshot({ path: outputPath, type: "png" });
La GitHub Action se activa con cualquier cambio en archivos .en.md, inicia Hugo, ejecuta el script y hace commit de la imagen generada de vuelta al repositorio:
on:
push:
branches: [main]
paths:
- "content/posts/**/*.md"
steps:
- name: Start Hugo server
run: hugo server --bind 0.0.0.0 --port 1313 &
- name: Generate OG images
run: node scripts/generate-og-image.js "$file"
- name: Commit changes
run: |
git add static/images/posts/ content/posts/
git commit -m "Generate Open Graph images for blog posts"
git push
La plantilla incluye condicionalmente la imagen cuando el frontmatter tiene un campo image:
{{- if .Params.image -}}
<meta property="og:image" content="{{ $canonical }}{{ .Params.image }}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="{{ $canonical }}{{ .Params.image }}">
{{- else -}}
<meta name="twitter:card" content="summary">
{{- end -}}
404: Slug No Encontrado
Primera ejecución. Playwright capturó una página en blanco. La URL devolvía 404.
El script había construido la URL a partir del nombre del archivo: automatic-blog-translations.en.md → /en/2025/12/23/automatic-blog-translations/
Pero Hugo no usa el nombre del archivo para los slugs. Usa el título. La URL real era /en/2025/12/23/automatic-blog-translations-with-claude-and-github-actions/.
Podría haber reimplementado el algoritmo slugify de Hugo en JavaScript. En su lugar: preguntarle a Hugo.
const output = execSync("hugo list all", { encoding: "utf8" });
// CSV output includes the exact permalink Hugo generates
Una línea. Sin casos especiales. Hugo ya conoce sus propias URLs.
Qué Sigue
La implementación actual solo genera imágenes para entradas en inglés. Las versiones traducidas referencian la misma ruta de imagen, lo cual funciona, pero significa que la imagen OG siempre muestra texto en inglés incluso cuando se comparte una versión en español o japonés.
El verdadero soporte i18n llegará en el próximo PR. Por ahora, cada entrada obtiene una imagen social, lo cual es mejor que ninguna.
La Conclusión
Esto tomó 14 minutos construir y depurar. Ahora cada entrada obtiene automáticamente una imagen de vista previa social. Sin trabajo manual por entrada. La automatización se paga a sí misma después de un puñado de entradas.
Lanza la versión funcional, descubre las brechas, itera.
Entradas relacionadas:
- Traducciones Automáticas del Blog con Claude y GitHub Actions - mismo stack, diferente automatización
- Mi Ingeniero de QA es un LLM - dejando que Claude revise el código que escribe
- Creando Mi Propio Newsletter con Claude - otra construcción de 30 minutos