Docs Site — This Site
demostatics-docs is the repository that produces the site you are reading right now. This page is the maintenance guide for it: how to run it, how a Markdown file becomes a page, what you can use inside a page, and what is currently broken or missing.
What it is
Section titled “What it is”A static documentation site built with Astro and the Starlight documentation theme. There is no server, no database and no runtime backend — pnpm build emits plain HTML, CSS and JavaScript into ./dist.
Status: Shipped. The site builds and the pages listed in the sidebar render.
Pinned versions
Section titled “Pinned versions”demostatics-docs/package.json declares caret ranges; demostatics-docs/pnpm-lock.yaml pins the exact resolutions. The lockfile is committed, so these are the versions you get.
| Package | Range in package.json | Resolved in lockfile |
|---|---|---|
astro | ^5.6.1 | 5.12.5 |
@astrojs/starlight | ^0.35.2 | 0.35.2 |
sharp | ^0.34.2 | 0.34.3 |
typescript (dev) | ^5.8.3 | 5.8.3 |
@astrojs/language-server (dev) | ^2.15.4 | 2.15.4 |
Those five are the only declared dependencies. Several packages that matter for authoring arrive transitively as dependencies of Starlight itself and are not listed in package.json:
| Package | Version | What it gives you |
|---|---|---|
@astrojs/mdx | 4.3.1 | .mdx support, auto-registered by Starlight |
@astrojs/sitemap | 3.4.2 | Sitemap generation, auto-registered by Starlight |
astro-expressive-code | 0.41.3 | Code block titles, frames and line markers |
pagefind | 1.3.0 | The search index, generated at build time |
node_modules/ is not committed (demostatics-docs/.gitignore excludes it), so a fresh clone must run pnpm install before anything else works.
Commands
Section titled “Commands”Run all of these from the repository root.
| Command | What it does |
|---|---|
pnpm install | Installs dependencies from the committed lockfile. Required on a fresh clone. |
pnpm dev | Dev server with hot reload on http://localhost:4321. |
pnpm build | Production build into ./dist. Also the only thing that generates the Pagefind search index. |
pnpm preview | Serves the already-built ./dist. Run pnpm build first. |
pnpm astro sync | Regenerates content-collection types into .astro/. Run it after changing src/content.config.ts or when the editor reports unknown frontmatter types. |
Search is a build-time artifact. Pagefind runs at the end of pnpm build and writes dist/pagefind/, so the search box has nothing to query under pnpm dev. To test search, run pnpm build then pnpm preview.
On pnpm 10 the install prints Ignored build scripts: esbuild, sharp. — pnpm 10 blocks postinstall scripts by default. The build still completed in testing, because no page currently puts an image through Astro’s image pipeline. If you add images and sharp fails, run pnpm approve-builds.
How pages become routes
Section titled “How pages become routes”Every .md or .mdx file under src/content/docs/ becomes a route derived from its path relative to that directory, with the extension dropped.
src/content/docs/index.md -> /src/content/docs/participations.md -> /participations/src/content/docs/components/docs-site.md -> /components/docs-site/src/content/docs/forum/forum-rules.md -> /forum/forum-rules/The root index.md maps to the empty slug. That is why its sidebar entry is written slug: "" rather than slug: "index".
The sidebar is manual
Section titled “The sidebar is manual”demostatics-docs/astro.config.mjs lists every sidebar entry by hand. There is no autogenerate anywhere in the active configuration. Creating a file gives you a working URL but no navigation — you must add the file and the sidebar entry.
Sidebar entries come in these shapes:
{ label: "Homepage", link: "https://demostatics.com" } // external or arbitrary URL{ label: "Participations", slug: "participations" } // internal page, by slug{ label: "Forum", items: [ /* nested entries */ ] } // group{ label: "Reference", autogenerate: { directory: "reference" } } // supported, unused hereAn entry can carry a badge, which is how this site marks build status in the navigation:
{ label: "Worker Tier", slug: "components/worker", badge: { text: "Not built", variant: "caution" },}Badge variants are note, tip, caution, danger, success and default.
A slug that matches no page is a hard build error, not a silent omission:
[AstroUserError] The slug `"does/not/exist"` specified in the Starlight sidebarconfig does not exist.That is a good property — a typo in the sidebar fails the build rather than shipping a dead link. Note the asymmetry, which was tested: autogenerate pointed at a directory that does not exist does not error, it just contributes nothing.
Frontmatter schema
Section titled “Frontmatter schema”demostatics-docs/src/content.config.ts calls docsSchema() with no extend, so the accepted frontmatter is exactly Starlight’s stock schema. Unknown keys are silently stripped rather than rejected — Zod’s default is to discard them, so a misspelled key fails nothing and simply does nothing.
| Field | Type | Default | Notes |
|---|---|---|---|
title | string | — | Required. Rendered as the page’s H1. |
description | string | — | Used for <meta name="description"> and og:description. |
template | doc | splash | doc | splash is a wide, sidebar-free landing layout. |
hero | object | — | Hero block (title, tagline, image, actions). Normally paired with template: splash. |
sidebar.order | number | — | Ascending sort key inside a group. Only affects autogenerated groups. |
sidebar.label | string | page title | Overrides the label used in navigation. |
sidebar.hidden | boolean | false | Excludes the page from autogenerated groups. |
sidebar.badge | string | object | — | Variants: note, tip, caution, danger, success, default. |
sidebar.attrs | object | — | Raw HTML attributes on the sidebar link. |
tableOfContents | object | false | { minHeadingLevel: 2, maxHeadingLevel: 3 } | Set false to remove the right-hand ToC on that page. |
banner.content | string (HTML) | — | Announcement bar at the top of the page. |
lastUpdated | date | boolean | — | lastUpdated: true is set globally, so the date comes from git history. Set this field to override or to false to hide it on one page. |
prev / next | boolean | string | object | — | Overrides the pagination links. |
pagefind | boolean | true | Set false to keep a page out of the search index. |
draft | boolean | false | Visible in pnpm dev, excluded from production builds. |
head | array | [] | Extra <head> tags for this page only. |
editUrl | string | boolean | true | Inert today: no editLink.baseUrl is configured, so no edit link is rendered anywhere. |
sidebar.order and sidebar.hidden are worth calling out: because this site’s sidebar is entirely manual, both fields currently do nothing. Ordering is whatever order you write in astro.config.mjs.
To add project-specific frontmatter, extend the schema rather than inventing keys:
import { defineCollection } from 'astro:content';import { docsLoader } from '@astrojs/starlight/loaders';import { docsSchema } from '@astrojs/starlight/schema';import { z } from 'astro:content';
export const collections = { docs: defineCollection({ loader: docsLoader(), schema: docsSchema({ extend: z.object({ status: z.enum(['shipped', 'partial', 'planned', 'not-built']).optional(), }), }), }),};Run pnpm astro sync after changing that file.
Authoring features
Section titled “Authoring features”Everything below was checked against the installed Starlight 0.35.2, not against upstream documentation.
Asides
Section titled “Asides”Asides work in plain .md with no import. There are exactly four names:
:::noteNeutral information.:::
:::tip[Did you know?]The bracketed form replaces the default title.:::
:::cautionSomething the reader can get wrong.:::
:::dangerSomething destructive or irreversible.:::The transform only runs on files inside the docs collection (src/content/docs/). The same syntax in a Markdown file elsewhere stays untouched.
Components need MDX
Section titled “Components need MDX”The Starlight components — Aside, Badge, Card, CardGrid, Icon, Tabs, TabItem, LinkCard, Steps, FileTree, LinkButton, Code — are Astro components and require a .mdx file. They cannot be used in .md.
MDX is already enabled. Starlight registers @astrojs/mdx itself unless you have added it manually, so there is no need to run astro add mdx — create a .mdx file and import what you need:
---title: Example---
import { Card, CardGrid } from '@astrojs/starlight/components';
<CardGrid> <Card title="One">Body</Card></CardGrid>The docs on this site are plain .md on purpose. Prefer Markdown; reach for .mdx only when a component genuinely earns it.
Code blocks
Section titled “Code blocks”Fenced blocks are rendered by Expressive Code. Available options, all confirmed working:
```ts title="src/thing.ts" {2} ins={3} del={4} "const"```| Option | Effect |
|---|---|
title="src/thing.ts" | Filename shown in the block’s header bar. |
frame="terminal" | Terminal chrome. Also frame="code" and frame="none". |
{1,3-4} | Highlights those lines. |
ins={3} / del={4} | Diff-style inserted and deleted line markers. |
"search text" | Highlights every occurrence of that string. |
Not available in this install, because the plugins are not present: showLineNumbers, collapsible sections and twoslash. Only the frames, shiki and text-markers plugins are installed. showLineNumbers is silently ignored — the build passes and you simply get no line numbers.
Always tag the language. Use text for ASCII diagrams and console output that is not a runnable command.
Headings and the table of contents
Section titled “Headings and the table of contents”Headings h2 through h6 get automatic ids from github-slugger, and Starlight adds an anchor link to each. ## Heading Two — With Punctuation! becomes #heading-two--with-punctuation.
Starlight renders a right-hand table of contents automatically, covering h2 and h3 only by default. Deeper headings get ids and anchors but do not appear in it.
Capability summary
Section titled “Capability summary”| Capability | Status | Notes |
|---|---|---|
| Markdown pages, sidebar, routing | Shipped | Working today. |
| Asides, code block decoration, auto ToC | Shipped | Verified against 0.35.2. |
| Pagefind search | Shipped | Build-time only; nothing to search under pnpm dev. |
| MDX and Starlight components | Shipped | Enabled but unused by any current page. |
| Sitemap and canonical URLs | Shipped | site is set, so sitemap-index.xml is emitted and canonical / og:url are populated. |
astro check type checking | Not built | @astrojs/check is not a dependency. |
| Mermaid diagrams | Not built | Needs a third-party plugin and a browser. |
| CI and deployment | Not built | No configuration of any kind in the repository. |
The site value is https://docs.demostatics.com. That is where the config says the site will live; nothing in this repository publishes it there. Change the value if the real host differs, because every canonical URL and every sitemap entry is derived from it.
House style for this site
Section titled “House style for this site”These docs are written to a fixed style. Follow it when you add or edit a page.
- Frontmatter is exactly
titleanddescription, in that order. Frontmatter is YAML, so an unquoted value containing a colon followed by a space fails the build withbad indentation of a mapping entry— quote the string or rewrite it. - No H1 in the body. Starlight renders the frontmatter
titleas the H1; a second one breaks the document outline. - Open with a one or two sentence lead paragraph directly under the frontmatter, before any heading.
- Sections are
##, subsections are###. Never go deeper. - Never hand-write an in-page anchor list. Starlight generates the ToC from your headings. Older pages in this repository did hand-write them, with invented anchors such as
#ROMand#MBthat github-slugger never produces, so every one of those links was dead. - Use Markdown tables for reference data and fenced blocks with a language tag for commands and payloads.
- Cross-link with root-relative paths and a trailing slash:
[the system map](/architecture/system-map/). - Refer to source files as inline code with a repo-relative path, such as
demostatics-web/routes/api.php. Never an absolute path from your own machine. - Mark every capability Shipped, Partial, Planned or Not built. Most of this platform is planned rather than built, and a reader must always be able to tell which is which. Never present a plan as a fact, and never invent a version, endpoint, command or path you have not verified.
The same rules are recorded for the codebase at large in the conventions page.
Known issues
Section titled “Known issues”Each item below was checked against the repository as it stands.
| Issue | Impact | Fix |
|---|---|---|
@astrojs/check is missing | pnpm astro check cannot run without an interactive install prompt, so there is no type checking of frontmatter, config or components. | pnpm add -D @astrojs/check |
| No CI, no deploy configuration | No .github/, no netlify.toml, no vercel.json, no Dockerfile, nothing. The config names a host it does not publish to, and nobody finds out a bad sidebar slug broke the build until someone builds locally. | Add a workflow that runs pnpm install and pnpm build on every push, then real hosting. |
No packageManager field and no .nvmrc | A committed pnpm-lock.yaml implies pnpm, but nothing enforces it or pins a Node version. Someone running npm install gets a different dependency tree. | Add "packageManager": "pnpm@10.13.1" to package.json and an .nvmrc. |
No editLink configured | The frontmatter editUrl field is accepted but inert — no “Edit page” link renders anywhere. | Add editLink: { baseUrl: "..." } to the Starlight options, or ignore the field. |
src/assets/houston.webp is unused | The Starlight mascot, shipped by the starter. No page or config references it. | Delete it. |
public/favicon.svg is the stock Astro icon | Not Demostatics branding. | Replace with a real favicon. |
Three starter-kit problems have already been cleared and should not be reintroduced: site is now set, README.md has been rewritten from the “Starlight Starter Kit” boilerplate, and the commented-out starter social links and Guides / Reference sidebar scaffolding have been deleted from astro.config.mjs. That scaffolding referenced a slug (guides/example) that does not exist, so pasting it back would fail the build.
The reporting-standarts typo
Section titled “The reporting-standarts typo”The directory src/content/docs/reporting-standarts/ is misspelled — it should be “standards”. The sidebar label was corrected to “Reporting Standards”, so the navigation now spells it one way and the URL another. The typo is baked into three live URLs:
/reporting-standarts/types-of-reports//reporting-standarts/reporting-rules//reporting-standarts/moderation/Renaming the directory is a one-command change in the repository and a three-entry change in astro.config.mjs, but it is not free. It changes three public URLs, which breaks any existing bookmark or inbound link and requires redirects to be configured wherever the site is hosted — and there is no hosting configuration in this repository yet, so there is currently nowhere to put them. Decide the hosting question first, then rename and add redirects in the same change. Do not rename it silently.
Adding a new page
Section titled “Adding a new page”Start to finish, for a hypothetical page at /architecture/threat-model/.
-
Create
src/content/docs/architecture/threat-model.md. The directory path undersrc/content/docs/is the URL path; create intermediate directories as needed. -
Write the frontmatter and a lead paragraph. Copy this block:
---title: Threat Modeldescription: One sentence, roughly 100 to 160 characters, saying plainly what this page is.---
One or two sentences of lead, directly under the frontmatter and before any heading.
## First Section
Body.- Add the sidebar entry in
astro.config.mjs, inside the group it belongs to. Theslugis the route without the leading or trailing slash — for a page at/architecture/threat-model/it isarchitecture/threat-model:
{ label: "Architecture", items: [ // ...existing entries { label: "Threat Model", slug: "architecture/threat-model", }, ],},-
Run
pnpm devand open the page. If the slug is wrong the dev server and the build both fail loudly withThe slug ... does not exist— that error means the sidebar and the file disagree, not that the file is malformed. -
Run
pnpm buildbefore you commit. It is the only check this repository has: it catches broken sidebar slugs and bad component imports, and it is the only thing that regenerates the search index. It does not catch unknown frontmatter keys — those are silently stripped.