Agents writing for humans ~70 tags · 40+ components stable expanded vocabulary

Richdoc is a small framework for documents written by an agent and read by a person in a browser. This page exercises every tag in the vocabulary the way a real document should — read top-to-bottom, with occasional asides dropping down to a Notes section at the foot of the page.

Every richdoc ships with a floating preview picker in the bottom-right corner. Open it to switch between the two themes (Editorial / Graphite), three modes (Light / Dark / Auto), and four page widths (Narrow / Standard / Wide / Full). Your choices persist per file. Suppress the picker with <rd-page prefs="off">.

Squeeze the page to Narrow in a wide browser to see the multi-column layouts, stat tiles, and TOC adapt to the chosen content width — responsiveness is driven by container queries on rd-page, not the viewport.

The look is intentionally editorial: warm paper, Fraunces display, Geist body, hairlines instead of cards, oldstyle figures, and the occasional pull-quote.

Five callout types, each with a Lucide icon and tracked small-caps title. They are quieter than the old filled blocks — a tinted background and a 2 px left rule do the work.

Callouts break out short asides — context, caveats, or pointers — that you do not want buried in a paragraph. Prefer callouts over bold-italic emphasis for anything more than a sentence. rd-cols collapses to a single column under ~720 px. Do not put more than four columns or content becomes unreadable on tablets. Never invent new rd-* tags. The linter fails and the rendered doc shows empty boxes. Title is optional. Without one, the callout uses the type name as a fallback heading.

Use rd-cols for genuinely parallel content. Cards are the natural child. Each accent value lights up a top rule and a coloured kicker label so the card reads typographically, not as a tinted block.

Three cards, no accents

Find out what exists. Read the code, run small probes, talk to whoever knows the system best. Decide what to change. Resolve the trade-offs explicitly. Write them down before touching anything. Execute the plan in reviewable units. Keep changes small and traceable. Ship boring code.

Four cards with accent colours

Neutral context, links, references.

Positive outcomes, recommendations.

Things to watch out for, fragile areas.

Hard blockers, breaking changes.

Asymmetric layout via template

Set template on rd-cols for a CSS-Grid string that defines arbitrary column ratios. Below 720 px it still collapses to one column.

Use the wide column for the primary thought. Use the narrow column for a related card, a quote, or a small comparison.

This pattern is editorial-native: think of it as a body column with a sidebar.

Muted cards are for asides, footnotes, or quiet credits. No border; italic by default.

Use rd-stat for dashboard tiles. The numeral is set in Fraunces at optical size 144 with tabular figures, sitting under a hairline rule — confident, not boxy.

Pull-quotes use the native <blockquote> element — styled by richdoc.css with an oversized opening glyph, Fraunces italic, and small-caps attribution via <cite> or <footer>.

Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.

Donald Knuth — Literate Programming, 1984

For decision matrices. Tones get a coloured dot prefix and a low-opacity stripe — readable in print, distinctive on screen.

No multi-column, no cards Possible, verbose Built in Tiny, universal Unbounded — drifts Closed, linted Good Drifts under load Validated at write time Yes — GitHub renders it Yes — any browser Browser only

Use rd-kv for short status blocks at the top of a doc. No outer box, just hairlines.

platform team in progress P0 infra auth security Values can contain inline HTML, badges, links — anything inline.

Code lives in rd-code. The bar at the top shows the language as a tracked small-caps kicker and an optional title in Fraunces italic. Syntax highlighting loads on demand from highlight.js and is themed against the editorial palette — no third-party stylesheet.

import { signToken } from "./jwt.ts"; import type { TokenPayload } from "./types.ts"; export async function authenticate(payload: TokenPayload): Promise<string> { const key = await loadSigningKey(); if (!key) { throw new Error("signing key unavailable"); } return signToken(payload, key, { algorithm: "EdDSA" }); } # Initialize a richdoc next to your file cd docs/ richdoc init . richdoc new plan.html --template plan

rd-diff renders a unified diff with additions and deletions colour-coded. If lang is set, the line bodies are syntax-highlighted by the same engine that powers rd-code.

