These compile to the same authoring notebooks the Documentation Tools palette
produces, then build with DocumentationBuild. The palette's job (insert
sections, format inline code, make links) is done from markdown structure.
These rules apply equally to Symbol pages, Guides, and TechNotes. A per-paclet
guide may override one of them (e.g. LeanLink's
DOC_GUIDE.md
inverts the per-section reset for state-threading tutorials) - read the paclet's
own doc guide first if it has one.
Every symbol the page mentions - built-in ([Range](), [Dataset](),
[FunctionCompile]()) and paclet - is a link, not a backticked code word.
The convention is:
- Bare mention is the inferred-link form
[Range]()(empty parens; the converter resolves it to the symbol's ref page). It renders asRangein code-link style and clicks through to the ref page. - Inline call is code-styled and linked: write
Range[n], not`Range[n]`and not plain[Range](). Markdown forbids nested formatting inside a backtick span but processes markdown inside an inline<code>element, so the link and italics render inside the code style. - Backticks
` `are reserved for things that are not symbols: a string literal (`"Open"`), a theorem/constant name (`"mul_comm"`), an option value (`"ProjectDir"`), a tactic / source fragment (`"intro P"`,`∀ (P : Prop), P → P`), or a path (`LeanLink/Assets/`). - If you link a paclet symbol whose
docs/Symbols/<Name>.mdpage does not exist yet, create the page in the same pass - else the link does not resolve at build time and the docked Check flags it.
In a ## Usage signature and in prose, write argument names in italics:
Range[n], Map[f, list]. In prose,
$...$ is always math: do not use the $x$ form for an argument name there -
it renders as inline LaTeX. Genuine mathematics in prose - a proposition like
$...$; that is math, not an
argument name.
Inside a <code> usage signature the converter reads $...$ as arguments, so
either form works there:
| In the signature | Becomes |
|---|---|
$n$, *n*
|
the italic template argument n |
$x_1$, $x_{ij}$, *x_i*
|
a subscripted argument |
$\beta$, $\beta_1$
|
the Greek letter as an argument, subscripted or not |
$\{a, b\}$, $\{t_1, t_2\}$
|
literal braces and commas around arguments, as {$a$, $b$}
|
$x^2$, $n \geq 1$, $a + b$
|
typeset math |
A math span holding a comma outside a braced list ($a, b$) is not read; write
each argument as its own span ($a$, $b$).
- No
Needs[…]. MTN reads the frontmatterContext:and loads the paclet automatically before evaluating any cell, so an example never needsNeeds["Publisher`PacletName`"]. If a page's examples span more than the primary context, list the extra ones in aContextPath:frontmatter list (e.g.ContextPath: [Other\Pkg`, Third`Pkg`]); they are loaded too - both at build time and in the page'sExampleInitialization` cell, so a reader running the examples gets every needed context. - One output per cell. Never pack two computations into one cell to save a paragraph - split them into two captioned demonstrations (see docs/examples.md for the rule and the nuance around lists that are the demonstration).
- Bare
[Association]()does not render cleanly. If the natural output is an<|...|>, project it through[Dataset]()(the natural shape for a record),[Normal](), or[Keys](), depending on what the reader needs to see. Strings, numbers, lists,[Failure]()objects, summary boxes, and graphics all serialise to the notebook fine; an Association does not.
The default EvaluateSeparator: Automatic resets the evaluation context at
every heading and --- delimiter, so a temp binding set in
## Basic Examples is not visible in ## Scope. This is right for a
reference page: each section reads as a self-contained block.
The reset is not total, though: any symbol introduced by the document's
## Definition section (the FunctionResource function, a heavy environment
import, a shared helper) is preserved across every later reset. Only
symbols introduced after the definition section get cleared at boundaries.
So a multi-part tutorial that needs an imported environment across all its
sections puts the import in ## Definition:
## Definition
\`\`\`wl
env = LeanImport["MyProject"]
\`\`\`
## Part 1 - Theorems
\`\`\`wl
env["Theorems"] (* env still bound here *)
\`\`\`
## Part 2 - Tactics
\`\`\`wl
env["Tactics"] (* still bound here too *)
\`\`\`EvaluateSeparator: None (no reset at all, one shared context for the
whole notebook) still works, but avoid it - it leaks every
per-example binding across every later section, so a stray tmp in
## Basic Examples can collide with a name in ## Scope.
Definition-preservation under Automatic covers the legitimate state-
threading use case without that risk.
A <!-- => ... --> comment after a wl cell documents the expected output
for the reader of the source. MTN strips HTML comments before evaluating,
so the hint is never checked against the real output. That is exactly why
the doc-build loop is build + read back the actual .nb - the hint
records what you expect, the build produces what is real. A stale hint is
worse than no hint: probe each example against the live paclet first and
paste the actual result in.
A headless wl build can rasterize graphics ([Plot](), [Graph](),
[BarChart]()) but cannot rasterize typeset boxes - a [Rasterize]() of
a [RawBoxes]() / [Style]() expression comes back all white. If a page needs
to show typeset math or a formatted box, verify it by exporting a PDF and
running sips over it, not by trusting an inline Rasterize of the source.
The build pipeline pins the front end to Light so that the graph outputs which
do rasterize are not inverted - keep that in mind if you author a paclet
guide that wants Dark.
The docked Check fires ThreeDotEllipsis whenever it sees a literal ...
in prose. MTN handles prose for you: every prose run (heading,
paragraph, list item, quote, usage description) gets ... rewritten to
… (U+2026) on the way to the notebook. The replacement only fires on
the plain-text runs, so ... inside backticks, inside an inline <code>
span, inside $...$ math, or inside a fenced wl cell is left untouched.
That carve-out exists because ... is syntactic in Wolfram Language -
it's the RepeatedNull pattern operator ({a, b...} matches a followed
by zero or more bs). MTN cannot tell whether your ... is meant as
"etc" or as the operator, so inside any code position it stays as-is.
That leaves one case for the author to handle: an inline <code> (or
backtick) span that shows a signature sketch, not runnable code, where
... means "and so on". Write … (or \[Ellipsis]) explicitly so a
reader does not mistake the placeholder for RepeatedNull:
- <code>[Association]()[*key* -> *value*, …]</code> <- "etc"
- <code>[Range]()[*n*]</code> <- one signature
- ``MyFn[args___] := body`` <- runnable, ... means RepeatedNullIn an actual fenced wl cell that the build evaluates, never substitute
… for ... - the parser does not accept \[Ellipsis] as a pattern
operator, and the cell will fail to evaluate. The rule of thumb: prose
is automatic; runnable code is verbatim; non-runnable signature sketches
use … deliberately.
Some examples need resources that may be absent on a given build machine - a mathlib4 checkout, a downloaded dataset, a sister paclet. Gate them so the build degrades to an explanatory string instead of an error:
If[ FileExistsQ["~/path/to/mathlib"],
realComputation[],
"(Run `lake exe cache get && lake build ...` first.)"
]Never hardcode an absolute home path in a way that errors when it is
missing. Slow scans (walking thousands of files) are fine in a tutorial but
keep them out of ## Basic Examples on a Symbol page.
---
Template: Symbol
Name: WCAGContrastRatio
Context: Wolfram`AccessibleColors`
Paclet: Wolfram/AccessibleColors
URI: Wolfram/AccessibleColors/ref/WCAGContrastRatio
Keywords: [contrast, WCAG]
SeeAlso: [WCAGLevel, AccessibleTextColor]
RelatedGuides: [AccessibleColors]
---
## Usage
`WCAGContrastRatio[c1, c2]` gives the ratio between colors `c1` and `c2`.
## Details & Options
Prose, possibly referencing `ColorConvert` and option `c1`.
## Basic Examples
Prose lead-in:
```wl
WCAGContrastRatio[Black, White]
```| Markdown | Palette / cell | Notebook |
|---|---|---|
# Name / Name: |
New Function Page | ObjectName |
## Usage line, leading `Call[a,b]` |
Double Usage Line | Usage cell: ModInfo + linked-call InlineFormula + description |
## Details & Options prose |
Details & Options / Note | Notes |
## Background & Context prose |
Function Essay section | FunctionEssay cells in a top-level FunctionEssaySection group (the built page's Background section, placed below Details by DocumentationBuild) |
### heading inside Details |
(groups the notes after it) | NotesSubsection |
## Basic Examples + wl cells |
Insert Text + Input | PrimaryExamplesSection, ExampleText, Input/Output |
Context: |
(load paclet) | ExamplesInitializationSection -> Needs["Context"]` |
ContextPath: [..] |
(load extra paclets) | one extra Needs["…"]per entry in the sameExampleInitialization` cell (and on the build-time eval path) |
SeeAlso: [..] |
Links ▸ Link to Function Page | SeeAlsoSection with paclet:Pub/Name/ref/X links |
RelatedGuides: [..] |
Links ▸ Link to Guide | MoreAboutSection |
RelatedTutorials: [..] |
Links ▸ Link to Tech Note | TechNotesSection; an entry may be [Label](Name) when the note's displayed title differs from its page name |
URI: / Keywords: |
Metadata / Keywords sections | build metadata + Keywords |
Examples are evaluated (cached) and spliced as Output cells. The first wl
example lands under PrimaryExamplesSection. A wl cell may be followed by an
<!-- => ... --> comment recording the expected output; comments are stripped
on conversion, so they document the source without affecting the build.
A Symbol page has no Description: field. Unlike a Guide, the function
summary comes from the ## Usage line, so frontmatter carries only metadata
(Name, Context, Paclet, URI, Keywords, SeeAlso, RelatedTutorials,
RelatedGuides).
Extended example sections (## Scope, ## Options, ## Applications,
## Properties and Relations, ## Possible Issues, ## Interactive Examples,
## Neat Examples) are
populated under the "More Examples" group: each maps to its ExampleSection
title (an InterpretationBox counter cell that resets the In[]/Out[]
numbering), wrapped in a CellGroupData with the section's prose, examples and
tables. Sections with no content are dropped (built pages omit empty sections).
---
Template: Guide
Name: AccessibleColors
Paclet: Wolfram/AccessibleColors
URI: Wolfram/AccessibleColors/guide/AccessibleColors
Keywords: [...]
RelatedGuides: [...]
Links: [...]
---
## Abstract
One paragraph.
## Functions
- `WCAGContrastRatio` the contrast ratio between two colors
- `WCAGLevel` the conformance level of a color pair| Markdown | Notebook |
|---|---|
Name: / Title: |
GuideTitle |
## Abstract (or Description:) |
GuideAbstract |
## Functions list |
GuideFunctionsSection with one GuideText per item, led by an InlineGuideFunction chip |
RelatedGuides: |
GuideMoreAbout |
Links: |
GuideRelatedLinks |
Keywords: |
Keywords |
The ## Functions list (the palette's Inline Listing) renders one GuideText
cell per - `` Symbol `` description item: the leading inline-code symbol
becomes a linked InlineGuideFunction chip and the rest is its description.
An item may lead with several comma-separated symbols
(- `` A``, ``B``, ``C `` description) — each becomes its own chip and
the — separator lands after the last one, so a single line can cover a group
of related symbols (e.g. a family of …Q predicates).
Built from TechNoteBaseTemplateExt (-> TechNotePageStylesExt). A tech note is
free-flowing, so the body is mapped directly rather than into fixed sections:
---
Template: TechNote
Name: DesigningAccessibleColorSchemes
Title: Designing Accessible Color Schemes
Context: Wolfram`AccessibleColors`
Paclet: Wolfram/AccessibleColors
URI: Wolfram/AccessibleColors/tutorial/DesigningAccessibleColorSchemes
Keywords: [...]
RelatedGuides: [AccessibleColors]
---
Intro paragraph.
## Measuring Contrast
prose, then a `wl` cell.| Markdown | Notebook |
|---|---|
Title: (falls back to Name:) |
Title |
## Heading / ### / #### |
Section / Subsection / Subsubsection |
| prose | Text |
wl cells |
Input / evaluated Output |
| tables / lists | GridBox / Item |
RelatedGuides: |
TutorialMoreAbout (Related Guides) |
RelatedTutorials: |
RelatedTutorials (Related Tech Notes) |
Keywords: |
Keywords; URI: -> tutorial/... |
Name is the file/URI id (no spaces); Title is the displayed heading.
A doc page is a twin: the .md source and the evaluated .nb that MTN
builds from it. A page is not done until you have built it and read back
every output cell - the rendered evaluation is the truth; the source is
the recipe. Use the wl CLI (not wolframscript; its init can wedge kernels
under concurrency):
wl -f build_docs.wls
A typical paclet's build_docs.wls discovers every docs/**/*.md, so a new
file builds with no wiring. It calls the deployed MarkdownToNotebook
cloud resource so the build needs cloud access:
mtn = ResourceFunction[ResourceObject[
"https://www.wolframcloud.com/obj/wolframinstitute/DeployedResources/Function/MarkdownToNotebook"]];
mtn["docs/Symbols/Foo.md", "Documentation/English/ReferencePages/Symbols/Foo.nb",
"EvaluateSeparator" -> None] (* or Automatic; see Conventions *)To iterate on one page without rebuilding the whole set, bind mtn at the
console and call it directly on the single .md you are working on. The
evaluation cache lives in PersistentObjects under
MarkdownToNotebook/ExampleOutput/<DocName>/<NNN>-<hash>, keyed by the
cumulative source up to each cell, so untouched cells are read from cache;
edit a cell and only it (plus anything depending on it) re-evaluates.
After MTN produces the .nb, build the published page with
DocumentationBuild:
Needs["DocumentationBuild`"];
UsingFrontEnd @ DocumentationBuildNotebook[None, Get["…/Symbols/X.nb"]]UsingFrontEnd is required: the Build/Preview path reads metadata via
CurrentValue and resolves links in the front end. Running it inside
LocalSubmit (a separate kernel) isolates the front-end session.
If the shim a paclet depends on (a native dylib, a sister paclet, a tool on
$PATH) is missing, the build will fail loudly rather than emit blank cells
- that is intended. Restore the dependency before re-running.


