Wit

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: an opener @name, the body, and a matching closer name@ — the same word with the @ on the other side.

source
@h1 The Lamp Keeper h1@

@blockquote A line worth setting apart. blockquote@
result

The Lamp Keeper

A line worth setting apart.

@h1 … h1@ becomes an <h1>; @blockquote … blockquote@ becomes a <blockquote>. The renderer decides whether a node is block or inline, so you don't. Nodes that hold nothing — a rule, an image — close themselves:

@hr

@img |src ./lamp.png| |alt The keeper's lamp|

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:

source
@ul
  @li First li@
  @li Second li@
ul@
result
  • First
  • Second

Written flush-left it would mean exactly the same thing; wit fmtre-indents to match the nesting without changing a single relationship. Forget a closer and the build stops with E_UNCLOSED_NODE pointing at the opener.

Body carries content; parameters carry metadata

You've seen @a |href …| text a@: the address rides in a parameter, the link text is the body. That split is the governing rule of the language — if it's what the reader reads, it's the body; if it only describes the node, it's a parameter. A parameter never carries a node's content.

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@code, @u, @s, @sub, @sup, @mark, @small, @br, plus the bold and italic shortcuts.
  • Lists@ul, @ol, @li; definition lists @dl / @dt / @dd.
  • Blocks@p, @blockquote, @pre, @hr.
  • Media@a, @img, @figure, @figcaption, @audio, @video.
  • Sectioning@section, @article, @aside, @header, @footer, @nav, @main.
  • Tables@table and its parts.
  • Layout@row, @col, @div, @span.

The syntax & core-vocabulary reference lists all 52 with their exact output. When the core doesn't have what you need, you 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.

  1. Prose — ordinary paragraphs; a blank line starts a new one. A document of nothing but prose is already valid.
  2. Nodes — the named structures above.
  3. Definitions — name a chunk of content once with #, reuse it anywhere with a bare @name.
  4. Data — records { key - value } and collections [ … ], pulled into prose with @name.field.
  5. Logic(if …) and (each … as …) shape a document from its data; a <% … %> script is the one escape hatch for anything more.

Here is one document climbing all five:

source
@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)
result

The Lamp Keeper

Ledbury Point has kept the light for 11 years.

The heading is a node (layer 1) around prose(layer 0); #station is a definition holding data(layers 2–3); @station.name pulls data into prose(layer 3); the (if …) is logic (layer 4). Wit is deliberately not Turing-complete — the logic layer shapes a document from its data; it is not a programming language in disguise.

See also