Nodes & the mental model
Every structure in Wit is built from one move — a node — and every feature sits on one of five layers stacked on plain prose. Learn the move and the stack, and the rest of the language is predictable.
The node: one shape for everything
A node wraps content in a named structure. It has two spellings that mean exactly the same thing:
(name … name)— the parenthesis form, for anything that fits on one line.@name … name@— the block form, for a container that spans several lines.
(h1 The Lamp Keeper h1) The lamp was (em always em) lit by six. @blockquote A line worth setting apart, and a second line under it. blockquote@
The Lamp Keeper
The lamp was always lit by six.
A line worth setting apart, and a second line under it.
(h1 … h1) becomes an <h1>, (em … em) an <em>, and @blockquote … blockquote@ a <blockquote>. Wit knows
which core nodes are blocks and which sit inside a sentence, so you don't say.
Nodes that hold nothing — a rule, an image — take no closer:
@hr @img(src: ./lamp.png, alt: The keeper's lamp)
A parenthesis only opens a node when a known node name follows it, so "(see figure 2)" and "(a few notes)" stay ordinary prose.
Nesting comes from pairs, not indentation
Nodes nest inside nodes, and scope is set entirely by the open/close pairs — indentation is for your eyes:
@ul (li First li) (li Second li) ul@
- First
- Second
Written flush-left it means exactly the same thing; wit fmt re-indents to match the nesting. Forget a closer on a block and the build stops
with E_UNCLOSED_NODE pointing at the opener.
Body carries content; parameters carry metadata
Parameters go in parentheses glued to the name, as key: value pairs:
Read (a(href: https://example.org/log) the keeper's log a) first.
The address is a parameter; the link text is the body. That split is the governing rule — if the reader reads it, it's the body; if it only describes the node, it's a parameter.
The built-in vocabulary
You define none of these — they ship with Wit, each mapping to one semantic HTML element:
- Headings —
h1…h6. - Inline marks —
em,strong,code,u,s,sub,sup,mark,small,span,cite,br, plus the*bold*and_italic_shortcuts. - Lists —
ul,ol,li; definition listsdl/dt/dd. - Blocks —
p,blockquote,pre,div,hr. - Media —
a,img,figure,figcaption,audio,video. - Sectioning —
section,article,aside,header,footer,nav,main. - Tables and layout —
tableand its parts,row,col.
The core vocabulary lists each with its parameters and output. When the core doesn't have what you need, define your own node and use it the same way.
The five layers
Every feature lives on one layer of a stack built on prose. Most documents never leave the bottom two, and nothing above a layer costs you anything until you reach for it.
- Prose — ordinary paragraphs; a blank line starts a new one. A document of nothing but prose is already valid.
- Nodes — the named structures above.
- Definitions — name a chunk of content once with
#, reuse it anywhere with@name. - Data — records
{ key: value }and lists[ … ], pulled into prose with@name.field. - Logic —
(if …)and(each … as …)shape a document from its data; a<% … %>script is the escape hatch for anything more.
One document climbing all five:
(h1 The Lamp Keeper h1) #station: { name: Ledbury Point, years: 11 } !! @station.name has kept the light for @station.years years. (if @station.years is 11) (aside(class: note) A milestone year. aside) (end)
The Lamp Keeper
Ledbury Point has kept the light for 11 years.
That builds to:
<h1>The Lamp Keeper</h1> <p>Ledbury Point has kept the light for 11 years.</p> <aside class="note"><p>A milestone year.</p></aside>
The heading is a node around prose; #station is a definition
holding data; @station.name pulls that data into a sentence; the (if …) is logic.
See also
- Writing text — prose in depth.
- Reuse: components & multi-file — definitions and parameters.
- Data, Conditionals and Iteration — the upper layers.