Skip to content

feat(docs): sitemap, canonical, Open Graph and structured data - #77

Merged
rodriabregu merged 1 commit into
mainfrom
feat/docs-seo
Aug 29, 2026
Merged

rodriabregu merged 1 commit into
mainfrom
feat/docs-seo

Conversation

@rodriabregu

Copy link
Copy Markdown
Owner

What & why

The site was already fully prerendered, so Google could read it — but nothing told a search engine how to discover, canonicalise or describe it. There was no sitemap, no canonical tag, no Open Graph and no structured data, and the ~60 component pages all carried titles (Button — Sve·UI) that competed for nothing.

This adds the technical SEO layer and puts the keyword into the titles that have the most to gain from it.

Review path

Start at apps/docs/src/lib/seo/ — three small files that everything else consumes:

  • site.ts — the only place an absolute URL is spelled out.
  • Seo.svelte — the single component that emits every tag, so they cannot drift page to page.
  • schema.ts — the schema.org builders.

Then src/routes/sitemap.xml/+server.ts (same generated-from-the-registry pattern as the existing /llms.txt) and DocPage.svelte, where one edit reaches all 64 documentation pages.

Intentionally out of scope: per-page OG images. Sixty generated PNGs is not worth the build cost yet. Off-site work (Search Console, sveltesociety.dev, awesome-svelte) is where the actual ranking fight is, and it is not code.

Changes

File Change
src/lib/seo/site.ts Canonical site identity — one source of truth for every absolute URL
src/lib/seo/Seo.svelte Emits title, description, canonical, Open Graph, Twitter card, JSON-LD
src/lib/seo/schema.ts WebSite, SoftwareApplication, TechArticle, BreadcrumbList, ItemList
src/routes/sitemap.xml/+server.ts Prerendered sitemap generated from the registry — 67 URLs
static/robots.txt Allow: / plus the Sitemap: line
static/og.png 1200×630 social card (committed; see below)
scripts/gen-og.mjs Regenerates that card via pnpm gen:og
src/app.html theme-color for light/dark, apple-touch-icon
src/lib/docs/DocPage.svelte New kind and seoTitle props — one edit covers 64 pages
+page.svelte (home, components, playground, 4 guides) Adopt <Seo>, keyword-bearing titles

Titles

Route Before After
/ Sve·UI — Styled, accessible Svelte 5 components. Zero config. Sve·UI — Svelte UI Component Library for Svelte 5
/components Components — Sve·UI Svelte UI Components — 60 Accessible Components — Sve·UI
/components/* Button — Sve·UI Svelte Button Component — Sve·UI
/docs/theming Theming — Sve·UI Theming Svelte Components with CSS Variables — Sve·UI

The <h1> on each component page stays plain (Button). The <title> already carries the term, and the docs should not read like a keyword list.

Two notes for the reviewer

gen:og is not part of build. The Vercel builder has no Chromium, so the PNG is generated locally and committed. Run pnpm gen:og after changing the tagline, the count or the branding.

gen-og.mjs strips comments before counting ready: true. The registry's own header documents that field in prose; without stripping, the public image would have claimed 61 components while check-docs-coverage reports 60.

Test plan

  • pnpm test — 644 tests across 72 files, run with --force (not from cache)
  • pnpm lint && pnpm check && pnpm build all green; pnpm format:check clean
  • pnpm --filter docs check:render — 67 pages structurally unchanged
  • Verified against the built HTML in .vercel/output/static/: exactly one canonical, one og:image and one JSON-LD block per page, and the sitemap's 60 component URLs match the 60 route directories exactly
  • Screenshot baselines untouched — visual.spec.ts captures .preview__canvas only, not the hero or the index

Checklist

  • Tests added/updated and passing (pnpm test)
  • pnpm lint && pnpm check && pnpm build all green
  • Changeset — not applicable: apps/docs is private and no library code changed
  • Docs updated if the public API changed — no public API change
  • No Tailwind / config required in consumer projects

The site was already fully prerendered, so its content was crawlable — but
nothing told a search engine how to discover, canonicalise or describe it.

- `/sitemap.xml`, generated from the component registry and guide nav exactly
  like `/llms.txt`, so a new component page is discoverable the moment it is
  registered. 67 URLs; `ready: false` entries are excluded because they 404.
- `robots.txt` now points at it.
- A single `<Seo>` component emits title, description, canonical, Open Graph,
  Twitter card and JSON-LD, so the tags cannot drift page to page.
- JSON-LD: WebSite and SoftwareApplication on the landing, TechArticle and
  BreadcrumbList on every doc page, ItemList on the component index.
- `static/og.png` (1200x630), generated by `pnpm gen:og` with the Playwright
  Chromium already installed for the visual suite. Deliberately NOT part of
  `build` — the Vercel builder has no browser — so the PNG is committed.
- Component pages move from `Button — Sve·UI` to `Svelte Button Component —
  Sve·UI`. Sixty pages that competed for nothing now each carry their own
  long-tail query. The `<h1>` stays plain: the title already carries the term
  and the docs should not read like a keyword list.

`gen-og.mjs` strips comments before counting `ready: true` — the registry's own
header documents that field in prose and would have put 61 on a public image
the site itself contradicts.
@vercel

vercel Bot commented Aug 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sve-ui Ready Ready Preview Aug 29, 2026 8:08pm

@rodriabregu
rodriabregu merged commit a63ddf8 into main Aug 29, 2026
3 checks passed
@rodriabregu
rodriabregu deleted the feat/docs-seo branch August 29, 2026 20:13

This branch was successfully deployed

1 active deployment
Preview — 19144c3c Deployed Aug 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant