All documents

Content and reading surfaces

Some screens are documents, not tools. They get the same system and a different rhythm.

A page is one Markdown file

components/brand/markdown.tsx renders it. Never hand-build a content page out of utility classes — the second writer will use different ones and the two pages will drift within a month. Everything below is decided once, in that file:

ElementTreatment
# and ##
text-display, 600, 40 px above / 12 px below, first:mt-0
###
text-section, 600, 28 px above
#### and deeper
mono caps in secondary ink — a label, not a smaller heading
paragraph, list
text-body, capped at 76 characters, 12 px rhythm
link
--accent-ink, underlined, underline-offset-2, un-underlines on hover; external links get target="_blank" rel="noreferrer"
quote
the bracket device — never a tinted box with a coloured left edge
inline code
sunken surface, mono at 0.92em, 10 px radius
code block
card surface, 1px border, 16 px radius, 16 px padding, own x-scroll
table
the real Table with stickyHeader={false} — a table in prose looks exactly like a table in a tool
table cell in prose
valign="top", caption size, 62-character cap, text-pretty
image
16 px radius, 1px border, max-w-full
rule
32 px of air, --border

Headings carry a slugified id and scroll-mt-20, so a deep link lands below the sticky header instead of under it.

Depends on react-markdown and remark-gfm. No content pages? Delete the file and both dependencies.

The measure

76 characters for prose, 62 inside a table cell. It is the same cap the tool screens use for their descriptions, which is why a documentation page and an overview page feel like one product.

Reading vs Source

A content page that is copied from somewhere upstream is worth showing as it is: a Reading view (a written lede plus the rendered body) and a Source view (the raw file). One file, two views, nothing to keep in sync. If the upstream still changes, hash it and fail a check when it moves — do not build a sync step, because a sync is what re-creates two sources.

Where content lives

One directory of Markdown, one loader, and the navigation is built from the directory — not from a second list in code. A page that exists is a file that exists.