diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a3f9c6be..f2a08cf9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,6 +20,11 @@ permissions: pages: write id-token: write +# Used by VitePress config for og:url, canonical URLs, and sitemap hostname. +# VitePress reads process.env.SITE_URL; defaults to localhost for local dev. +env: + SITE_URL: https://www.fontist.org + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index c39c7890..3d3a7339 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -19,6 +19,10 @@ permissions: contents: read pull-requests: write +# Match docs.yml — VitePress config reads SITE_URL for og:url / canonical / sitemap. +env: + SITE_URL: https://www.fontist.org + jobs: link_checker: runs-on: ubuntu-latest diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 0883afd8..30ff7fd7 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,5 +1,12 @@ import { defineConfig } from "vitepress"; +// Allow override via env (CI sets SITE_URL=https://www.fontist.org). +// Default to localhost so `vitepress dev` and local `npm run build` produce +// URLs that actually resolve during testing. +const SITE_ORIGIN = process.env.SITE_URL || "http://localhost:5173"; +const SITE_PATH = process.env.BASE_PATH || "/formulas/"; +const SITE_BASE = `${SITE_ORIGIN}${SITE_PATH}`; + // https://vitepress.dev/reference/site-config export default defineConfig({ lang: "en-US", @@ -16,6 +23,8 @@ export default defineConfig({ "testing-google-import.md", "GOOGLE_FONTS_IMPLEMENTATION.md", "TODO.revamp.md", + "TODO.style.md", + "V5_MIGRATION_PLAN.md", ], ignoreDeadLinks: true, @@ -85,18 +94,48 @@ export default defineConfig({ ], ["link", { rel: "manifest", href: "/site.webmanifest" }], ["meta", { property: "og:type", content: "website" }], - ["meta", { property: "og:title", content: "Fontist Formulas" }], - [ - "meta", - { - property: "og:description", - content: "Searchable index of all Fontist Formulas", - }, - ], - ["meta", { property: "og:image", content: "/logo-full.svg" }], + ["meta", { property: "og:image", content: `${SITE_BASE}logo-full.svg` }], ["meta", { name: "twitter:card", content: "summary_large_image" }], ], + // Generates /sitemap.xml at build time so crawlers can discover all + // 4,283 formula pages even though /browse/ renders its list client-side. + // VitePress sitemap doesn't auto-include the `base` path (/formulas/), + // so transformItems prepends SITE_PATH to every URL. + sitemap: { + hostname: SITE_ORIGIN, + transformItems(items) { + return items.map((item) => ({ + ...item, + url: `${SITE_PATH}${item.url}`.replace(/\/{2,}/g, "/"), + })); + }, + }, + + // Per-page SEO tags (og:title, og:description, og:url, canonical URL) + // sourced from page frontmatter. Global head only sets site-wide og:type + // and og:image — title/description/url must be per-page or scrapers see + // generic "Fontist Formulas" for every page. + transformHead(context) { + const pageData = context.pageData; + const title = + (pageData.frontmatter.title as string) || "Fontist Formulas"; + const description = + (pageData.frontmatter.description as string) || + "Searchable index of all Fontist Formulas"; + const canonicalPath = pageData.relativePath + .replace(/(index)?\.md$/, "") + .replace(/\\/g, "/"); + const canonicalURL = `${SITE_BASE}${canonicalPath}`; + + return [ + ["meta", { property: "og:title", content: title }], + ["meta", { property: "og:description", content: description }], + ["meta", { property: "og:url", content: canonicalURL }], + ["link", { rel: "canonical", href: canonicalURL }], + ]; + }, + themeConfig: { logo: "/logo-full.svg", siteTitle: false, diff --git a/docs/public/robots.txt b/docs/public/robots.txt new file mode 100644 index 00000000..550b98ed --- /dev/null +++ b/docs/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://www.fontist.org/formulas/sitemap.xml