Skip to content

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.

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.

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.

PackageRange in package.jsonResolved in lockfile
astro^5.6.15.12.5
@astrojs/starlight^0.35.20.35.2
sharp^0.34.20.34.3
typescript (dev)^5.8.35.8.3
@astrojs/language-server (dev)^2.15.42.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:

PackageVersionWhat it gives you
@astrojs/mdx4.3.1.mdx support, auto-registered by Starlight
@astrojs/sitemap3.4.2Sitemap generation, auto-registered by Starlight
astro-expressive-code0.41.3Code block titles, frames and line markers
pagefind1.3.0The 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.

Run all of these from the repository root.

CommandWhat it does
pnpm installInstalls dependencies from the committed lockfile. Required on a fresh clone.
pnpm devDev server with hot reload on http://localhost:4321.
pnpm buildProduction build into ./dist. Also the only thing that generates the Pagefind search index.
pnpm previewServes the already-built ./dist. Run pnpm build first.
pnpm astro syncRegenerates 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.

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".

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 here

An 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 sidebar
config 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.

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.

FieldTypeDefaultNotes
titlestringRequired. Rendered as the page’s H1.
descriptionstringUsed for <meta name="description"> and og:description.
templatedoc | splashdocsplash is a wide, sidebar-free landing layout.
heroobjectHero block (title, tagline, image, actions). Normally paired with template: splash.
sidebar.ordernumberAscending sort key inside a group. Only affects autogenerated groups.
sidebar.labelstringpage titleOverrides the label used in navigation.
sidebar.hiddenbooleanfalseExcludes the page from autogenerated groups.
sidebar.badgestring | objectVariants: note, tip, caution, danger, success, default.
sidebar.attrsobjectRaw HTML attributes on the sidebar link.
tableOfContentsobject | false{ minHeadingLevel: 2, maxHeadingLevel: 3 }Set false to remove the right-hand ToC on that page.
banner.contentstring (HTML)Announcement bar at the top of the page.
lastUpdateddate | booleanlastUpdated: 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 / nextboolean | string | objectOverrides the pagination links.
pagefindbooleantrueSet false to keep a page out of the search index.
draftbooleanfalseVisible in pnpm dev, excluded from production builds.
headarray[]Extra <head> tags for this page only.
editUrlstring | booleantrueInert 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:

src/content.config.ts
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.

Everything below was checked against the installed Starlight 0.35.2, not against upstream documentation.

Asides work in plain .md with no import. There are exactly four names:

:::note
Neutral information.
:::
:::tip[Did you know?]
The bracketed form replaces the default title.
:::
:::caution
Something the reader can get wrong.
:::
:::danger
Something 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.

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.

Fenced blocks are rendered by Expressive Code. Available options, all confirmed working:

```ts title="src/thing.ts" {2} ins={3} del={4} "const"
```
OptionEffect
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 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.

CapabilityStatusNotes
Markdown pages, sidebar, routingShippedWorking today.
Asides, code block decoration, auto ToCShippedVerified against 0.35.2.
Pagefind searchShippedBuild-time only; nothing to search under pnpm dev.
MDX and Starlight componentsShippedEnabled but unused by any current page.
Sitemap and canonical URLsShippedsite is set, so sitemap-index.xml is emitted and canonical / og:url are populated.
astro check type checkingNot built@astrojs/check is not a dependency.
Mermaid diagramsNot builtNeeds a third-party plugin and a browser.
CI and deploymentNot builtNo 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.

These docs are written to a fixed style. Follow it when you add or edit a page.

  • Frontmatter is exactly title and description, in that order. Frontmatter is YAML, so an unquoted value containing a colon followed by a space fails the build with bad indentation of a mapping entry — quote the string or rewrite it.
  • No H1 in the body. Starlight renders the frontmatter title as 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 #ROM and #MB that 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.

Each item below was checked against the repository as it stands.

IssueImpactFix
@astrojs/check is missingpnpm 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 configurationNo .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 .nvmrcA 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 configuredThe 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 unusedThe Starlight mascot, shipped by the starter. No page or config references it.Delete it.
public/favicon.svg is the stock Astro iconNot 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 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.

Start to finish, for a hypothetical page at /architecture/threat-model/.

  1. Create src/content/docs/architecture/threat-model.md. The directory path under src/content/docs/ is the URL path; create intermediate directories as needed.

  2. Write the frontmatter and a lead paragraph. Copy this block:

---
title: Threat Model
description: 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.
  1. Add the sidebar entry in astro.config.mjs, inside the group it belongs to. The slug is the route without the leading or trailing slash — for a page at /architecture/threat-model/ it is architecture/threat-model:
{
label: "Architecture",
items: [
// ...existing entries
{
label: "Threat Model",
slug: "architecture/threat-model",
},
],
},
  1. Run pnpm dev and open the page. If the slug is wrong the dev server and the build both fail loudly with The slug ... does not exist — that error means the sidebar and the file disagree, not that the file is malformed.

  2. Run pnpm build before 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.