Authoring reports
Reports live in apps/site/src/content/reports/<slug>/v<version>.mdx. Each file is one version of one report; the newest version is served at /reports/<slug>/ and every version stays at /reports/<slug>/v<version>/.
Frontmatter
Section titled “Frontmatter”title: "Atmospheric CO₂ at Mauna Loa"subtitle: "A reference analysis …"slug: atmospheric-co2-2026 # lower-case, hyphens; never changestype: analysis # report | analysis | brief (reports get a PDF cover page, a contents page and back matter on its own page)palette: goap # optional: default | landscape | goap — colour palette for this report (see /design/palette/)status: published # draft | review | published | superseded (drafts build only in dev)version: "1.0" # semver-ish; bump for a new filedate: 2026-08-17updated: 2026-08-20 # optionalauthors: - name: Jane Doe orcid: 0000-0002-1825-0097 affiliation: Centre for Sustainable Development Reform role: Lead author - name: CSDR Platform Team type: organizationabstract: >- # used for <meta description>, JSON-LD and the reports list …keywords: [carbon dioxide, Mauna Loa]licence: CC BY 4.0doi: 10.5281/zenodo.0000000 # optional; when mintedfunder: … # optionalseries: Scaffold reference reports # optionaldata_bundles: [noaa-mlo-co2] # bundle ids used by the reportoutputs: { pdf: true, html: true } # PDF + self-contained HTML written next to the page by pnpm buildchangelog: - { version: "1.0", date: 2026-08-17, note: First release }The schema is reportSchema in src/content.config.ts; the build fails on invalid frontmatter.
Public or internal is decided by the folder, not by frontmatter (D11): src/content/reports/<slug>/ is on the public site; src/content/internal/<slug>/ is only on the sign-in internal site (pnpm dev:internal to preview). Setting visibility: internal on a file under reports/ fails pnpm check — move the directory instead. See Deploy → Audiences.
The vocabulary
Section titled “The vocabulary”Plain Markdown does most of the work — Markdown tables are auto-wrapped in a scroll container,  becomes a captioned figure. Beyond that, import from @csdr/editorial:
import { Figure, Table, Callout, Ref, KeyMessages, StatRow, StatTile, DataDownload, Definition } from '@csdr/editorial';| Component | Use |
|---|---|
<KeyMessages> | Bulleted opening block; one number per bullet if you can |
<StatRow><StatTile …/></StatRow> | 2–4 headline numbers with unit, delta, source |
<Figure id caption source width webNote printNote> | Numbered figure; put a chart, image or map inside. webNote (“Hover for values…”) is dropped from the PDF/print; printNote (“An interactive version is online.”) appears only there |
<Table id caption units source landscape keep> | Numbered table; put a Markdown table inside (blank lines around it). In the PDF, landscape gives a wide table its own landscape page and keep stops it splitting across pages (by default long tables run over with the header row repeated) |
<Callout kind title> | note · key-finding · method · warning · caution |
<Ref to="fig-x" /> or {ref:fig-x} | “Figure N” / “Table N”, resolved at build |
[@key] | Citation from src/content/bib/references.bib; end the report with ## References and [^ref] |
[^1] | GFM footnote |
<DataDownload bundle="id" /> | Files, licence, provenance, citation of a bundle |
<Definition term="SEEA">…</Definition> | Inline definition |
See the live catalogue at /design/ — every component with a render and copyable code.
Numbers that come from data
Section titled “Numbers that come from data”Do not type numbers that the data can change. Put a small .astro “analysis cell” next to the report (see src/components/analyses/co2/) that loads the bundle with the data loaders, computes derived values once, and exposes charts and inline values (<Co2Value of="latestAnnual" />). The reference report is the worked example.
Versions
Section titled “Versions”To publish a revision, copy v1.0.mdx to v1.1.mdx, bump version, add a changelog entry, and set the old file’s status: superseded. Both build; the old one shows a banner and canonicalises to the new one.
Checklist before /publish
Section titled “Checklist before /publish”pnpm checkandpnpm testpass- Every figure has
caption,sourceand (for charts) analtsentence - Key numbers come from an analysis cell, not the keyboard
- References render under
## References - Preview at
/reports/<slug>/and print preview look right
Print and PDF
Section titled “Print and PDF”Every build writes the PDF and the self-contained HTML export next to the page (see Deploy → Exports). What authors control:
- The PDF is a print-native document, not the web page on paper: a 10 pt body on a 154 mm measure (20 mm left / 36 mm right), numbered sections (
1,2.1— the screen stays unnumbered), figures at text width, no decorative rules. The same layer drives the HTML export, so the two files are one document in two containers. type: reportgets a title page (title block, authors, version/licence, the abstract and the key messages, which move up from the body for print), a contents page with page numbers (filled from a first render, then printed again) plus an imprint (recommended citation, versions, changelog, licence), and running headers from page 2;analysisandbriefflow from page 1 — title block, key messages, headline numbers, first section — with the same running header/footer.status: draft | review | supersededprints a watermark on every page and names the status in the running header. Drafts are not built in production at all.outputs: { pdf: false }or{ html: false }switches an export off for that report.<Figure webNote="…" printNote="…">keeps screen-only sentences (“hover for values”) out of the PDF and adds a print-only one;<Table landscape keep>controls how a wide or long table paginates.- Figures, callouts, key messages, stat rows and the data card never split across pages; tables may, with the header row repeated. Charts print at text width and at most 80 mm tall (90 mm for
width="wide") — the default chart shape (720 × 320) is chosen for that. Open a section with a sentence, not a figure: a heading is kept with whatever follows it, so a heading + full-width figure that does not fit moves to the next page together and leaves a gap. If something still paginates badly, the fix belongs inpackages/theme/src/print.css, not in the report.