From 7f1113ecb3fafb1eefebebeb07dca58b17583c1e Mon Sep 17 00:00:00 2001
From: Stephen Walsh <868017+stphnwlsh@users.noreply.github.com>
Date: Sat, 7 Nov 2026 10:17:56 -0600
Subject: [PATCH 1/2] Check internal links during the docs build
---
README.md | 2 +-
astro.config.mjs | 5 +
package-lock.json | 245 ++++++++++--------
package.json | 1 +
src/content/docs/brand/colour.mdx | 2 +-
src/content/docs/brand/contributing.mdx | 4 +-
src/content/docs/brand/downloads.mdx | 24 +-
src/content/docs/brand/index.mdx | 16 +-
src/content/docs/brand/kit/social.mdx | 2 +-
src/content/docs/brand/kit/tokens.mdx | 8 +-
src/content/docs/brand/logo.mdx | 2 +-
.../docs/graphql/explanation/decisions.mdx | 4 +-
src/content/docs/graphql/getting-started.mdx | 8 +-
.../docs/graphql/guides/run-locally.mdx | 2 +-
.../graphql/guides/ship-the-container.mdx | 6 +-
src/content/docs/graphql/index.mdx | 10 +-
src/content/docs/graphql/reference/schema.mdx | 2 +-
.../docs/lambda/explanation/decisions.mdx | 2 +-
src/content/docs/lambda/getting-started.mdx | 8 +-
.../docs/lambda/guides/deploy-to-aws.mdx | 2 +-
.../docs/lambda/guides/run-locally.mdx | 2 +-
src/content/docs/lambda/index.mdx | 10 +-
.../docs/lambda/reference/functions.mdx | 2 +-
.../docs/mcp/explanation/decisions.mdx | 4 +-
src/content/docs/mcp/getting-started.mdx | 8 +-
.../docs/mcp/guides/connect-a-client.mdx | 4 +-
src/content/docs/mcp/guides/run-locally.mdx | 4 +-
.../docs/mcp/guides/ship-the-container.mdx | 6 +-
src/content/docs/mcp/index.mdx | 10 +-
.../mcp/reference/resources-and-prompts.mdx | 8 +-
src/content/docs/mcp/reference/tools.mdx | 2 +-
.../docs/minimalapi/explanation/decisions.mdx | 4 +-
.../docs/minimalapi/getting-started.mdx | 8 +-
.../docs/minimalapi/guides/run-locally.mdx | 2 +-
.../minimalapi/guides/ship-the-container.mdx | 6 +-
src/content/docs/minimalapi/index.mdx | 10 +-
.../docs/minimalapi/reference/endpoints.mdx | 2 +-
.../docs/webapi/explanation/decisions.mdx | 4 +-
src/content/docs/webapi/getting-started.mdx | 8 +-
.../docs/webapi/guides/run-locally.mdx | 2 +-
.../docs/webapi/guides/ship-the-container.mdx | 6 +-
src/content/docs/webapi/index.mdx | 10 +-
.../docs/webapi/reference/endpoints.mdx | 2 +-
43 files changed, 263 insertions(+), 216 deletions(-)
diff --git a/README.md b/README.md
index 2738162..db87c1d 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ The home page is `src/pages/index.astro`, the templates it lists are in `src/dat
The template docs are built with [Starlight](https://starlight.astro.build). Each template has a folder in `src/content/docs` named after its key, and its `index.mdx` is the template's home page, the one its repository links to. Each template also gets its own sidebar, set in `astro.config.mjs`, and its own colour, which `src/components/docs/Head.astro` picks from the first part of the page's path. Same site, five outfits.
-To add a page, drop a Markdown or MDX file in the template's folder and add it to that template's sidebar.
+To add a page, drop a Markdown or MDX file in the template's folder and add it to that template's sidebar. `npm run build` checks every internal link and heading anchor across the docs and fails if one is broken.
## The brand pages
diff --git a/astro.config.mjs b/astro.config.mjs
index 14d363d..f41e878 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -1,6 +1,7 @@
// @ts-check
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
+import starlightLinksValidator from 'starlight-links-validator';
import starlightLlmsTxt from 'starlight-llms-txt';
import starlightSidebarTopics from 'starlight-sidebar-topics';
import { brand, templates } from './src/data/templates.ts';
@@ -125,6 +126,10 @@ export default defineConfig({
items: sidebars[t.id],
})),
),
+ starlightLinksValidator({
+ errorOnLocalLinks: false,
+ exclude: ['/', '/#*'],
+ }),
starlightLlmsTxt({
projectName: 'Lean Mean Clean Apps',
description:
diff --git a/package-lock.json b/package-lock.json
index a3e4588..b4f7471 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@fontsource-variable/jetbrains-mono": "5.3.0",
"astro": "7.4.4",
"sharp": "0.35.5",
+ "starlight-links-validator": "0.27.0",
"starlight-llms-txt": "0.12.0",
"starlight-sidebar-topics": "0.9.0"
},
@@ -95,9 +96,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -114,9 +112,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -133,9 +128,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -152,9 +144,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -439,9 +428,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -455,9 +441,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -471,9 +454,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -487,9 +467,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -834,9 +811,6 @@
"cpu": [
"arm"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -853,9 +827,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -872,9 +843,6 @@
"cpu": [
"ppc64"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -891,9 +859,6 @@
"cpu": [
"riscv64"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -910,9 +875,6 @@
"cpu": [
"s390x"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -929,9 +891,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -948,9 +907,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -967,9 +923,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -986,9 +939,6 @@
"cpu": [
"arm"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1011,9 +961,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1036,9 +983,6 @@
"cpu": [
"ppc64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1061,9 +1005,6 @@
"cpu": [
"riscv64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1086,9 +1027,6 @@
"cpu": [
"s390x"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1111,9 +1049,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1136,9 +1071,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1161,9 +1093,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1513,9 +1442,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1532,9 +1458,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1551,9 +1474,6 @@
"cpu": [
"ppc64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1570,9 +1490,6 @@
"cpu": [
"s390x"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1589,9 +1506,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1608,9 +1522,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1904,6 +1815,21 @@
"am-i-vibing": "dist/cli.mjs"
}
},
+ "node_modules/ansi-escapes": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+ "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -2535,6 +2461,18 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/es-module-lexer": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-3.0.4.tgz",
@@ -2718,6 +2656,18 @@
"uncrypto": "^0.1.3"
}
},
+ "node_modules/has-flag": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz",
+ "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/hast-util-embedded": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz",
@@ -3076,6 +3026,18 @@
"url": "https://github.com/sponsors/brc-dd"
}
},
+ "node_modules/is-absolute-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-5.0.0.tgz",
+ "integrity": "sha512-sdJyNpBnQHuVnBunfzjAecOhZr2+A30ywfFvu3EnxtKLUWfwGgyWUmqHbGZiU6vTfHpCPm5GvLe4BAvlU9n8VQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-alphabetical": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@@ -3329,9 +3291,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3352,9 +3311,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3375,9 +3331,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3398,9 +3351,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3665,6 +3615,30 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.1.tgz",
+ "integrity": "sha512-oXp1GIa+E9IwIs3a106+tr3Bz2MlTTBvlXjOZMoIat11kk7vjqgmBnksB9WHmWv+gZh7k3IUgpAHlwHB7DH4GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/mdast-util-phrasing": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
@@ -5121,6 +5095,29 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/starlight-links-validator": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.27.0.tgz",
+ "integrity": "sha512-UvPl8L9hfWR+yWbLOEtljLmpJCGcM6AVseVy5IO9dmqdOjW4R+Y8FaYyVA5axCC8RvxM5xU7M1WTerpwU8rYbw==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/markdown-satteri": "^0.4.0",
+ "github-slugger": "^2.0.0",
+ "hast-util-from-html": "^2.0.3",
+ "is-absolute-url": "^5.0.0",
+ "mdast-util-mdx-jsx": "^3.2.0",
+ "mdast-util-to-hast": "^13.2.1",
+ "picomatch": "^4.0.3",
+ "satteri": "^0.10.3",
+ "terminal-link": "^5.0.0",
+ "unist-util-visit": "^5.1.0",
+ "yaml": "^2.8.3"
+ },
+ "peerDependencies": {
+ "@astrojs/starlight": ">=0.42.0",
+ "astro": ">=7.2.10"
+ }
+ },
"node_modules/starlight-llms-txt": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/starlight-llms-txt/-/starlight-llms-txt-0.12.0.tgz",
@@ -5181,6 +5178,34 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/supports-color": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/supports-hyperlinks": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.6.0.tgz",
+ "integrity": "sha512-FL/q/YePS4tFFTNiBOR+zPk2ZrKheqNtmTJ/9OUZ4e1wKHJofDbGQSmSsxS0XNnabSkqexryCaXGvEKz1vf9UQ==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^5.0.1",
+ "supports-color": "^10.2.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
+ }
+ },
"node_modules/svgo": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz",
@@ -5206,6 +5231,22 @@
"url": "https://opencollective.com/svgo"
}
},
+ "node_modules/terminal-link": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-5.0.0.tgz",
+ "integrity": "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "supports-hyperlinks": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/tiny-inflate": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
diff --git a/package.json b/package.json
index 50a9a0d..4e217bf 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"@fontsource-variable/jetbrains-mono": "5.3.0",
"astro": "7.4.4",
"sharp": "0.35.5",
+ "starlight-links-validator": "0.27.0",
"starlight-llms-txt": "0.12.0",
"starlight-sidebar-topics": "0.9.0"
}
diff --git a/src/content/docs/brand/colour.mdx b/src/content/docs/brand/colour.mdx
index dba7e3d..7cb91a1 100644
--- a/src/content/docs/brand/colour.mdx
+++ b/src/content/docs/brand/colour.mdx
@@ -46,4 +46,4 @@ The core is paper on dark backgrounds and ink on light ones.
- Body text on ink is paper, and secondary text is slate 300.
- On ink, a template's 300 shade is its accent: kickers, command chips and highlights on its social preview.
-- On a docs site, a template's 400 shade is the accent in dark mode and its 700 shade in light mode. The [Starlight themes](../kit/tokens/) set this up for you.
+- On a docs site, a template's 400 shade is the accent in dark mode and its 700 shade in light mode. The [Starlight themes](/brand/kit/tokens/) set this up for you.
diff --git a/src/content/docs/brand/contributing.mdx b/src/content/docs/brand/contributing.mdx
index e056af1..f7b26fb 100644
--- a/src/content/docs/brand/contributing.mdx
+++ b/src/content/docs/brand/contributing.mdx
@@ -34,9 +34,9 @@ The build wipes `assets/` and `tokens/` and makes them again. It takes a few sec
1. Add it to `templates` in `scripts/brand.mjs`, with a hue nobody else is using. If it's one of the reserved ones, fill in the `repo`, `package` and `what` fields and drop `reserved: true`.
2. Run `npm run build`. You get its logos, web icons, social preview and Starlight accent.
-3. Add a section for it on [Logo files](../kit/logos/) and a row on [Quick downloads](../downloads/). These pages live in the [website](https://github.com/LeanMeanCleanApps/leanmeancleanapps.github.io) repository, in `src/content/docs/brand/`.
+3. Add a section for it on [Logo files](/brand/kit/logos/) and a row on [Quick downloads](/brand/downloads/). These pages live in the [website](https://github.com/LeanMeanCleanApps/leanmeancleanapps.github.io) repository, in `src/content/docs/brand/`.
-A brand new hue needs a full 50 to 900 scale in `scales`. Check it against the others on the [Templates](../templates/) page before you commit to it: two neighbours that look alike in a row of marks are the thing to avoid.
+A brand new hue needs a full 50 to 900 scale in `scales`. Check it against the others on the [Templates](/brand/templates/) page before you commit to it: two neighbours that look alike in a row of marks are the thing to avoid.
## These pages
diff --git a/src/content/docs/brand/downloads.mdx b/src/content/docs/brand/downloads.mdx
index a470420..b7219a3 100644
--- a/src/content/docs/brand/downloads.mdx
+++ b/src/content/docs/brand/downloads.mdx
@@ -3,26 +3,26 @@ title: Quick downloads
description: The logo and social files most people need, linked directly.
---
-These are the files you'll reach for most. Right-click, save, done. No sign-up, no newsletter, no cookie banner. Every other size and format is on [Logo files](../kit/logos/) and [Social templates](../kit/social/).
+These are the files you'll reach for most. Right-click, save, done. No sign-up, no newsletter, no cookie banner. Every other size and format is on [Logo files](/brand/kit/logos/) and [Social templates](/brand/kit/social/).
## Organisation
| Where it goes | File |
| --- | --- |
-| GitHub organisation avatar | [org-spectrum-square-512.png](../assets/logos/org-spectrum/png/org-spectrum-square-512.png) |
-| NuGet.org and Docker Hub avatar | [org-spectrum-square-512.png](../assets/logos/org-spectrum/png/org-spectrum-square-512.png) |
-| Organisation profile README banner | [banner.png](../assets/social/png/banner.png) |
-| Link preview for an organisation website | [og-image.png](../assets/social/png/og-image.png) |
-| Favicons for an organisation website | [favicon.svg](../assets/logos/org-spectrum/web/favicon.svg), [favicon.ico](../assets/logos/org-spectrum/web/favicon.ico), [apple-touch-icon.png](../assets/logos/org-spectrum/web/apple-touch-icon.png) |
+| GitHub organisation avatar | [org-spectrum-square-512.png](/brand/assets/logos/org-spectrum/png/org-spectrum-square-512.png) |
+| NuGet.org and Docker Hub avatar | [org-spectrum-square-512.png](/brand/assets/logos/org-spectrum/png/org-spectrum-square-512.png) |
+| Organisation profile README banner | [banner.png](/brand/assets/social/png/banner.png) |
+| Link preview for an organisation website | [og-image.png](/brand/assets/social/png/og-image.png) |
+| Favicons for an organisation website | [favicon.svg](/brand/assets/logos/org-spectrum/web/favicon.svg), [favicon.ico](/brand/assets/logos/org-spectrum/web/favicon.ico), [apple-touch-icon.png](/brand/assets/logos/org-spectrum/web/apple-touch-icon.png) |
## Templates
| Template | Repository social preview | NuGet package icon | Docs site favicon |
| --- | --- | --- | --- |
-| Minimal API | [CleanMinimalApi.png](../assets/social/png/CleanMinimalApi.png) | [minimalapi-square-128.png](../assets/logos/minimalapi/png/minimalapi-square-128.png) | [favicon.svg](../assets/logos/minimalapi/web/favicon.svg) |
-| Web API | [CleanWebApi.png](../assets/social/png/CleanWebApi.png) | [webapi-square-128.png](../assets/logos/webapi/png/webapi-square-128.png) | [favicon.svg](../assets/logos/webapi/web/favicon.svg) |
-| GraphQL API | [CleanGraphQLApi.png](../assets/social/png/CleanGraphQLApi.png) | [graphql-square-128.png](../assets/logos/graphql/png/graphql-square-128.png) | [favicon.svg](../assets/logos/graphql/web/favicon.svg) |
-| Model Context Protocol | [CleanModelContextProtocol.png](../assets/social/png/CleanModelContextProtocol.png) | [mcp-square-128.png](../assets/logos/mcp/png/mcp-square-128.png) | [favicon.svg](../assets/logos/mcp/web/favicon.svg) |
-| Lambda Function | [CleanLambdaFunction.png](../assets/social/png/CleanLambdaFunction.png) | [lambda-square-128.png](../assets/logos/lambda/png/lambda-square-128.png) | [favicon.svg](../assets/logos/lambda/web/favicon.svg) |
+| Minimal API | [CleanMinimalApi.png](/brand/assets/social/png/CleanMinimalApi.png) | [minimalapi-square-128.png](/brand/assets/logos/minimalapi/png/minimalapi-square-128.png) | [favicon.svg](/brand/assets/logos/minimalapi/web/favicon.svg) |
+| Web API | [CleanWebApi.png](/brand/assets/social/png/CleanWebApi.png) | [webapi-square-128.png](/brand/assets/logos/webapi/png/webapi-square-128.png) | [favicon.svg](/brand/assets/logos/webapi/web/favicon.svg) |
+| GraphQL API | [CleanGraphQLApi.png](/brand/assets/social/png/CleanGraphQLApi.png) | [graphql-square-128.png](/brand/assets/logos/graphql/png/graphql-square-128.png) | [favicon.svg](/brand/assets/logos/graphql/web/favicon.svg) |
+| Model Context Protocol | [CleanModelContextProtocol.png](/brand/assets/social/png/CleanModelContextProtocol.png) | [mcp-square-128.png](/brand/assets/logos/mcp/png/mcp-square-128.png) | [favicon.svg](/brand/assets/logos/mcp/web/favicon.svg) |
+| Lambda Function | [CleanLambdaFunction.png](/brand/assets/social/png/CleanLambdaFunction.png) | [lambda-square-128.png](/brand/assets/logos/lambda/png/lambda-square-128.png) | [favicon.svg](/brand/assets/logos/lambda/web/favicon.svg) |
-Not sure where a file goes? [Using the assets](../kit/platforms/) walks through each platform.
+Not sure where a file goes? [Using the assets](/brand/kit/platforms/) walks through each platform.
diff --git a/src/content/docs/brand/index.mdx b/src/content/docs/brand/index.mdx
index 6a40a30..b059987 100644
--- a/src/content/docs/brand/index.mdx
+++ b/src/content/docs/brand/index.mdx
@@ -7,10 +7,10 @@ hero:
tagline: Logos, colours, type and social templates for every Lean Mean Clean Apps template. Take what you need and put it everywhere.
actions:
- text: Grab the files
- link: downloads/
+ link: /brand/downloads/
icon: right-arrow
- text: Read the rules
- link: logo/
+ link: /brand/logo/
variant: minimal
---
@@ -26,10 +26,10 @@ Every template wears the same ring mark. The core in the middle never changes, j
## Pick your path
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/src/content/docs/brand/kit/social.mdx b/src/content/docs/brand/kit/social.mdx
index 6019896..3dec330 100644
--- a/src/content/docs/brand/kit/social.mdx
+++ b/src/content/docs/brand/kit/social.mdx
@@ -9,7 +9,7 @@ Each image comes in three formats, in `assets/social/`:
- **PNG** is what you upload.
- **SVG** has the text turned into outlines, so it looks identical everywhere, fonts or no fonts.
-- **Editable SVG** keeps live text and named layers. Install the [three typefaces](../../typography/) before you open it, or your editor will swap in something else.
+- **Editable SVG** keeps live text and named layers. Install the [three typefaces](/brand/typography/) before you open it, or your editor will swap in something else.
## Repository previews
diff --git a/src/content/docs/brand/kit/tokens.mdx b/src/content/docs/brand/kit/tokens.mdx
index bb49c75..3bbcd4d 100644
--- a/src/content/docs/brand/kit/tokens.mdx
+++ b/src/content/docs/brand/kit/tokens.mdx
@@ -3,12 +3,12 @@ title: Design tokens
description: The colours and typefaces as JSON, CSS variables and Starlight themes.
---
-The same colours and typefaces you see on [Colour](../../colour/) and [Typography](../../typography/), in files code can use. They're all in `tokens/`, generated from `scripts/brand.mjs`, so never edit them by hand.
+The same colours and typefaces you see on [Colour](/brand/colour/) and [Typography](/brand/typography/), in files code can use. They're all in `tokens/`, generated from `scripts/brand.mjs`, so never edit them by hand.
| File | What it's for |
| --- | --- |
-| [`tokens.json`](../../tokens/tokens.json) | Every colour scale, the template hues and the font stacks, in the [Design Tokens Community Group](https://www.designtokens.org) format. Style Dictionary, Figma Tokens Studio and most token tools read it. |
-| [`tokens.css`](../../tokens/tokens.css) | The same values as CSS custom properties, for any website. |
+| [`tokens.json`](/brand/tokens/tokens.json) | Every colour scale, the template hues and the font stacks, in the [Design Tokens Community Group](https://www.designtokens.org) format. Style Dictionary, Figma Tokens Studio and most token tools read it. |
+| [`tokens.css`](/brand/tokens/tokens.css) | The same values as CSS custom properties, for any website. |
| `starlight/base.css` | The shared Starlight theme: ink and paper greys, the three typefaces. |
| `starlight/.css` | One accent per template, plus `org.css` for the organisation. Pair one with `base.css`. |
@@ -54,4 +54,4 @@ Template hues are aliases, so a token tool keeps them in step with the scale the
## Starlight themes
-Each accent file sets Starlight's accent colours (and its purple, which some components use instead) from the template's scale: step 400 in dark mode and 700 in light mode, so links pass contrast on both. [Using the assets](../platforms/#docs-sites) shows the full setup.
+Each accent file sets Starlight's accent colours (and its purple, which some components use instead) from the template's scale: step 400 in dark mode and 700 in light mode, so links pass contrast on both. [Using the assets](/brand/kit/platforms/#docs-sites) shows the full setup.
diff --git a/src/content/docs/brand/logo.mdx b/src/content/docs/brand/logo.mdx
index 08c6a6a..5b9c9c3 100644
--- a/src/content/docs/brand/logo.mdx
+++ b/src/content/docs/brand/logo.mdx
@@ -43,7 +43,7 @@ Three rules cover every mark in the library:
2. **Rings alternate.** Neighbouring rings use different shades, so the rings stay distinct without relying on the gaps alone.
3. **Light on the left.** The left half of every ring is one shade lighter than the right half. Every change of colour lands on a gap, never in the middle of a segment.
-The exact shades are on the [Colour](../colour/) page.
+The exact shades are on the [Colour](/brand/colour/) page.
## The small version
diff --git a/src/content/docs/graphql/explanation/decisions.mdx b/src/content/docs/graphql/explanation/decisions.mdx
index 7b44791..c98eccd 100644
--- a/src/content/docs/graphql/explanation/decisions.mdx
+++ b/src/content/docs/graphql/explanation/decisions.mdx
@@ -44,11 +44,11 @@ Validation runs once, in one place, before any handler. Handlers only ever see v
## Authentication is in or out
-Authentication is a choice you make when you create the solution. With `--include-auth`, every query and mutation, `version` included, carries `[Authorize]` and needs a valid JWT bearer token. Without it, there's no authentication code at all, rather than code that sits there switched off. There's nothing to leave half configured, and nothing to read past when you don't need it. See [Secure the API](../../guides/secure-the-api/).
+Authentication is a choice you make when you create the solution. With `--include-auth`, every query and mutation, `version` included, carries `[Authorize]` and needs a valid JWT bearer token. Without it, there's no authentication code at all, rather than code that sits there switched off. There's nothing to leave half configured, and nothing to read past when you don't need it. See [Secure the API](/graphql/guides/secure-the-api/).
## SQLite next to the app
-The template needs a database that works in a container and on a laptop without any setup. SQLite does that, with [real caveats](../../guides/database/). It's there to show the shape of Infrastructure, not to be your production database.
+The template needs a database that works in a container and on a laptop without any setup. SQLite does that, with [real caveats](/graphql/guides/database/). It's there to show the shape of Infrastructure, not to be your production database.
## Infrastructure is internal
diff --git a/src/content/docs/graphql/getting-started.mdx b/src/content/docs/graphql/getting-started.mdx
index 65ad555..72aeaae 100644
--- a/src/content/docs/graphql/getting-started.mdx
+++ b/src/content/docs/graphql/getting-started.mdx
@@ -108,8 +108,8 @@ You need the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0). [
## Where to next
-
-
-
-
+
+
+
+
diff --git a/src/content/docs/graphql/guides/run-locally.mdx b/src/content/docs/graphql/guides/run-locally.mdx
index fa51c50..7321764 100644
--- a/src/content/docs/graphql/guides/run-locally.mdx
+++ b/src/content/docs/graphql/guides/run-locally.mdx
@@ -61,7 +61,7 @@ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:7333 dotnet run --project ./src/Pre
## Try the schema
-Nitro is the easy way: it knows the schema, autocompletes, and keeps your query history. The sample data uses a fixed seed, so these ids are the same on every machine. See the [schema reference](../../reference/schema/) for every query, mutation and type.
+Nitro is the easy way: it knows the schema, autocompletes, and keeps your query history. The sample data uses a fixed seed, so these ids are the same on every machine. See the [schema reference](/graphql/reference/schema/) for every query, mutation and type.
```graphql
# One movie, its reviews and who wrote them
diff --git a/src/content/docs/graphql/guides/ship-the-container.mdx b/src/content/docs/graphql/guides/ship-the-container.mdx
index 099ae95..630e6e8 100644
--- a/src/content/docs/graphql/guides/ship-the-container.mdx
+++ b/src/content/docs/graphql/guides/ship-the-container.mdx
@@ -16,7 +16,7 @@ docker build . --target run --tag acme-movies:1.2.3 \
--build-arg VERSION_PREFIX=1.2.3
```
-The version is stamped into the assemblies, so the `version` query tells you exactly which build is running, and so does `service.version` on every span, metric and log. See the [configuration reference](../../reference/configuration/) for every build argument.
+The version is stamped into the assemblies, so the `version` query tells you exactly which build is running, and so does `service.version` on every span, metric and log. See the [configuration reference](/graphql/reference/configuration/) for every build argument.
## Pick an environment
@@ -51,11 +51,11 @@ Both answer without a token, even when authentication is on.
## Keep the data somewhere safe
-The SQLite file lives in `/app/data`. Mount a volume there or, better, [point Infrastructure at a real database](../database/#swap-sqlite-for-something-real) before anyone relies on it. A database inside a container that gets replaced on every deploy is a database with a short memory.
+The SQLite file lives in `/app/data`. Mount a volume there or, better, [point Infrastructure at a real database](/graphql/guides/database/#swap-sqlite-for-something-real) before anyone relies on it. A database inside a container that gets replaced on every deploy is a database with a short memory.
## Turn authentication on
-In a solution created with `--include-auth`, set `Authentication__Authority` and `Authentication__Audience` as environment variables to point each environment at its own authorization server. Without `--include-auth` there's nothing to turn on yet. The [security guide](../secure-the-api/) has the details.
+In a solution created with `--include-auth`, set `Authentication__Authority` and `Authentication__Audience` as environment variables to point each environment at its own authorization server. Without `--include-auth` there's nothing to turn on yet. The [security guide](/graphql/guides/secure-the-api/) has the details.