Syntax reference
Every surface form Wit accepts, with its canonical shape, the AST node it produces, and the rule that closes it. This is the "how do I write X" index. For depth on any form, follow its guide link; for the exact node HTML, see the core vocabulary.
Node call forms
A call invokes a node. The parameter style you open with is the style that
closes the call — mixing them raises E_MIXED_PARAM_SOURCE.
| Form | paramsSource | closeStyle | Fixture |
|---|---|---|---|
| Bare reference | none | bare | 04 bare-reference |
| Named body block | none | named | 04 block-name-body |
| Named body inline | none | named | 04 inline-name-body |
| Dotted data access | none | bare | 04 dotted-access |
| Parens | parens | parens | 05 single-named-param |
| Pipes | pipes | named | 06 basic-named |
| Record-arg | record | parens | 22 inline-single-field |
| Form-fill body | form-fill | named | 23 template-invocation |
| Colon-scatter | pipes | named | 24 body-scatter-single |
| Flag param | pipes / parens | — | 06 flag-with-bang |
| Opaque pass-through | parens | parens | 20 bare-node-passthrough |
| Raw literal | none | named | literal-nodes |
| Frozen literal | none | named | ast.ts frozen |
Bare reference
Resolves to a same-named definition. Binds nothing → an unresolved span.
@nameNamed body — block and inline
Opener and matching close on their own lines is a block use
(body is Block[]). Mid-paragraph it is an inlineuse (inline: true, body is Inline[]).
@blockquote A quotation, set apart. blockquote@ An @em inline em@ mark sits mid-sentence.
A quotation, set apart.
An inline mark sits mid-sentence.
Dotted data access
Binds a data def, not a node def. Numeric segments index a
collection. Unresolved → <span class="wit-unresolved">@a.b</span>.
@author.name @sales.0.region
Parens
Self-closing. Space-separated or colon keys. A multi-word colon value must be quoted.
@img(src photo.jpg, alt A photo) @h2(id intro, class lead) Title h2@ @x(k: "a b")
Pipes
Each pipe is one parameter; duplicate keys are last-one-wins. Closed by name@.
@a |href /docs| |target _blank| Read the docs a@
Record-arg
An inline or multi-line record as the argument; self-closing. Hyphen or colon delimiter is accepted at a call site.
@card { title - Q3, status - shipped }Form-fill body
A bare opener followed by key: value lines. Needs two or more content lines, or it is read as prose.
@contact name: Ada Lovelace role: Author contact@
Colon-scatter
A single-line body of k:v tokens (no space after the colon).
Surfaces internally as paramsSource: pipes. A space after the
colon is prose.
@badge status:new priority:high badge@
Flag param
A valueless parameter: a trailing bang in a pipe, or a bare flag in parens.
@task |title Ship| |urgent!| task@
Opaque pass-through
The universal container. If type names a core-vocab node it renders
as that element; otherwise it emits a <div> carrying every named
param as data-*.
@node(type h2) A heading node@ @node(type box, role note) Body node@
A heading
Body
Raw and frozen literals
@@name … name@@ makes the body one verbatim Text node
(raw); {{path}} holes still expand. @@@name … name@@@ adds frozen — even {{…}} passes through untouched.
@@style .lead { font-size: 1.2rem } style@@
Definition forms
A definition declares a reusable node or a named value. Opens with a hash.
| Form | AST shape | Note | Fixture |
|---|---|---|---|
| Single-line def | single-line | one-line template | 07 single-line-def |
| Value-block def | value-block | closed by a double bang | 07 multi-line-value |
| Block def | block | uses the body slot | 07 block-definition |
| Capture list | block | declares captures | 07 multi-capture-list |
| Additive partial | block / single | merges across the file | 08 simple-additive-prefix |
| Data def record | dataDef | hyphen delimiter required | 09 records |
| Data def collection | dataDef | ordered list | 10 collections |
| Data def scalar | dataDef | number / bool / null / prose | 09 scalar-types |
#lead: A one-line reusable value. #abstract: A multi-line value, closed by two bangs. !! #callout ||label, body|| @aside(class ::label::) ... aside@ callout# +#authors: Ada +#authors: Grace #site: { name - Docs, year - 2026 } #tags: [ ref, spec, wit ] #count: 42
Other constructs
| Construct | Renders as | Fixture |
|---|---|---|
| Emphasis | strong / em | 02 emphasis |
| Interpolation | the captured param | 07 captures-and-interpolation |
| Raw-body hole | expand-time value in raw bodies | literal-nodes |
| Body slot | the invocation body | 07 body-slot |
| Comment | omitted from output | 03 comments |
| Reference directive | pulled-in defs | 15 references |
| Conditional | kept or dropped body | 12 conditionals |
| Iteration | repeated body | 13 iteration |
| Script | effects only — no output | 15 scripting |
| Escape | the literal char | 00 lexical |
| External data | a bound data def | data-loader |
Wrap *bold* and _italic_ inline. ::name:: {{theme.accent}} ~ this line is a comment reference "shared/defs.wit" (if @author) By @author.name (end) (if @status is shipped) Live (else) Draft (end) (each @tags as tag) @li ::tag:: li@ (end) <% total = lh.data.a + lh.data.b %> \* a literal asterisk, not a mark @load env load@ @load sales |as rows| |region West| load@
The equivalence proof
One template, every call form: fixture 26-all-param-forms/showcase.wit invokes the same node thirteen ways
and asserts identical output. Form 13 (self-closing pipes) is placed last on purpose — pipes with no same-line close greedily bind to
the next close, so ordering is a real constraint, not a parser quirk.
See also
Core vocabulary· Data model · Tables · Gotchas · Glossary · Cheatsheet.