diff --git a/.github/assets/wordmark-dark.svg b/.github/assets/wordmark-dark.svg new file mode 100644 index 00000000..d77cc4aa --- /dev/null +++ b/.github/assets/wordmark-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/.github/assets/wordmark-light.svg b/.github/assets/wordmark-light.svg new file mode 100644 index 00000000..827832d8 --- /dev/null +++ b/.github/assets/wordmark-light.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d619ed7..4ef7a54d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,4 +23,4 @@ permissions: jobs: check: name: CI - uses: kirchDev/workflows/.github/workflows/_ci-check.yml@94cbcbd676b989ac6c6327055a240132f5cd76a5 # v0.8.0 + uses: kirchDev/workflows/.github/workflows/_ci-check.yml@1e57db95ff601f03ae87b78e329ef238f9d0cbf6 # v0.8.2 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6022683f..44775274 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,4 +28,4 @@ jobs: packages: read actions: read contents: read - uses: kirchDev/workflows/.github/workflows/_codeql.yml@94cbcbd676b989ac6c6327055a240132f5cd76a5 # v0.8.0 + uses: kirchDev/workflows/.github/workflows/_codeql.yml@1e57db95ff601f03ae87b78e329ef238f9d0cbf6 # v0.8.2 diff --git a/.github/workflows/fast-forward-queue.yml b/.github/workflows/fast-forward-queue.yml index 8467a90a..068acec3 100644 --- a/.github/workflows/fast-forward-queue.yml +++ b/.github/workflows/fast-forward-queue.yml @@ -34,7 +34,7 @@ jobs: pull-requests: read checks: read statuses: read - uses: kirchDev/workflows/.github/workflows/_fast-forward-queue.yml@94cbcbd676b989ac6c6327055a240132f5cd76a5 # v0.8.0 + uses: kirchDev/workflows/.github/workflows/_fast-forward-queue.yml@1e57db95ff601f03ae87b78e329ef238f9d0cbf6 # v0.8.2 with: pr: ${{ inputs.pr }} secrets: diff --git a/.github/workflows/promotion-pr.yml b/.github/workflows/promotion-pr.yml index ee23edba..a181b162 100644 --- a/.github/workflows/promotion-pr.yml +++ b/.github/workflows/promotion-pr.yml @@ -18,4 +18,4 @@ permissions: jobs: promotion-pr: name: Promotion PR - uses: kirchDev/workflows/.github/workflows/_promotion-pr.yml@94cbcbd676b989ac6c6327055a240132f5cd76a5 # v0.8.0 + uses: kirchDev/workflows/.github/workflows/_promotion-pr.yml@1e57db95ff601f03ae87b78e329ef238f9d0cbf6 # v0.8.2 diff --git a/.github/workflows/queue-branch.yml b/.github/workflows/queue-branch.yml index 17a67919..05d8d827 100644 --- a/.github/workflows/queue-branch.yml +++ b/.github/workflows/queue-branch.yml @@ -20,6 +20,6 @@ jobs: permissions: contents: read pull-requests: write - uses: kirchDev/workflows/.github/workflows/_queue-branch.yml@94cbcbd676b989ac6c6327055a240132f5cd76a5 # v0.8.0 + uses: kirchDev/workflows/.github/workflows/_queue-branch.yml@1e57db95ff601f03ae87b78e329ef238f9d0cbf6 # v0.8.2 secrets: BWS_ACCESS_TOKEN: ${{ secrets.BWS_ACCESS_TOKEN }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 0ba2f4b7..43546d60 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -15,6 +15,6 @@ jobs: permissions: contents: write pull-requests: write - uses: kirchDev/workflows/.github/workflows/_release-please.yml@94cbcbd676b989ac6c6327055a240132f5cd76a5 # v0.8.0 + uses: kirchDev/workflows/.github/workflows/_release-please.yml@1e57db95ff601f03ae87b78e329ef238f9d0cbf6 # v0.8.2 secrets: BWS_ACCESS_TOKEN: ${{ secrets.BWS_ACCESS_TOKEN }} diff --git a/.gitignore b/.gitignore index c0168f0b..d3ebd3dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # region gitignore-sync -# stacks: core, agents, git, node, nuxt, vscode, intellij, macos, windows, linux +# stacks: core, agents, git, node, nuxt, dotenv, vscode, intellij, macos, windows, linux # ───────────────────────────────────────── # region core@v1 @@ -43,6 +43,14 @@ coverage .data # endregion +# region dotenv@v1 +.env +.env.* +!.env.example +!.env.*.example +!.env.testing +# endregion + # region vscode@v1 .vscode/* !.vscode/extensions.json diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 16488145..35515e1b 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -13,6 +13,7 @@ "CLAUDE.md", "AGENTS.md", "docs/**/*.md", - "www/content/**/*.md" + "www/content/**/*.md", + "public/devtools/*.html" ] } diff --git a/.tituskirch-skills.json b/.tituskirch-skills.json index 155cdba5..c3c6872f 100644 --- a/.tituskirch-skills.json +++ b/.tituskirch-skills.json @@ -48,7 +48,9 @@ } }, "docs": { - "preset": "package" + "preset": "package", + "render": "duxt", + "locales": ["de", "es", "fr", "pt"] }, "work": { "tracker": "github", diff --git a/README.md b/README.md index b73b0aab..a0d2f05c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@
-# 📚 duxt + + + duxt + **Versioned, multi-repo documentation for Nuxt — one line to extend, no collection boilerplate** @@ -118,6 +121,18 @@ pnpm check # lint + format + typecheck + tests + policy parity + build + a11 The repo root **is** the layer — `nuxt.config.ts`, `content.config.ts`, `app/`, `modules/` and `server/` live there, and `package.json` points at them. `www/` beside it is the site that consumes the layer, and the development target: it deliberately carries the awkward cases — two repositories, four refs, one version of each lifecycle. It is not a template; the exemplary starting point lives in [`kirchDev/duxt-starter`](https://github.com/kirchDev/duxt-starter). +## 🎨 Assets & branding + +The mark is the package name with its first letter bracketed — `[d]uxt` — because that is how the package is written where it is used: `extends: ['@kirchdev/duxt']`, an array with one entry. It is set in [IBM Plex Mono](https://github.com/IBM/plex) SemiBold and converted to outlines, so no asset depends on the font being installed anywhere. + +> [!NOTE] +> The wordmark is **AI-assisted placeholder artwork** — a stand-in to be replaced at some point, with no fixed timeline. No image generator was involved: it is typeset, not drawn. + +Every asset, the colour values, why the icon's brackets are redrawn and the font licensing are in [Conventions → Branding](https://duxt.app/conventions/branding). + +> [!IMPORTANT] +> The layer ships **no** branding. `duxt.logo` is unset by default, so `DuxtBrand` falls back to the consumer's own `duxt.title` beside a generic icon: a site extending duxt shows its own name in the header and footer and its own icon in the tab, never this one. These assets belong to this repository and to `www/`, not to the published package. + ## 🤝 Contributing PRs welcome. Conventional Commits are enforced via commitlint, and husky runs the linters on `git commit`. Branch off `dev`. diff --git a/app/app.vue b/app/app.vue index b23ef520..bb2b683d 100644 --- a/app/app.vue +++ b/app/app.vue @@ -59,6 +59,56 @@ if (baseUrl) { const localeHead = useLocaleHead({ dir: false, lang: false, seo: true }); useHead(() => localeHead.value); } + +/** + * `og:locale`, which nuxt-seo-utils does not derive from i18n. + * + * Open Graph spells a locale with an underscore — `de_DE`, not `de-DE` — and + * the alternates are the OTHER locales this site serves, so a share card comes + * back in the reader's language where the network offers the choice. Only where + * the site has more than one; a single-locale site listing no alternates is + * correct, not incomplete. + */ +const ogLocale = (code: string) => code.replace('-', '_'); + +useSeoMeta({ + ogSiteName: () => duxt.title, + ogLocale: () => ogLocale(locale.value), + ogLocaleAlternate: () => + locales.value + .map((entry) => (typeof entry === 'string' ? entry : entry.code)) + .filter((code) => code !== locale.value) + .map(ogLocale) +}); + +/** + * WHAT THE SITE IS, once, for every page under it. + * + * nuxt-schema-org keeps one `@graph` per document and merges into it, so this + * node and the per-page `Article` in `[...slug].vue` end up in the same script + * tag with the references between them resolved — which is the whole reason the + * hand-written JSON-LD that used to live there could go. + * + * The `Organization` is conditional and stays that way: duxt renders somebody + * else's documentation and must not name a publisher it invented. A consumer + * fills in `duxt.organization`; until then the site describes itself and no + * more. + */ +useSchemaOrg([ + defineWebSite({ + name: duxt.title, + inLanguage: locale.value + }), + ...(duxt.organization?.name + ? [ + defineOrganization({ + name: duxt.organization.name, + url: duxt.organization.url, + logo: duxt.organization.logo + }) + ] + : []) +]);