/aeo-geo-bestpractices — SEO + AEO + GEO + Lighthouse 100 Playbook
Workflow completo para transformar qualquer página ou SPA em uma máquina de descoberta orgânica (SEO), motores de resposta (AEO) e motores generativos/agentes de IA (GEO), garantindo simultaneamente 98–100 no Google Lighthouse 13 (Desktop e Mobile) nas 5 categorias: Performance, Accessibility, Best Practices, SEO e Agentic Browsing.
Caminhos: <skill> é a pasta onde este SKILL.md está localizado.
0. Alinhamento Rápido Antes de Executar
0.1. Detecte a stack e leia o guia correspondente
As regras deste arquivo valem para qualquer site. O que muda entre frameworks (onde fica o <head>, a pasta pública, como pré-renderizar, como otimizar imagens e evitar TBT, qual comando sobe o preview) está em references/frameworks/. Identifique a stack pelo projeto e leia o guia antes de aplicar as etapas 1 a 5:
| Sinal no projeto | Stack | Guia |
|---|---|---|
angular.json ou @angular/core | Angular | references/frameworks/angular.md |
next.config.* ou next | Next.js | references/frameworks/nextjs.md |
nuxt.config.* ou nuxt / vite.config.* + vue | Nuxt / Vue + Vite | references/frameworks/vue-nuxt.md |
astro.config.* ou astro | Astro | references/frameworks/astro.md |
vite.config.* + react | Vite + React | references/frameworks/vite-react.md |
index.html sem framework | HTML estático | references/frameworks/html-estatico.md |
Se a stack não estiver na lista (ex.: SvelteKit, Remix, Gatsby, WordPress), aplique as regras gerais deste arquivo e use o guia mais parecido como referência: frameworks com SSR/SSG seguem a lógica de Next/Nuxt; SPAs seguem a de Vite + React.
0.2. Confirme com o usuário
Antes de alterar o código, confirme (ou extraia do próprio projeto se já estiver explícito):
- Domínio canônico de produção (ex.:
https://meusite.com.br/). Se ainda não houver domínio definido, pergunte ou use um placeholder claro e avise o usuário. - FAQ Visível vs. Invisível (apenas AEO/GEO):
- Pergunte se o usuário quer a seção de Perguntas Frequentes (FAQ) visível no layout da página (
<details>/<summary>) ou apenas invisível nos bastidores (dentro do@graphJSON-LD +llms.txt+<noscript>) para não alterar o design visual.
- Pergunte se o usuário quer a seção de Perguntas Frequentes (FAQ) visível no layout da página (
- Preferência de rastreamento para treino (ao criar ou alterar
robots.txt) :- Verifique se o site já definiu uma preferência. Se não houver uma escolha clara, ofereça duas opções: permitir busca e bloquear o rastreamento para treino da OpenAI e da Anthropic; ou permitir também esse rastreamento para treino. Preserve as demais regras do site.
1. SEO Técnico Completo (index.html, robots.txt, sitemap.xml)
Consulte os templates prontos em references/templates.md.
- Meta Tags Essenciais no
<head>:<html lang="pt-BR">(ou idioma do projeto).<title>forte (50–65 caracteres) com Nome da Marca/Especialista + Proposta Principal.<meta name="description">(140–165 caracteres) respondendo claramente o que é, para quem é e o que inclui.<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="pt-BR" ... />ex-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) e Twitter Cards (summary_large_image).
public/robots.txtAmigável para Buscadores e IAs :- Para manter a possibilidade de aparecer nas buscas do ChatGPT e do Claude, permita
OAI-SearchBoteClaude-SearchBot.Claude-Userpode buscar páginas em resposta a pedidos de usuários;ChatGPT-Usertambém é acionado por usuários, mas não controla a inclusão na busca do ChatGPT. GPTBoteClaudeBotsão os bots documentados para possível uso do conteúdo em treino. Se o usuário optar por bloquear esse rastreamento, use grupos próprios comDisallow: /para ambos, sem bloquear os bots de busca. Se optar por permiti-lo, useAllow: /para ambos. Veja as duas variantes emreferences/templates.md.- Trate controles de outros provedores, como
Google-Extended, separadamente. Permitir acesso aos bots de busca aumenta a elegibilidade, mas não garante citação ou presença nas respostas. Consulte as orientações da OpenAI e da Anthropic ao atualizar a lista de bots. - Aponte
Sitemap: https://dominio.com/sitemap.xml.
- Para manter a possibilidade de aparecer nas buscas do ChatGPT e do Claude, permita
public/sitemap.xmlRico (Imagens + Vídeo) :- Inclua os namespaces
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"exmlns:video="http://www.google.com/schemas/sitemap-video/1.1"quando houver imagens de destaque e vídeo de vendas/apresentação.
- Inclua os namespaces
2. AEO — Answer Engine Optimization (Featured Snippets, Voz e Crawlers sem JS)
Motores de resposta precisam de entidades conectadas e HTML semântico legível sem executar JavaScript:
- Grafo Conectado
@graphemSchema.orgJSON-LD:- Em vez de scripts JSON-LD soltos, crie um único
<script type="application/ld+json">com@graphconectando as entidades por@id(veja o modelo emreferences/templates.md):WebSite(#website)PersonouOrganization(#person/#org) comsameAspara redes sociais oficiaisWebPage(#webpage) comaboutemainEntityProduct/SoftwareApplication/Service(#product) comoffersCourse(#course) quando houver curso/treinamento/bônus educacionalVideoObject(#video) quando houver VSL ou vídeo de apresentaçãoFAQPage(#faq) com 4 a 6 perguntas reais de alta intenção de busca e respostas diretas e completas (40–70 palavras cada).
- Em vez de scripts JSON-LD soltos, crie um único
- Fallback Semântico
<noscript>para SPAs (React / Vue / Vite) :- Só se aplica a SPAs sem pré-renderização. Em Next.js, Nuxt, Astro, Angular com SSR/prerender e HTML estático, o HTML já chega completo e o
<noscript>é desnecessário (veja o guia da stack). - Crawlers de LLMs (
GPTBot,ClaudeBot,PerplexityBot,OAI-SearchBot) frequentemente fazem fetch apenas do HTML bruto e não executam o bundle JavaScript. - Em projetos SPA onde
<div id="root"></div>vem vazio, adicione um bloco<noscript><main>...</main></noscript>noindex.htmlcontendo o<h1>, resumo da oferta, lista de entregáveis (<ul><li>) e link direto para o CTA/checkout. - Importante para Performance: Use
<noscript>(e não elementos dentro de<div id="root">quando usarcreateRoot().render()), pois colocar DOM dentro de#rootsem SSR faz o React desmontar e recriar toda a árvore no mount, atrasando o LCP e gerando TBT.
- Só se aplica a SPAs sem pré-renderização. Em Next.js, Nuxt, Astro, Angular com SSR/prerender e HTML estático, o HTML já chega completo e o
3. GEO & Agentic Browsing (Nota 100 no Lighthouse 13)
O Google Lighthouse 13 introduziu a categoria Agentic Browsing, que audita se a página está preparada para agentes autônomos e LLMs (llms-txt e ard-schema).
public/llms.txt+ Link no<head>:- Crie
public/llms.txtem Markdown estruturado (vejareferences/templates.md) contendo:# Nome do Produto / Marca> Resumo em uma frase da proposta de valor- Seções claras:
## Sobre,## O Que Está Incluído,## Bônus / Condições,## Perguntas Frequentes (AEO / GEO),## Links Oficiais.
- No
<head>doindex.html, adicione:<link rel="help" type="text/plain" href="/llms.txt" title="LLMs.txt — Informações estruturadas para IAs" />
- Crie
public/.well-known/ard.json(Agent Resource Discovery v0.91) :- Em SPAs (Vite, Next, etc.), rotas inexistentes como
/.well-known/ard.jsonretornam oindex.html(<!doctype html>) por fallback, fazendo a auditoriaard-schemado Lighthouse 13 falhar com erro de parse JSON. - Crie sempre o arquivo
public/.well-known/ard.jsonseguindo o schema oficial ARD v0.91 (modelo exato emreferences/templates.md):- Deve conter
ardVersion: "0.91.0"eentriescomidentifierno formatourn:air:<dominio>:<tipo>:<slug>,displayName,type: "application/agent-skills+md",urlapontando para/llms.txt,descriptionerepresentativeQueries.
- Deve conter
- Em SPAs (Vite, Next, etc.), rotas inexistentes como
4. Google Lighthouse 100/100: Performance & Acessibilidade
Para atingir 98–100 no Mobile (4G Simulado + 4x CPU Throttling) e 100 no Desktop, aplique rigorosamente estas regras:
4.1. LCP (Largest Contentful Paint < 2.1s no Mobile)
- Preload no Topo do
<head>:- Coloque o
<link rel="preload" as="image" ... fetchpriority="high">logo abaixo do<title>(nas primeiras 15 linhas doindex.html), antes das dezenas de meta tags Open Graph e do JSON-LD.
- Coloque o
- Calibragem de
srcSetesizespara o DPR1.75xdo Lighthouse Mobile:- O Lighthouse Mobile emula um Moto G Power (
412pxde largura comdevicePixelRatio: 1.75). - Se uma imagem tiver
sizes="(max-width: 767px) 260px, 500px", o Chrome calcula260 * 1.75 = 455w. Se a versão mobile nosrcSetfor declarada como260w(menor que455w), o Chrome ignora a imagem mobile e baixa a versão desktop pesada! - Regra de ouro: Declare a imagem mobile no
srcSetcom largura>= ceil(largura_css_mobile * 1.8)(ex.:450wpara imagens de240px, ou680wpara imagens de360pxfull-width no mobile) e comprima em WebP (< 30 KB).
- O Lighthouse Mobile emula um Moto G Power (
- Evite que Imagens Decorativas de Fundo "Roubem" o LCP:
- Se a seção Hero tiver uma foto decorativa de fundo (
opacity: 0.15) com caixa (boundingRect) maior que o retrato principal ou<h1>, o Chrome vai considerá-la o elemento LCP. - No mobile (
@media (max-width: 767px)), oculte fotos de fundo gigantes puramente decorativas (display: none) ou reduza suas dimensões para que o elemento principal (pré-carregado comfetchpriority="high") seja o único candidato a LCP.
- Se a seção Hero tiver uma foto decorativa de fundo (
- Não Faça Preload de Fontes que Competem com o JS/CSS Crítico em SPAs:
- Use
font-display: swapno@font-facee deixe o navegador priorizar o download do CSS e do bundle JS inicial antes da fonte secundária.
- Use
4.2. TBT Zero (0ms Total Blocking Time)
- Substitua Animações Contínuas de JS por CSS Compositor +
IntersectionObserver:- Bibliotecas completas de animação no bundle inicial adicionam
50–150 KBde JS e bloqueiam a main thread na hidratação. - Para reveals de scroll, use
IntersectionObservernativo alternando uma classe.is-visiblecomtransition: opacity, transformacelerada por GPU (translate3d). - Para partículas ou rotações contínuas, use
@keyframesem CSS comtransform: translate3d(...)/rotate(...)ewill-change: transform.
- Bibliotecas completas de animação no bundle inicial adicionam
- Cuidado com Filtros SVG (
<feTurbulence>,drop-shadow) e SMIL<animate>no Load:- Nunca use
<feTurbulence>em tela cheia sem dimensões fixas (ele recalcula ruído Perlin em milhões de pixels na CPU, gerando Long Tasks de300ms+). Prefiraradial-gradientleve em CSS. - Nunca combine
filter: drop-shadow(...)em um elemento pai cujo SVG filho sofre mutação de geometria a cada frame no carregamento inicial. Se houver morphing de<path>via<animate attributeName="d">, ative o morphing apenas após a primeira interação do usuário (pointermove,touchstart,wheel) ou após o carregamento inicial (setTimeoutde8000ms), mantendo apenas a rotação CSS na GPU durante o load.
- Nunca use
- Code-Splitting de Smooth Scroll (
Lenis) :- Carregue o
Lenisdinamicamente (await import("lenis")) apenas no primeiro eventowheel,touchstartoukeydown({ passive: true, once: true }). Assim ele pesa0 KBe0msde CPU no carregamento inicial.
- Carregue o
- Isole Timers (
setInterval/ Contadores de Urgência) em Componentes Folha:- Nunca coloque um hook
useCountdown(setIntervalde 1s) no componente raiz<App />. Isso força o React a re-renderizar a página inteira a cada segundo durante o teste do Lighthouse. Isole o estado do relógio em um pequeno componente<CountdownClock />que renderiza apenas os dígitos.
- Nunca coloque um hook
4.3. Acessibilidade Nota 100 (WCAG AA)
- Contraste de Cores (
color-contrast) :- Textos pequenos sobre fundo escuro (
#10100f/#171512) precisam de razão de contraste>= 4.5:1. Use tons claros para textos secundários (#c4beb4,#d6c5b2) e laranjas luminosos para destaques sobre fundo escuro (#ff9452,#ffa36b). - Botões com fundo laranja vibrante (
#ff772f) devem usar texto escuro (#140c06) para contraste máximo.
- Textos pequenos sobre fundo escuro (
- Consistência entre Texto Visível e Nome Acessível (
label-content-name-mismatch) :- Nunca coloque um
aria-labelem um link/botão que não contenha exatamente o texto visível dentro dele (ex.: se a logo dizCF ZANELLA., não coloquearia-label="Início").
- Nunca coloque um
- Semântica e Teclado:
- Inclua
<a class="skip-link" href="#conteudo">Pular para o conteúdo</a>como primeiro foco do<body>. - Use hierarquia estrita de títulos (
h1->h2->h3),aria-labelledbyem todas as<section>, earia-pressedem seletores de estado.
- Inclua
5. Validação Automatizada com Google Lighthouse
Sempre valide as alterações rodando o script automatizado da skill:
# Projeto local: detecta a stack, roda `npm run build` (se existir) e sobe o preview na porta 4173
node <skill>/scripts/run-lighthouse.mjs
# Pular o build (quando a saída do build já está atualizada
…
[truncated for translation]