agentleFS
karerokarero/croftweaver/skills/website-seo-geo/SKILL.md

website-seo-geo

Skill9 starsChanged 31 days agoMITView on GitHub

The on-page SEO + GEO contract for a new site: exact metadata limits (title 50–60 chars, meta description 140–160 with a 120 floor), og:title/og:description == title/desc, twitter inherits OG, clean canonical == og:url, required JSON-LD schema, and an llms.txt index of the site. Use when building or reviewing a page's head / metadata (step 6 of the new-website pipeline), or when a title/description is too long or tags are out of sync. Enforced by the seo.spec.ts check in the new-website test suite. Delegates depth to ai-seo (GEO) and schema-markup. Trigger phrases: "SEO metadata", "title too long", "meta description length", "og tags", "twitter card", "canonical", "is my SEO set up", "llms.txt", "GEO", "head tags", "structured data on this page".

  • Installs packages

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If Barrierefreiheitsprüfung (24 chars alone) doesn't leave room for anything else, reach for a shorter near-synonym or split brand/category …

    line 51
  2. Very specific:

    Every page: WebSite + Organization (once, site-wide) and a WebPage node.

    line 97
  3. Very specific:

    Add page-type schema via schema-markup: BreadcrumbList on nested pages, FAQPage on FAQs, Article/TechArticle with author @id + dates on …

    line 98
  4. Distinctive:

    After fixing a live PDF, re-submit its URL in Bing/Google so it recrawls.

    line 153

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Website SEO & GEO

The metadata contract every page must satisfy. This skill owns the exact limits and consistency rules; it hands depth off to the existing global skills: ai-seo (AI answer-engine / GEO strategy), schema-markup (rich JSON-LD), seo-audit (technical SEO / crawl / Core Web Vitals — see also website-qa).

The limit table (enforced)

TagIdealHard capWhy
<title>50–60 chars~60Google truncates ~580–600px; >60 risks a cut, >70 almost always cut
<meta description>140–160 chars~160Truncates ~920px; >160 cut; <120 wastes space / keyword room
og:title / og:description= <title> / = description—Test fails on any divergence
twitter:title / twitter:descriptioninherit OG (same strings)—twitter:card = summary_large_image

The test counts characters (em-dash, en-dash, curly apostrophe each = 1 char but render wider — stay clear of the cap). <meta name="description"> is separate from og:description; one does not replace the other — ship both, in sync.

German (and other compounding languages) eat the budget faster

The limits above are pixel-render caps, not word counts — they don't change per language, and the numbers in the table stay exactly as specified. But German compound nouns (Suchmaschinenoptimierung, Barrierefreiheit, Barrierefreiheitsprüfung) pack far more meaning per character into a single unbroken word than English does, so the same 50–60 / 140–160 char cap holds noticeably less content once translated. Write for it deliberately:

  • Front-load the key term. German syntax tends to push the important noun toward the end of a compound or clause; if you translate an English title 1:1 the keyword can land past where truncation cuts. Put the primary keyword/term first, then qualify it, rather than burying it in a trailing compound.
  • When one natural compound blows the budget, don't force it — restructure. If Barrierefreiheitsprüfung (24 chars alone) doesn't leave room for anything else, reach for a shorter near-synonym or split brand/category from descriptor with a colon or dash: Website-Builder: barrierefrei & SEO-optimiert reads better and fits, where one giant compound noun forces an awkward truncation or abbreviation.
  • Budget for ~10–15% fewer effective words of meaning than an English title/description of the same character count. Plan the German copy's content around that from the start — don't write the English version, translate it, and then trim words until the character count fits; that's how you end up with a mangled abbreviation instead of a deliberate, readable title.
  • Target the LOWER half of the title range (~50–55 chars) for German. Google truncates by pixel width (~580px), not characters — German capitalizes every noun, and capitals plus umlauts render wide, so a passing 60-char German title can still be cut in the SERP. The 60-char cap is a ceiling, not a safe harbor.

This is a writing tactic, not a test change: seo.spec.ts enforces the same character counts regardless of language, and it should — the fix here is how you write within that budget, not the budget itself.

Consistency rules

  • One pair of inputs (title, description) drives <title>, OG, Twitter, canonical and the WebPage schema — wired once in the new-website skill's templates/astro/src/layouts/Base.astro, so pages only pass title/description and the tags can never drift.
  • Canonical is clean (no .html), absolute, and equals og:url and the page's sitemap.xml <loc>.
  • No trailing slash (convention): URLs are /about, not /about/ (home stays /). The starter sets trailingSlash: 'never' + build.format: 'file'; write internal links without a trailing slash. navigation.spec.ts flags any /about/ link (404/redirect hop) and seo.spec.ts pins canonical == SITE.url + path, so the whole site stays consistent.
  • Exactly one <h1> per page (its text MAY differ from the keyword-tuned title).
  • ASCII slugs only: transliterate German umlauts/ß to digraphs (/ueber-uns, not /über-uns) — machine-serialized URLs (canonical, sitemap) percent-encode non-ASCII while hand-written strings (PAGES, llms.txt, authored links) stay raw, a mismatch factory (site-architecture has the full rule).
  • og:locale derives from <html lang> (de → de_DE; regioned tags → base+region; one shared ogLocaleFor() in src/config.ts, emitted by Base.astro, asserted by seo.spec.ts whenever a value derives — mapped bases and all regioned tags) — a German page silently carrying og:locale en_US is exactly the drift this row exists to stop.

