For developers
How the documentation stays current
Docs as code: where the pages live, how they are built, why every PR must touch them, and how 'last updated' is computed.
Where the pages live
apps/docs/content/<language>/<section>/<page>.md, one tree per language: sv (default and complete), en, no, da, fi. The same folders and file names in every language. A page missing in a language is shown from sv with a "not translated yet" notice, so a link is never broken and a new language can grow page by page. sections.json per language gives the section names.
Frontmatter:
---
title: "Page title"
description: "One sentence shown in search and cards."
order: 2
updated: 2026-09-05
keywords: [words, that, help, search]
---
The sections and their order are in apps/docs/content/<language>/sections.json. A new folder without an entry there lands last in the navigation with the folder name as title.
Language in the address
Swedish is at the root (/frakt/sendify), other languages have a prefix (/en/frakt/sendify, /no/...). The language switcher in the header switches language on the same page, and every page declares hreflang for all languages so search engines show the right version.
The build
apps/docs/scripts/build-content.mjs runs before next build and generates navigation, HTML per page, table of contents, search index and changelog. Markdown is rendered with marked; tables, code blocks and quotes (used as notices) are supported.
Last updated
Per page: the latest git commit that touched the file. The deploy workflow fetches the full history so the date is exact. In environments with shallow history (Vercel Preview) updated: in the frontmatter is used, then the build time. The home page and footer show the latest update across the whole documentation.
Changelog
/changelog is built from merge commits to main: PR number, title (with type such as New, Fixed) and area from the commit prefix.
The rule: no product change without docs
The Docs required workflow stops a pull request that changes admin, storefront, designer, platform components, migrations or edge functions without touching apps/docs/content/**. It suggests which pages are probably affected. The label docs-not-needed on the PR, or [no-docs] in the title, lets pure refactors through.
Publishing
Docs deploy runs on every push to main: builds the content with full git history and publishes to the Vercel project bunstack-docs (docs.bunstack.co). If the secrets VERCEL_TOKEN and VERCEL_DOCS_PROJECT_ID are missing the site is built but not published, with a notice in the job.