Imagen OG animada
Todos los archivos que usa esta habilidad se entregan en su propia carpeta, junto a este SKILL.md, y las rutas aquí son relativas a esa carpeta. No necesita nada de otras habilidades ni de otras carpetas, así que no vayas a buscarlas. A menos que el usuario indique otro lugar, guarda todo lo que genere en out/og-image-animated/ dentro del proyecto actual.
La tarjeta de compartir estática animada como una revelación breve y tranquila:
- Tarjeta vacía: solo fondo y brillo suave.
- La marca y el título caen desde arriba (fundido + deslizamiento hacia abajo).
- La ventana de la aplicación asciende desde abajo (fundido + deslizamiento hacia arriba) y se asienta exactamente donde estaba en la tarjeta estática.
- Pausa en el fotograma final, que debe coincidir con la imagen OG estática.
Ejemplos: references/pulse.gif (el resultado) y references/pulse-frames.png (cuatro fotogramas: inicio, título entrando, aplicación ascendiendo, final).
Reglas
- Partir de la tarjeta estática. Si el proyecto aún no tiene un componente de tarjeta OG, constrúyelo primero siguiendo
references/static-card.md(producto real, mensajes del sitio actual, datos realistas). La animación solo añade movimiento: mismo marcado, mismo diseño final, sin contenido nuevo. - Un solo componente, dos modos. Añade una propiedad
animatedal componente de tarjeta existente en lugar de copiarlo, para que la PNG estática y la animación nunca se desvíen. Sin la propiedad, no se anima nada. - Movimiento, no efectos. Solo
opacity+transform: translateY, una única curva de aceleración compartida (cubic-bezier(0.16, 1, 0.3, 1)). Sin rebotes, desenfoques, escalados, texto de máquina de escribir ni elementos giratorios. - Corta. Toda la línea de tiempo dura unos 2–2.5 s, luego una pausa de ~1.5 s. El título debe ser completamente legible hacia ~1.3 s.
- Solo animaciones CSS (keyframes +
animationcon rellenoboth), sin interpolaciones ni temporizadores JavaScript dentro de la tarjeta. El grabador busca animaciones CSS a través de la Web Animations API; el movimiento controlado por JS no se capturaría de manera determinista.
Línea de tiempo
| Elemento | Keyframes | Duración | Retardo |
|---|---|---|---|
| Marca | desde opacity: 0; translateY(-48px) | 900 ms | 100 ms |
| Título | desde opacity: 0; translateY(-48px) | 1000 ms | 250 ms |
| Ventana de app | desde opacity: 0; translateY(320px) | 1300 ms | 700 ms |
Ajusta las distancias a la tarjeta: la aplicación debe comenzar mayormente por debajo del borde inferior (aproximadamente la mitad de la altura de la tarjeta) y el título aproximadamente a su propia altura por encima de su lugar de descanso. Si hay un panel flotante, que llegue al final (+200 ms después de que la ventana comience).
Pasos
1. Descubrir
- Encuentra el componente de tarjeta estática y su ruta de renderizado (ej.
og-image.tsx,/og/<pagina>), y la PNG actual enpublic/. - Anota los envoltorios de la marca, el título y la ventana de la aplicación. Si la ventana usa
zoompara ajustarse, anima un envoltorio interno, no el elemento con zoom, para que los desplazamientos sigan siendo correctos.
2. Añadir el modo animado
En el componente de tarjeta:
const KEYFRAMES = `
@keyframes og-drop { from { opacity: 0; transform: translateY(-48px); } to { opacity: 1; transform: none; } }
@keyframes og-rise { from { opacity: 0; transform: translateY(320px); } to { opacity: 1; transform: none; } }
`;
const EASE = 'cubic-bezier(0.16, 1, 0.3, 1)';
const enter = (name: string, ms: number, delay: number) => ({ animation: `${name} ${ms}ms ${EASE} ${delay}ms both` });
// <div data-og-frame …>{animated && <style>{KEYFRAMES}</style>} …
// marca: style={animated ? enter('og-drop', 900, 100) : undefined}
// título: style={animated ? enter('og-drop', 1000, 250) : undefined}
// ventana: <div style={{ zoom }}><div className="h-full" style={animated ? enter('og-rise', 1300, 700) : undefined}>…
Pon los keyframes en el componente (una etiqueta <style>) o en el CSS global del proyecto; con Tailwind v4, los keyframes dentro de @theme solo se emiten cuando se usa un token --animate-* correspondiente, así que prefiere el componente o CSS plano.
3. Añadir una ruta web que lo reproduzca
Una ruta hermana de la estática, ej. /og/<pagina>-animated (noindex, sin interfaz de la aplicación):
- Renderiza
<Card animated />dentro del marco 1200×630. - Se reproduce en bucle para previsualización: vuelve a montar la tarjeta con una
keycambiante cada ~4.5 s. ?loop=0reproduce una sola vez (el grabador usa esto). Normaliza el parámetro de búsqueda a cadena primero: enrutadores como TanStack parsean?loop=0como el número0, y una verificación omitida seguiría remontando la tarjeta durante la grabación.
Ábrela en el navegador y mírala varias veces. Verifica el orden de entrada, que nada parpadee antes de que comience la animación y que el estado final coincida con la tarjeta estática.
4. Grabar MP4 + GIF
Con el servidor de desarrollo en ejecución:
node <carpeta de esta habilidad>/scripts/record.mjs \
--url "http://localhost:<puerto>/og/<pagina>-animated?loop=0" \
--out out/og-image-animated/og-<pagina>
Escribe og-<pagina>.mp4 (2400×1260, H.264, 30 fps), og-<pagina>.gif (1200 px de ancho, optimizado por paleta, en bucle) y og-<pagina>.png (fotograma final). Cómo funciona: pausa cada animación en la página, lee la duración de la línea de tiempo, luego recorre todas las animaciones fotograma a fotograma y captura una región fija de [data-og-frame]. El resultado es idéntico en cada ejecución, sin fotogramas perdidos. Opciones: --fps, --hold, --lead, --scale, --formats mp4,gif,png, --selector.
Necesita ffmpeg en PATH. Playwright se instala en la carpeta scripts/ de esta habilidad en la primera ejecución, junto con su Chromium si no se encuentra ninguno.
Trampas:
- La página no debe forzar movimiento reducido. Muchas hojas de estilo globales acortan las animaciones bajo
prefers-reduced-motion; el grabador no lo emula y falla ruidosamente si no encuentra animaciones. - Las capturas de pantalla de elementos esperan "estabilidad", que nunca llega durante una animación. Por eso el script recorta una región de la página.
- Asegúrate de que el puerto realmente sirva esta aplicación (otro servidor local podría responder en
localhost).
5. Revisar
Extrae una hoja de contactos y mírala:
ffmpeg -y -loglevel error -i og-<pagina>.mp4 \
-vf "select='eq(n\,8)+eq(n\,22)+eq(n\,40)+eq(n\,<último>)',scale=1000:-1,tile=2x2" -frames:v 1 sheet.png
- Fotograma ~0.25 s: tarjeta vacía (solo brillo), sin salto de diseño.
- ~0.7 s: título entrando o casi, aplicación aún no visible.
- ~1.3 s: aplicación a media altura, título completamente legible.
- Último fotograma: idéntico en contenido a la PNG estática.
Mantén el GIF razonablemente pequeño (por debajo de ~3 MB). Si es más grande, baja --fps (20–24) o acorta --hold.
Salida
Informa al usuario:
- la URL de la ruta de previsualización (en bucle) y los archivos escritos (MP4, GIF, PNG)
- el comando de regeneración
- la línea de tiempo (qué se mueve, duraciones)
- que
og:imagedebe seguir siendo la PNG estática: las plataformas sociales no reproducen imágenes OG animadas. Usa el MP4/GIF para publicaciones, README, páginas de lanzamiento oog:videodonde sea compatible.