Schema (JSON-LD) — minimum per page

  • Every page: WebSite + Organization (once, site-wide) and a WebPage node.
  • Add page-type schema via schema-markup: BreadcrumbList on nested pages, FAQPage on FAQs, Article/TechArticle with author @id + dates on posts, Service/Product as relevant. Author/Organization @ids and sameAs feed EEAT (see website-content-guide); keep sameAs URLs that actually resolve.
  • FAQPage describes the Q&A to machines, not a Google rich result. Google stopped showing FAQ rich results on 7 May 2026 and removed their documentation on 15 June 2026 (Google, removal); no site gets one now. Don't promise one.
  • Datetimes in JSON-LD carry an explicit timezone. VideoObject.uploadDate, Event.startDate/endDate, Offer.validFrom, Article dates etc. must be full ISO-8601 with an offset (2026-03-11T00:00:00+00:00, not a bare 2026-03-11). Google's video parser rejects a date-only uploadDate as "missing a timezone" / "invalid datetime value" — a GSC error that only surfaces weeks after launch.
  • Validate with Google Rich Results Test / schema.org validator before launch.

GEO (generative engine optimization)

  • Ship /llms.txt (template in the starters): a curated, accurate index of key pages + facts, for AI tools and people that read it. Ship it because it is cheap: no crawler page of OpenAI, Perplexity or Anthropic says it reads the file, and Google says Google Search doesn't use it but keeping one for other services is fine (Google). Write it in the site's primary content language (German site → German descriptions; multilingual site → each entry's description in that page's language) — same precedent as the privacy page: an answer engine quoting your English blurb to a German searcher is the wrong-language failure again. Keep it in sync with the site — route coverage is enforced both ways in the new-website starter suite: tests/llms-coverage.spec.ts fails if any PAGES route is missing from it, or if a same-site entry goes stale (page removed/renamed). Add the page's - [Title](URL): description line in the same commit as the page; deliberately-hidden pages (paid-ad landers) go in its LLMS_EXEMPT with a reason. On a site that predates the guard, copy the spec in from the starter before relying on it.
  • For deeper GEO/AEO (being cited by ChatGPT/Perplexity/AI Overviews, answer formatting, entity coverage), run ai-seo — do not duplicate it here.

PDFs you host (the doc-title IS the search-result title)

A PDF has no <title> — Google and Bing print its embedded doc-title as the search-result title. Authoring tools ship a placeholder ("PowerPoint-Präsentation", "Microsoft Word - report.docx") or a working-filename slug, which Bing flags as "page title too short" (seen in production on a real site built with this kit, 2026-06-19). So any PDF you put in public/ needs a descriptive doc-title set before it ships:

  • Title convention: the same human title the page uses for that document — e.g. Talk Title — Speaker | Brand. ≥15 chars, never the authoring placeholder.
  • Set it in BOTH places crawlers read — the Info dict /Title and the XMP dc:title. Fixing only one leaves the stale title visible. Use the helper:
    python3 scripts/set_pdf_title.py public/<file>.pdf "Descriptive Title | Brand" --author "Author"
    (needs pip install pypdf; local authoring tool only — the test below is dep-free.)
  • Enforced by seo.spec.ts: the PDF guard scans public/**/*.pdf and fails the build on a placeholder, a title under 15 chars, or no doc-title at all. No-op for sites that host no PDFs.
  • After fixing a live PDF, re-submit its URL in Bing/Google so it recrawls.

Done means

npm test green — specifically seo.spec.ts: every title/description within limits, og/twitter/canonical in sync, JSON-LD parses, a sitemap exists, and every hosted PDF carries a descriptive doc-title (not an authoring placeholder).

Judgment pass (what the tests cannot check)

seo.spec.ts proves the metadata is well-formed; it cannot tell you it is good. Once it is green, do a one-page judgment read — this is a model call (Rule 5: use the model only where code can't answer):

  • Title matches search intent, not just ≤60 chars — would a searcher click it over its SERP neighbours?
  • Description earns the click — answers the query, not keyword-stuffed.
  • Schema is semantically right — correct @type and real relationships, not merely valid JSON (a valid-but-wrong @type still passes the test).
  • llms.txt is accurate vs the live site — facts and wording current, no drift (route coverage is the mechanical half, pinned by tests/llms-coverage.spec.ts; this read is about the wording half).
  • EEAT signals resolve — sameAs / author URLs actually load and corroborate.

Intent and semantics only. Structural correctness is the test's job; cross-file route/URL/config/SEO consistency is the website-review (Double-Knuth) skill's job.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.