@@ -3,5 +3,10 @@ import { signToken } from "./jwt.ts"; import type { TokenPayload } from "./types.ts"; -export async function authenticate(payload: TokenPayload): Promise<string> { - const key = await loadSigningKey(); - return signToken(payload, key, { algorithm: "EdDSA" }); +export async function authenticate( + payload: TokenPayload, + opts: AuthOptions = {}, +): Promise<Token> { + const key = await loadSigningKey(opts.keyId); + if (!key) throw new AuthError("signing key unavailable"); + return signToken(payload, key, { algorithm: opts.algorithm ?? "EdDSA" }); }

Use rd-math for KaTeX-rendered formulas. Block display by default; pass display="inline" for inline use, e.g. the relation E = mc^2 can sit inside a sentence. Block displays render between hairline rules:

P(A \mid B) = \frac{P(B \mid A)\, P(A)}{P(B)} \frac{\partial}{\partial t}\Psi(\mathbf{r}, t) = -\frac{i}{\hbar} \hat{H}\, \Psi(\mathbf{r}, t)

Inline SVG glyphs from the full Lucide library (~1,900 names at the pinned lucide-static version). A core set ships inline in richdoc.js; everything else is lazy-loaded from jsDelivr on first reference and cached. Run richdoc components --tag rd-icon for the authoritative list.

check   x   warn   info

up   down   next   out

sparkles   zap   star   bookmark

heart   compass   cloud   database   cpu   package   rocket   telescope

Sizes: small, medium, large.

Tabbed content for the same idea expressed three ways. First tab is active; arrow keys navigate.

Tabs are good for the same content rendered three ways: language variants, before/after, examples by stack.

They are not for hiding important decisions — humans scanning the doc will miss whatever isn't on the active tab.

  • Same example in multiple languages.
  • Before / after code comparisons.
  • Optional deep dives that interrupt the main flow.
  • Hiding important steps. Use sections instead.
  • Long content. Tabs cap at a screenful of comfortable reading.
  • Search-critical content (only the active tab is visible to Ctrl+F on first load).

Sequenced events: release history, project milestones, incident postmortems. Dates in tracked small-caps, titles in Fraunces italic, the rule is dotted.

Initial vocabulary (17 tags), three templates, smoke-tested CLI. Modular source tree, build pipeline, schema co-located with each component, five new tags. Fraunces + Geist + Fira Code, hairline rules, pull-quotes, oversized stats. Five new tags (rd-icon, rd-math, rd-diff, glossary support via rd-kv layout="stacked"); rd-code upgraded with syntax highlighting and line numbers. richdoc site for static collections with sidebar nav and search.

One element, ~25 diagram languages. Source is sent to a Kroki-compatible endpoint (default kroki.io) and the rendered SVG embeds inline. Set endpoint per-element or <rd-page diagram-endpoint="…"> for the whole doc; for sensitive content point at a self-hosted Kroki. Hover any diagram and click the corner maximize button for the fullscreen pan/zoom viewer (0 fit, 1 reset, arrow keys pan, Esc close).

flowchart LR A[Agent writes .html] --> B{richdoc lint} B -- OK --> C[Open in browser] B -- errors --> D[Fix and re-run] D --> B C --> E[Human review] @startuml actor Agent participant "richdoc CLI" as CLI database Browser Agent -> CLI : richdoc lint doc.html CLI --> Agent : ok Agent -> Browser : open doc.html Browser --> Agent : rendered page @enduml Browser -> API: POST /login API -> DB: verify API -> Browser: set cookie

Wrap an image, SVG, or diagram with a centred caption. The caption is set in Fraunces italic with a "Fig." kicker in the accent colour.

flowchart TB Plan --> Draft --> Validate --> Publish Validate -- fail --> Draft

Use rd-detail for content that is relevant but not on the main path — appendices, full error logs, derivations. Native <details> under the hood, so it works without JS and remains keyboard-accessible.

Free-form HTML lets agents drift. One doc invents <callout>, the next uses <note>, the third leaves it as bold-italic prose. The reader experience fragments and the agent has no way to verify their output.

A closed vocabulary plus a validator makes both ends of the loop reliable: the agent always knows what is allowed, and the reader always sees the same components.

Pass the open attribute to start expanded.

For action items with optional assignee and due date. Hairline-separated rows, ticks in display face when complete.

Land v0.1 PoC Split sources into per-component folders Add build pipeline + schema.json Editorial redesign + vocabulary expansion Static-site mode for multi-doc collections

Inline status pills, prefixed with a coloured dot: default info success warn danger muted

Use rd-kv layout="stacked" for a definition list. Terms are set in Fraunces italic, the block is bracketed by hairline rules, rows separated by dotted lines.

A small framework for AI-authored, human-read HTML documents that uses a closed vocabulary of rd-* custom elements. A design voice borrowed from print publications: hairline rules, oldstyle figures, drop caps, Fraunces display type, restrained colour. A fixed list of tags that the agent must choose from. New tags require a deliberate authoring decision and a linter update. A Fraunces variation axis that adjusts letterforms for the size at which they will appear. Set higher (72, 144) for display.

Use <rd-callout type="tldr"> as the first block after the hero. It's a focal summary band with an oversized eyebrow and slightly larger body type than the surrounding prose. See the very top of this page.

Use rd-hero instead of an ad-hoc <h1> + rd-kv opener. It coordinates eyebrow, title, lede, and meta into one display block, and accepts any inline children (e.g. an rd-kv) as an "extras" strip below the meta line. The top of this page demonstrates the full structure.

Doc-status ribbons sit at the top of the page. Five types:

Linear progress / capacity bar. Accepts a 0..1 decimal, N%, or N/M fraction. The fill animates from 0 to the target on entry.

Numbered procedural steps with a rich body per step. Use this for runbooks, onboarding flows, migration guides. Distinct from rd-timeline (dated history) and rd-checklist (task tracking).

Run richdoc init docs once in the directory that will hold your .html file.

Pick a template: richdoc new docs/plan.html -t plan.

Use only the rd-* tags from the vocabulary. Plain HTML is welcome inside any block.

Run richdoc lint docs/plan.html. Linting cleanly is part of "done".

Two-column ✓/✗ grid for decision documents. Distinct from rd-compare — that's a matrix; this is one-sided evaluation of a single option.

Already-shipped pattern in the existing vocabulary. No new external dependencies in Phase 1. Linter enforces shape on existing rule engine. Vocabulary grows from ~30 → ~70 entries. Authors must learn the new tags.

ADR-style decision header + rationale. Four statuses control the left rule colour and the status pill.

Bundle size is not a concern for documentation artefacts. Plot's grammar-of-graphics defaults match the editorial voice better than anything we'd hand-roll, and one library covers every chart type plus sparklines.

A floating-gutter sidenote (rd-margin) was prototyped but rejected: the page column is centred at 1280px with no reserved gutter, so the negative-margin float overflowed off-screen on 1024–1500px viewports — the exact range the wide-mode media query targeted. rd-cite with rd-ref already covers the related authoring case on every viewport.

Superseded by ADR-014.

Too much per-chart code; Plot covers the same surface with a much smaller author API.

Dated reverse-chron entries for changelogs, release notes, status reports. The kind glyph and tint differ between release / change / note.

Expanded the framework's structural vocabulary — hero, banner, progress, steps, pros/cons, decision records, updates, terminal sessions, API blocks, and rubrics all landed in one release.

Introduced Decision & planning and Reference groupings in the schema registry. Mirrored in the SKILL.md tag tables.

The 20-component plan was reviewed and accepted. Implementation phases queued.

Question/answer disclosure uses rd-detail variant="question" — questions render in display Fraunces, answers in body Inter, with hairline separators between entries.

Use Markdown where the renderer might be anything (GitHub, chat, CLI). For documents read in a browser, richdoc gives a much richer presentation with no extra effort.

No — the linter will reject unknown rd-* tags. The fixed vocabulary is the point. Extensions ship through a new release.

Yes. The two shipped assets (richdoc.css, richdoc.js) work over file://. Components that need a CDN library (mermaid, KaTeX, charts) degrade to a readable fallback when offline.

Terminal transcripts for runbooks and tutorials. Distinct from rd-code — no syntax highlighting, no copy button, just prompt → output.

richdoc init docs copied richdoc.css → docs/richdoc.css copied richdoc.js → docs/richdoc.js richdoc new docs/plan.html -t plan wrote docs/plan.html (1245 bytes) richdoc lint docs/plan.html {"ok": true, "issues": []}

Single-endpoint reference block. Method pill is coloured per HTTP verb; status pills tint themselves based on the 2xx/4xx/5xx class.

Document identifier. Treat warnings as errors. Optional client-supplied trace identifier. The document to lint. Lint succeeded. Body is { ok, issues[] }. Malformed input. Schema validation failed; issues[] contains details.

Weighted scoring grid for comparison documents — like rd-compare but with numeric scores, weights, and an automatic totals row. The winning column highlights.

Bibliography support follows the footnote pattern but with square-bracketed numbers and a separate collector. Authors write rd-cite markers inline and rd-ref entries anywhere in the doc. The system numbers citations in document order, builds the bibliography below, and shows the entry as a tooltip when hovering a marker.

Multiple cites to the same key share a number. Entries not cited still appear in the bibliography, after the cited ones.

Reference work for editorial data graphics; cited in this doc for sparkline philosophy and the footnote-as-aside pattern. Uncited — still appears in the bibliography after the cited works.

SVG charts via Observable Plot, lazy-loaded from jsDelivr on first use. Six kinds out of the box. If Plot can't load (offline, blocked CDN), each chart falls back to a clean table of the underlying data.

[ {"month":"Jan","users":42}, {"month":"Feb","users":58}, {"month":"Mar","users":61}, {"month":"Apr","users":79}, {"month":"May","users":92}, {"month":"Jun","users":104} ] [{"month":"Jan","requests":1.2},{"month":"Feb","requests":1.8},{"month":"Mar","requests":2.1},{"month":"Apr","requests":2.6},{"month":"May","requests":3.0},{"month":"Jun","requests":3.4}] region,product,value North,Core,18 North,Add-on,7 South,Core,14 South,Add-on,9 East,Core,22 East,Add-on,5 West,Core,16 West,Add-on,11

Tiny inline trends share the chart engine via <rd-chart variant="sparkline"> — stripped of axes, titles, legends. They render in tables, in prose, or inside rd-stat. Latency last 30 days: . Signups: .

Spoilers use rd-detail variant="reveal" — content hides behind an eye-icon button until the reader is ready. Useful for runbook solutions, exam answers, or any "think before you peek" pattern.

The same component can also wrap multiple paragraphs, callouts, or even code blocks — anything that should not be eye-deep on first read.

Hidden content can include any other component.

You can still use everyday semantic HTML inside any component or directly inside rd-page:

  1. First step in a sequence.
  2. Second step.
  3. Third step.
"The best documentation is the kind you actually read." — anonymous

Horizontal rules separate sections of free-form prose where rd-section would feel heavy.