The generated site had the shape of a product launch page: a near-black navy ground with mint/coral radial glows, a gradient-clipped headline, a glowing pill badge, nine emoji feature cards and scroll-reveal animations. None of it told a reader anything about the language. It is now typographic and light-first — a warm paper ground, a serif display face, one ink-blue accent used only where it means something, and rules instead of floating cards. Colour is reserved for code. Dark mode is the same design with the ground inverted, defined once as tokens under a single prefers-color-scheme block. Structurally: - base.css holds the tokens and shared chrome; site.css and docs.css hold what is specific to the landing page and the reference pages. They ship as linked files rather than being inlined into all 900+ pages, which takes the site from 16 MB to 5 MB and means a design change no longer needs a regenerate to be seen. - api.css was dead — the generator never referenced it, rendering the API index with item.css — and is gone. docs.css replaces item.css and covers all four reference page kinds. - Grids draw their separators as cell borders instead of bleeding a ruled background through gaps, so a final row with fewer cards than columns stops cleanly instead of leaving a grey hole. The feature card count is not a multiple of the column count at any breakpoint. - Inline code loses its tinted chip; in a language reference, a box behind every keyword turns a paragraph into confetti. - Fonts are the platform's own, so the site makes no webfont request. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| assets | ||
| inventory.json | ||
| README.md | ||
Ludic documentation generator
Generates the public documentation site (the pages branch) from a single
source of truth, so the site can never drift from the language.
Source of truth
docs/
language/<category>/<id>.md one file per symbol — keyword, type, phase,
builtin, namespace method, operator, annotation
language/<category>/_section.md section title + blurb + order
language/colors/palette.json the 221 named colors (generated by `x docs-palette`)
site/site.json landing-page messaging (hero, features, …)
site/snippets/*.ludic the code shown on the landing page (real programs)
tools/docgen/inventory.json the authoritative symbol set the coverage guard checks
A symbol file
---
id: screen-fill_rectangle # anchor + page name (screen-fill_rectangle.html)
name: Screen.fill_rectangle
category: screen
kind: namespace-method # keyword|type|phase|namespace-method|builtin|annotation|operator
tokens: Screen.fill_rectangle # literal token(s) the highlighter matches & links
sig: Screen.fill_rectangle(x, y, width, height, color)
tip: Draw a solid, filled rectangle. # one sentence — the hover-card summary
order: 1
ns: Screen # namespace-method only
member: fill_rectangle # namespace-method only
related: screen-clear screen-draw_rectangle
---
Rich description — inline `<code>`/`backticks`, "model instance" language.
Parameters: # for anything that takes arguments
- `x` — the left edge, in pixels
- `color` — the fill color, e.g. a `Color.*` name
```ludic
program Example { … descriptive-named, compilable … }
```
What it produces
One page per symbol (<id>.html), a namespace overview page per namespace
(ns-screen.html … ns-color.html), a searchable index (api.html, fuzzy
search over every symbol), the landing page (index.html), the
ludic-highlight.js highlighter (all its symbol tables, tips, per-item link
targets, per-parameter anchors and hover-card data generated from the sources
above), symbols.json, and .nojekyll.
In any code sample: keywords/types/builtins/annotations link to their page;
Screen.fill_rectangle links Screen → the namespace page and fill_rectangle
→ the method page separately; a named argument like width: links to that
parameter's anchor; Color.Charcoal links Color and Charcoal separately;
hovering any token shows a summary card from the real API data.
Build & check
The generator and its guards are written in Ludic and run through x — there is
no Python in the pipeline. assets/ (the CSS/HTML/JS templates) and
inventory.json are the only inputs the tools here still read directly.
bin/x docs-gen --out build/pages # generate the whole site
bin/x docs-check build/pages # coverage + duplicate-token + link guard
bin/x check-docs # parse every ```ludic doc fence
docs-check fails CI if any symbol in inventory.json lacks a page, if a token
is documented on two pages, or if a highlighter link points at a missing page —
so "every symbol is documented, autogenerated each time" is enforced. It needs a
built bin/x (bootstrapped from the IR seed with clang alone).
Publish
.forgejo/workflows/docs.yml bootstraps the toolchain from the IR seed and runs
x docs-gen + x docs-check on every push to main that touches docs/**,
tools/docgen/** or tools/x/**, and publishes the result to the pages branch
root. index.html + .nojekyll always stay at the root.
Colors
palette.json and selfhost/backend/stdlib/emit_color.ludic are both generated
by bin/x docs-palette from a single palette table — the pal_add(...) rows in
tools/x/docgen.ludic. Edit the table there and regenerate; do not hand-edit the
generated files.