/aeo-geo-mejores-practicas — SEO + AEO + GEO + Lighthouse 100 Playbook
Workflow completo para transformar cualquier página o SPA en una máquina de descubrimiento orgánico (SEO), motores de respuesta (AEO) y motores generativos/agentes de IA (GEO), garantizando simultáneamente 98–100 en Google Lighthouse 13 (Desktop y Mobile) en las 5 categorías: Performance, Accessibility, Best Practices, SEO y Agentic Browsing.
Caminos: <skill> es la carpeta donde se encuentra este SKILL.md.
0. Alineamiento Rápido Antes de Ejecutar
0.1. Detecta la stack y lee la guía correspondiente
Las reglas de este archivo aplican para cualquier sitio web. Lo que cambia entre frameworks (dónde está el <head>, la carpeta pública, cómo prerenderizar, cómo optimizar imágenes y evitar TBT, qué comando inicia la vista previa) está en references/frameworks/. Identifica la stack por el proyecto y lee la guía antes de aplicar los pasos 1 a 5:
| Señal en el proyecto | Stack | Guía |
|---|---|---|
angular.json o @angular/core | Angular | references/frameworks/angular.md |
next.config.* o next | Next.js | references/frameworks/nextjs.md |
nuxt.config.* o nuxt / vite.config.* + vue | Nuxt / Vue + Vite | references/frameworks/vue-nuxt.md |
astro.config.* o astro | Astro | references/frameworks/astro.md |
vite.config.* + react | Vite + React | references/frameworks/vite-react.md |
index.html sin framework | HTML estático | references/frameworks/html-estatico.md |
Si la stack no está en la lista (ej.: SvelteKit, Remix, Gatsby, WordPress), aplica las reglas generales de este archivo y usa la guía más parecida como referencia: los frameworks con SSR/SSG siguen la lógica de Next/Nuxt; las SPA siguen la de Vite + React.
0.2. Confirma con el usuario
Antes de modificar el código, confirma (o extrae del propio proyecto si ya está explícito):
- Dominio canónico de producción (ej.:
https://misitio.com/). Si aún no hay dominio definido, pregunta o usa un placeholder claro y avisa al usuario. - FAQ Visible vs. Invisible (solo AEO/GEO):
- Pregunta si el usuario quiere la sección de Preguntas Frecuentes (FAQ) visible en el diseño de la página (
<details>/<summary>) o solo invisible en segundo plano (dentro del@graphJSON-LD +llms.txt+<noscript>) para no alterar el diseño visual.
- Pregunta si el usuario quiere la sección de Preguntas Frecuentes (FAQ) visible en el diseño de la página (
- Preferencia de rastreo para entrenamiento (al crear o modificar
robots.txt) :- Verifica si el sitio ya ha definido una preferencia. Si no hay una elección clara, ofrece dos opciones: permitir la búsqueda y bloquear el rastreo para entrenamiento de OpenAI y Anthropic; o permitir también ese rastreo para entrenamiento. Conserva las demás reglas del sitio.
1. SEO Técnico Completo (index.html, robots.txt, sitemap.xml)
Consulta las plantillas listas en references/templates.md.
- Meta Tags Esenciales en el
<head>:<html lang="es">(o idioma del proyecto).<title>fuerte (50–65 caracteres) con Nombre de la Marca/Especialista + Propuesta Principal.<meta name="description">(140–165 caracteres) respondiendo claramente qué es, para quién es y qué incluye.<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" /><link rel="canonical" href="https://dominio.com/" />+<link rel="alternate" hreflang="es" ... />yx-default.- Open Graph completo (
og:type,og:locale,og:site_name,og:title,og:description,og:url,og:image,og:image:width,og:image:height,og:image:alt) y Twitter Cards (summary_large_image).
public/robots.txtAmigable para Buscadores e IAs:- Para mantener la posibilidad de aparecer en las búsquedas de ChatGPT y Claude, permite
OAI-SearchBotyClaude-SearchBot.Claude-Userpuede buscar páginas en respuesta a solicitudes de usuarios;ChatGPT-Usertambién es activado por usuarios, pero no controla la inclusión en la búsqueda de ChatGPT. GPTBotyClaudeBotson los bots documentados para posible uso del contenido en entrenamiento. Si el usuario opta por bloquear ese rastreo, usa grupos propios conDisallow: /para ambos, sin bloquear los bots de búsqueda. Si opta por permitirlo, usaAllow: /para ambos. Consulta las dos variantes enreferences/templates.md.- Trata los controles de otros proveedores, como
Google-Extended, por separado. Permitir acceso a los bots de búsqueda aumenta la elegibilidad, pero no garantiza la citación o presencia en las respuestas. Consulta las directrices de OpenAI y de Anthropic al actualizar la lista de bots. - Apunta a
Sitemap: https://dominio.com/sitemap.xml.
- Para mantener la posibilidad de aparecer en las búsquedas de ChatGPT y Claude, permite
public/sitemap.xmlRico (Imágenes + Video):- Incluye los namespaces
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"yxmlns:video="http://www.google.com/schemas/sitemap-video/1.1"cuando haya imágenes destacadas y video de ventas/presentación.
- Incluye los namespaces
2. AEO — Answer Engine Optimization (Featured Snippets, Voz y Crawlers sin JS)
Los motores de respuesta necesitan entidades conectadas y HTML semántico legible sin ejecutar JavaScript:
- Grafo Conectado
@graphenSchema.orgJSON-LD:- En lugar de scripts JSON-LD sueltos, crea un único
<script type="application/ld+json">con@graphconectando las entidades por@id(consulta el modelo enreferences/templates.md):WebSite(#website)PersonoOrganization(#person/#org) consameAspara redes sociales oficialesWebPage(#webpage) conaboutymainEntityProduct/SoftwareApplication/Service(#product) conoffersCourse(#course) cuando haya curso/entrenamiento/bonus educativoVideoObject(#video) cuando haya VSL o video de presentaciónFAQPage(#faq) con 4 a 6 preguntas reales de alta intención de búsqueda y respuestas directas y completas (40–70 palabras cada).
- En lugar de scripts JSON-LD sueltos, crea un único
- Fallback Semántico
<noscript>para SPAs (React / Vue / Vite):- Solo aplica para SPAs sin prerenderización. En Next.js, Nuxt, Astro, Angular con SSR/prerender y HTML estático, el HTML ya llega completo y el
<noscript>es innecesario (consulta la guía de la stack). - Los crawlers de LLMs (
GPTBot,ClaudeBot,PerplexityBot,OAI-SearchBot) frecuentemente hacen fetch solo del HTML bruto y no ejecutan el bundle JavaScript. - En proyectos SPA donde
<div id="root"></div>viene vacío, agrega un bloque<noscript><main>...</main></noscript>en elindex.htmlque contenga el<h1>, resumen de la oferta, lista de entregables (<ul><li>) y enlace directo al CTA/checkout. - Importante para Rendimiento: Usa
<noscript>(y no elementos dentro de<div id="root">cuando usescreateRoot().render()), ya que colocar DOM dentro de#rootsin SSR hace que React desmonte y recrea todo el árbol en el mount, retrasando el LCP y generando TBT.
- Solo aplica para SPAs sin prerenderización. En Next.js, Nuxt, Astro, Angular con SSR/prerender y HTML estático, el HTML ya llega completo y el
3. GEO & Agentic Browsing (Nota 100 en Lighthouse 13)
Google Lighthouse 13 introdujo la categoría Agentic Browsing, que audita si la página está preparada para agentes autónomos y LLMs (llms-txt y ard-schema).
public/llms.txt+ Enlace en el<head>:- Crea
public/llms.txten Markdown estructurado (consultareferences/templates.md) conteniendo:# Nombre del Producto / Marca> Resumen en una frase de la propuesta de valor- Secciones claras:
## Sobre,## Qué Incluye,## Bonus / Condiciones,## Preguntas Frecuentes (AEO / GEO),## Enlaces Oficiales.
- En el
<head>delindex.html, agrega:<link rel="help" type="text/plain" href="/llms.txt" title="LLMs.txt — Información estructurada para IAs" />
- Crea
public/.well-known/ard.json(Agent Resource Discovery v0.91):- En SPAs (Vite, Next, etc.), las rutas inexistentes como
/.well-known/ard.jsonretornan elindex.html(<!doctype html>) por fallback, haciendo que la auditoríaard-schemade Lighthouse 13 falle con error de parse JSON. - Crea siempre el archivo
public/.well-known/ard.jsonsiguiendo el schema oficial ARD v0.91 (modelo exacto enreferences/templates.md):- Debe contener
ardVersion: "0.91.0"yentriesconidentifieren formatourn:air:<dominio>:<tipo>:<slug>,displayName,type: "application/agent-skills+md",urlapuntando a/llms.txt,descriptionyrepresentativeQueries.
- Debe contener
- En SPAs (Vite, Next, etc.), las rutas inexistentes como
4. Google Lighthouse 100/100: Rendimiento y Accesibilidad
Para alcanzar 98–100 en Mobile (4G Simulado + 4x CPU Throttling) y 100 en Desktop, aplica rigurosamente estas reglas:
4.1. LCP (Largest Contentful Paint < 2.1s en Mobile)
- Preload en la Parte Superior del
<head>:- Coloca el
<link rel="preload" as="image" ... fetchpriority="high">justo debajo del<title>(en las primeras 15 líneas delindex.html), antes de las decenas de meta tags Open Graph y del JSON-LD.
- Coloca el
- Calibración de
srcSetysizespara el DPR1.75xde Lighthouse Mobile:- Lighthouse Mobile emula un Moto G Power (
412pxde ancho condevicePixelRatio: 1.75). - Si una imagen tiene
sizes="(max-width: 767px) 260px, 500px", Chrome calcula260 * 1.75 = 455w. Si la versión mobile en elsrcSetse declara como260w(menor que455w), Chrome ignora la imagen mobile y descarga la versión de escritorio pesada. - Regla de oro: Declara la imagen mobile en el
srcSetcon un ancho>= ceil(ancho_css_mobile * 1.8)(ej.:450wpara imágenes de240px, o680wpara imágenes de360pxfull-width en mobile) y comprímela en WebP (< 30 KB).
- Lighthouse Mobile emula un Moto G Power (
- Evita que Imágenes Decorativas de Fondo "Roben" el LCP:
- Si la sección Hero tiene una foto decorativa de fondo (
opacity: 0.15) con una caja (boundingRect) más grande que el retrato principal o<h1>, Chrome la considerará el elemento LCP. - En mobile (
@media (max-width: 767px)), oculta fotos de fondo gigantes puramente decorativas (display: none) o reduce sus dimensiones para que el elemento principal (precargado confetchpriority="high") sea el único candidato a LCP.
- Si la sección Hero tiene una foto decorativa de fondo (
- No hagas Preload de Fuentes que Compiten con el JS/CSS Crítico en SPAs:
- Usa
font-display: swapen el@font-facey deja que el navegador priorice la descarga del CSS y del bundle JS inicial antes de la fuente secundaria.
- Usa
4.2. TBT Cero (0ms Total Blocking Time)
- Sustituye Animaciones Continuas de JS por CSS Compositor +
IntersectionObserver:- Las bibliotecas completas de animación en el bundle inicial añaden
50–150 KBde JS y bloquean el hilo principal en la hidratación. - Para reveals de scroll, usa
IntersectionObservernativo alternando una clase.is-visiblecontransition: opacity, transformacelerada por GPU (translate3d). - Para partículas o rotaciones continuas, usa
@keyframesen CSS contransform: translate3d(...)/rotate(...)ywill-change: transform.
- Las bibliotecas completas de animación en el bundle inicial añaden
- Cuidado con Filtros SVG (
<feTurbulence>,drop-shadow) y SMIL<animate>en la Carga:- Nunca uses
<feTurbulence>en pantalla completa sin dimensiones fijas (recalcula ruido Perlin en millones de píxeles en la CPU, generando Long Tasks de300ms+). Prefiereradial-gradientligero en CSS. - Nunca combines
filter: drop-shadow(...)en un elemento padre cuyo SVG hijo sufre mutación de geometría en cada frame durante la carga inicial. Si hay morphing de<path>mediante<animate attributeName="d">, activa el morphing solo después de la primera interacción del usuario (pointermove,touchstart,wheel) o después de la carga inicial (setTimeoutde8000ms), manteniendo solo la rotación CSS en la GPU durante la carga.
- Nunca uses
- Code-Splitting de Smooth Scroll (
Lenis):- Carga
Lenisdinámicamente (await import("lenis")) solo en el primer eventowheel,touchstartokeydown({ passive: true, once: true }). Así pesa0 KBy0msde CPU en la carga inicial.
- Carga
- Aísla Timers (
setInterval/ Contadores de Urgencia) en Componentes Hoja:- Nunca coloques un hook
useCountdown(setIntervalde 1s) en el componente raíz<App />. Esto fuerza a React a re-renderizar la página completa cada segundo durante la prueba de Lighthouse. Aísla el estado del reloj en un pequeño componente<CountdownClock />que renderice solo los dígitos.
- Nunca coloques un hook
4.3. Accesibilidad Nota 100 (WCAG AA)
- Contraste de Colores (
color-contrast):- Textos pequeños sobre fondo oscuro (
#10100f/#171512) necesitan una relación de contraste>= 4.5:1. Usa tonos claros para textos secundarios (#c4beb4,#d6c5b2) y naranjas luminosos para destacados sobre fondo oscuro (#ff9452,#ffa36b). - Botones con fondo naranja vibrante (
#ff772f) deben usar texto oscuro (#140c06) para contraste máximo.
- Textos pequeños sobre fondo oscuro (
- Consistencia entre Texto Visible y Nombre Accesible (
label-content-name-mismatch):- Nunca pongas un
aria-labelen un enlace/botón que no contenga exactamente el texto visible dentro de él (ej.: si el logo diceCF ZANELLA., no pongasaria-label="Inicio").
- Nunca pongas un
- Semántica y Teclado:
- Incluye
<a class="skip-link" href="#contenido">Saltar al contenido</a>como primer foco del<body>. - Usa jerarquía estricta de títulos (
h1->h2->h3),aria-labelledbyen todas las<section>, yaria-presseden selectores de estado.
- Incluye
5. Validación Automatizada con Google Lighthouse
Siempre valida los cambios ejecutando el script automatizado del skill:
# Proyecto local: detecta la stack, ejecuta `npm run build` (si existe) e inicia la vista previa en el puerto 4173
node <skill>/scripts/run-lighthouse.mjs
# Saltar el build (cuando la salida del build ya está actualizada@azure)...#
[El contenido se ha truncado para la traducción]