/aeo-geo-bestpractices – SEO + AEO + GEO + Lighthouse 100 Playbook
Vollständiger Workflow, um jede Seite oder SPA in eine Maschine für organische Auffindbarkeit (SEO), Antwortmaschinen (AEO) und generative KI-Motoren/Agenten (GEO) zu verwandeln und gleichzeitig 98–100 in Google Lighthouse 13 (Desktop und Mobil) in den 5 Kategorien zu erreichen: Performance, Accessibility, Best Practices, SEO und Agentic Browsing.
Pfade: <skill> ist der Ordner, in dem sich diese SKILL.md befindet.
0. Schnelle Abstimmung vor der Ausführung
0.1. Erkenne den Stack und lies die entsprechende Anleitung
Die Regeln dieser Datei gelten für jede Website. Was sich zwischen den Frameworks unterscheidet (wo sich <head>, der öffentliche Ordner befindet, wie vorgerendert wird, wie Bilder optimiert und TBT vermieden werden, welcher Befehl die Vorschau startet), findest du in references/frameworks/. Identifiziere den Stack anhand des Projekts und lies die Anleitung, bevor du die Schritte 1 bis 5 anwendest:
| Signal im Projekt | Stack | Anleitung |
|---|---|---|
angular.json oder @angular/core | Angular | references/frameworks/angular.md |
next.config.* oder next | Next.js | references/frameworks/nextjs.md |
nuxt.config.* oder nuxt / vite.config.* + vue | Nuxt / Vue + Vite | references/frameworks/vue-nuxt.md |
astro.config.* oder astro | Astro | references/frameworks/astro.md |
vite.config.* + react | Vite + React | references/frameworks/vite-react.md |
index.html ohne Framework | Statisches HTML | references/frameworks/html-estatico.md |
Wenn der Stack nicht in der Liste steht (z. B. SvelteKit, Remix, Gatsby, WordPress), wende die allgemeinen Regeln dieser Datei an und verwende die ähnlichste Anleitung als Referenz: Frameworks mit SSR/SSG folgen der Logik von Next/Nuxt; SPAs folgen der von Vite + React.
0.2. Bestätige mit dem Benutzer
Bevor du den Code änderst, bestätige (oder extrahiere aus dem Projekt, wenn es bereits explizit ist):
- Kanonische Produktionsdomäne (z. B.
https://meinewebseite.de/). Wenn noch keine Domäne definiert ist, frage nach oder verwende einen klaren Platzhalter und informiere den Benutzer. - Sichtbare vs. unsichtbare FAQ (nur AEO/GEO):
- Frage, ob der Benutzer den Abschnitt Häufig gestellte Fragen (FAQ) sichtbar im Seitenlayout (
<details>/<summary>) oder nur unsichtbar im Hintergrund (innerhalb des@graphJSON-LD +llms.txt+<noscript>) haben möchte, um das visuelle Design nicht zu verändern.
- Frage, ob der Benutzer den Abschnitt Häufig gestellte Fragen (FAQ) sichtbar im Seitenlayout (
- Präferenz für Tracking zum Training (beim Erstellen oder Ändern von
robots.txt):- Überprüfe, ob die Website bereits eine Präferenz festgelegt hat. Wenn keine klare Wahl getroffen wurde, biete zwei Optionen an: Suche zulassen und Crawling zum Training von OpenAI und Anthropic blockieren; oder dieses Crawling ebenfalls zum Training zulassen. Bewahre die übrigen Regeln der Website.
1. Vollständiges technisches SEO (index.html, robots.txt, sitemap.xml)
Konsultiere die fertigen Vorlagen in references/templates.md.
- Essentielle Meta-Tags im
<head>:<html lang="de">(oder Sprache des Projekts).- Starker
<title>(50–65 Zeichen) mit Marken-/Expertenname + Hauptangebot. <meta name="description">(140–165 Zeichen), die klar beantwortet, was es ist, für wen es ist und was es beinhaltet.<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" /><link rel="canonical" href="https://domain.de/" />+<link rel="alternate" hreflang="de" ... />undx-default.- Vollständiges Open Graph (
og:type,og:locale,og:site_name,og:title,og:description,og:url,og:image,og:image:width,og:image:height,og:image:alt) und Twitter Cards (summary_large_image).
public/robots.txtsuchmaschinen- und KI-freundlich:- Um die Möglichkeit zu erhalten, in den Suchergebnissen von ChatGPT und Claude angezeigt zu werden, erlaube
OAI-SearchBotundClaude-SearchBot.Claude-Userkann Seiten als Antwort auf Benutzeranfragen durchsuchen;ChatGPT-Userwird ebenfalls von Benutzern aktiviert, kontrolliert aber nicht die Aufnahme in die ChatGPT-Suche. GPTBotundClaudeBotsind die dokumentierten Bots für die mögliche Nutzung von Inhalten zum Training. Wenn der Benutzer sich dafür entscheidet, dieses Crawling zu blockieren, verwende eigene Gruppen mitDisallow: /für beide, ohne die Such-Bots zu blockieren. Wenn er es zulassen möchte, verwendeAllow: /für beide. Siehe die beiden Varianten inreferences/templates.md.- Behandle die Steuerung anderer Anbieter, wie
Google-Extended, separat. Das Zulassen von Zugriff für Such-Bots erhöht die Eignung, garantiert aber keine Zitierung oder Präsenz in den Antworten. Konsultiere die Richtlinien von OpenAI und Anthropic bei der Aktualisierung der Bot-Liste. - Verweise auf
Sitemap: https://domain.de/sitemap.xml.
- Um die Möglichkeit zu erhalten, in den Suchergebnissen von ChatGPT und Claude angezeigt zu werden, erlaube
public/sitemap.xmlreichhaltig (Bilder + Video):- Füge die Namespaces
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"undxmlns:video="http://www.google.com/schemas/sitemap-video/1.1"hinzu, wenn es hervorgehobene Bilder und Verkaufs-/Präsentationsvideos gibt.
- Füge die Namespaces
2. AEO – Answer Engine Optimization (Featured Snippets, Sprachsuche und Crawler ohne JS)
Antwortmaschinen benötigen verbundene Entitäten und semantisches HTML, das ohne Ausführung von JavaScript lesbar ist:
- Verbundener
@graphinSchema.orgJSON-LD:- Anstatt einzelner JSON-LD-Skripte erstelle ein einziges
<script type="application/ld+json">mit@graph, das die Entitäten über@idverbindet (siehe Modell inreferences/templates.md):WebSite(#website)PersonoderOrganization(#person/#org) mitsameAsfür offizielle soziale NetzwerkeWebPage(#webpage) mitaboutundmainEntityProduct/SoftwareApplication/Service(#product) mitoffersCourse(#course) bei Kursen/Trainings/ Bildungs-BonusVideoObject(#video) bei VSL oder PräsentationsvideoFAQPage(#faq) mit 4 bis 6 echten Fragen mit hohem Suchintention und direkten, vollständigen Antworten (jeweils 40–70 Wörter).
- Anstatt einzelner JSON-LD-Skripte erstelle ein einziges
<noscript>-Fallback für SPAs (React / Vue / Vite):- Gilt nur für SPAs ohne Vorab-Rendering. Bei Next.js, Nuxt, Astro, Angular mit SSR/Prerender und statischem HTML kommt das HTML bereits vollständig an und
<noscript>ist unnötig (siehe Stack-Anleitung). - Crawler von LLMs (
GPTBot,ClaudeBot,PerplexityBot,OAI-SearchBot) holen oft nur das rohe HTML und führen das JavaScript-Bundle nicht aus. - In SPA-Projekten, bei denen
<div id="root"></div>leer kommt, füge einen<noscript><main>...</main></noscript>-Block imindex.htmlhinzu, der<h1>, eine Zusammenfassung des Angebots, eine Liste der Leistungen (<ul><li>) und einen direkten Link zum CTA/Checkout enthält. - Wichtig für die Performance: Verwende
<noscript>(und keine Elemente innerhalb von<div id="root">bei Verwendung voncreateRoot().render()), da das Einfügen von DOM in#rootohne SSR dazu führt, dass React die gesamte Baumstruktur beim Mounten demontiert und neu erstellt, was LCP verzögert und TBT verursacht.
- Gilt nur für SPAs ohne Vorab-Rendering. Bei Next.js, Nuxt, Astro, Angular mit SSR/Prerender und statischem HTML kommt das HTML bereits vollständig an und
3. GEO & Agentic Browsing (Punktzahl 100 in Lighthouse 13)
Google Lighthouse 13 hat die Kategorie Agentic Browsing eingeführt, die prüft, ob die Seite für autonome Agenten und LLMs vorbereitet ist (llms-txt und ard-schema).
public/llms.txt+ Link im<head>:- Erstelle
public/llms.txtin strukturiertem Markdown (siehereferences/templates.md) mit:# Produktname / Marke> Zusammenfassung des Wertversprechens in einem Satz- Klare Abschnitte:
## Über,## Was ist enthalten,## Bonus / Bedingungen,## Häufig gestellte Fragen (AEO / GEO),## Offizielle Links.
- Füge im
<head>desindex.htmlhinzu:<link rel="help" type="text/plain" href="/llms.txt" title="LLMs.txt – Strukturierte Informationen für KI" />
- Erstelle
public/.well-known/ard.json(Agent Resource Discovery v0.91):- Bei SPAs (Vite, Next usw.) geben nicht existierende Routen wie
/.well-known/ard.jsondasindex.html(<!doctype html>) als Fallback zurück, was dazu führt, dass dieard-schema-Prüfung von Lighthouse 13 mit einem JSON-Parse-Fehler fehlschlägt. - Erstelle immer die Datei
public/.well-known/ard.jsongemäß dem offiziellen ARD-Schema v0.91 (genaues Modell inreferences/templates.md):- Sie muss
ardVersion: "0.91.0"undentriesmitidentifierim Formaturn:air:<domäne>:<typ>:<slug>,displayName,type: "application/agent-skills+md",urlverweisend auf/llms.txt,descriptionundrepresentativeQueriesenthalten.
- Sie muss
- Bei SPAs (Vite, Next usw.) geben nicht existierende Routen wie
4. Google Lighthouse 100/100: Performance & Barrierefreiheit
Um 98–100 auf Mobilgeräten (simuliertes 4G + 4x CPU-Throttling) und 100 auf dem Desktop zu erreichen, wende diese Regeln strikt an:
4.1. LCP (Largest Contentful Paint < 2,1s auf Mobilgeräten)
- Preload im oberen Bereich des
<head>:- Platziere
<link rel="preload" as="image" ... fetchpriority="high">direkt unterhalb des<title>(in den ersten 15 Zeilen desindex.html), vor den Dutzenden von Open-Graph-Meta-Tags und dem JSON-LD.
- Platziere
- Kalibrierung von
srcSetundsizesfür den DPR1,75xvon Lighthouse Mobile:- Lighthouse Mobile emuliert ein Moto G Power (
412pxBreite mitdevicePixelRatio: 1,75). - Wenn ein Bild
sizes="(max-width: 767px) 260px, 500px"hat, berechnet Chrome260 * 1.75 = 455w. Wenn die mobile Version imsrcSetmit260wdeklariert ist (kleiner als455w), ignoriert Chrome das mobile Bild und lädt die schwere Desktop-Version herunter! - Goldene Regel: Deklariere das mobile Bild im
srcSetmit einer Breite>= ceil(css_mobile_breite * 1,8)(z. B.450wfür Bilder von240pxoder680wfür Bilder von360pxin voller Breite auf Mobilgeräten) und komprimiere es in WebP (< 30 KB).
- Lighthouse Mobile emuliert ein Moto G Power (
- Vermeide, dass dekorative Hintergrundbilder den LCP "stehlen":
- Wenn der Hero-Bereich ein dekoratives Hintergrundfoto (
opacity: 0,15) mit einer Box (boundingRect) hat, die größer ist als das Hauptporträt oder<h1>, betrachtet Chrome dies als LCP-Element. - Auf Mobilgeräten (
@media (max-width: 767px)) blende riesige, rein dekorative Hintergrundfotos aus (display: none) oder reduziere ihre Abmessungen, sodass das Hauptelement (mitfetchpriority="high"vorgeladen) der einzige LCP-Kandidat ist.
- Wenn der Hero-Bereich ein dekoratives Hintergrundfoto (
- Vermeide Preload von Schriftarten, die mit dem kritischen JS/CSS in SPAs konkurrieren:
- Verwende
font-display: swapim@font-faceund lasse den Browser den Download von CSS und des anfänglichen JS-Bundles vor der sekundären Schriftart priorisieren.
- Verwende
4.2. TBT Null (0ms Total Blocking Time)
- Ersetze kontinuierliche JS-Animationen durch CSS-Compositor +
IntersectionObserver:- Vollständige Animationsbibliotheken im anfänglichen Bundle fügen
50–150 KBJS hinzu und blockieren den Hauptthread bei der Hydrierung. - Verwende für Scroll-Reveals den nativen
IntersectionObserver, der eine Klasse.is-visiblemittransition: opacity, transform(GPU-beschleunigt viatranslate3d) umschaltet. - Verwende für Partikel oder kontinuierliche Drehungen
@keyframesin CSS mittransform: translate3d(...)/rotate(...)undwill-change: transform.
- Vollständige Animationsbibliotheken im anfänglichen Bundle fügen
- Vorsicht mit SVG-Filtern (
<feTurbulence>,drop-shadow) und SMIL<animate>beim Laden:- Verwende
<feTurbulence>niemals im Vollbildmodus ohne feste Abmessungen (es berechnet Perlin-Rauschen auf Millionen von Pixeln auf der CPU neu, was Long Tasks von300ms+erzeugt). Bevorzuge leichtesradial-gradientin CSS. - Kombiniere
filter: drop-shadow(...)niemals mit einem übergeordneten Element, dessen untergeordnetes SVG beim anfänglichen Laden in jedem Frame seine Geometrie ändert. Wenn es ein Morphing von<path>via<animate attributeName="d">gibt, aktiviere das Morphing erst nach der ersten Benutzerinteraktion (pointermove,touchstart,wheel) oder nach dem anfänglichen Laden (setTimeoutvon8000ms) und behalte nur die CSS-Drehung auf der GPU während des Ladens bei.
- Verwende
- Code-Splitting von Smooth Scroll (
Lenis):- Lade
Lenisdynamisch (await import("lenis")) nur beim erstenwheel-,touchstart- oderkeydown-Event ({ passive: true, once: true }). Dadurch wiegt es0 KBund verursacht0msCPU beim anfänglichen Laden.
- Lade
- Isoliere Timer (
setInterval/ Dringlichkeitszähler) in Blattkomponenten:- Setze niemals einen Hook
useCountdown(setIntervalvon 1s) in die Root-Komponente<App />. Dies zwingt React, die gesamte Seite während des Lighthouse-Tests jede Sekunde neu zu rendern. Isoliere den Uhrenstatus in einer kleinen Komponente<CountdownClock />, die nur die Ziffern rendert.
- Setze niemals einen Hook
4.3. Barrierefreiheit Punktzahl 100 (WCAG AA)
- Farbkontrast (
color-contrast):- Kleine Texte auf dunklem Hintergrund (
#10100f/#171512) benötigen ein Kontrastverhältnis von>= 4,5:1. Verwende helle Farbtöne für sekundäre Texte (#c4beb4,#d6c5b2) und leuchtende Orange für Hervorhebungen auf dunklem Hintergrund (#ff9452,#ffa36b). - Schaltflächen mit leuchtend orangefarbenem Hintergrund (
#ff772f) sollten dunkle Schrift (#140c06) für maximalen Kontrast verwenden.
- Kleine Texte auf dunklem Hintergrund (
- Konsistenz zwischen sichtbarem Text und zugänglichem Namen (
label-content-name-mismatch):- Verwende niemals ein
aria-labelfür einen Link/Button, der nicht genau den sichtbaren Text enthält (z. B. wenn das LogoCF ZANELLA.sagt, verwende keinaria-label="Startseite").
- Verwende niemals ein
- Semantik und Tastatur:
- Füge
<a class="skip-link" href="#inhalt">Zum Inhalt springen</a>als ersten Fokus im<body>hinzu. - Verwende eine strenge Hierarchie von Überschriften (
h1->h2->h3),aria-labelledbyauf allen<section>undaria-pressedfür Statusauswähler.
- Füge
5. Automatisierte Validierung mit Google Lighthouse
Validiere die Änderungen immer, indem du das automatisierte Skill-Skript ausführst:
# Lokales Projekt: Erkennt den Stack, führt `npm run build` aus (falls vorhanden) und startet die Vorschau auf Port 4173
node <skill>/scripts/run-lighthouse.mjs
# Build überspringen (wenn die Build-Ausgabe bereits aktuell ist)
…