tramaj
A small template language for turning a JSON value into a document tree.
Templates are meant to be comfortable to write by hand and easy for an LLM
to emit: a HAML-like block syntax for structure, a jq-flavoured expression
language for the data half, and a deliberately tiny builtin set. Rendering is
decoupled from the language — evaluation produces a generic Node AST, and
it is the host that decides whether that becomes Halogen HTML, JSON, or
anything else.
@items=$ctx.items
@n=cardinality($items)
.div(class: "items",
.h1("Items"),
.p("there are `$n` of them"),
.ul(map($items, (i) => .li(action("on-click", "select", {"id": $i.id}), $i.name))))
Bindings are evaluated once, in order. `$n` interpolates into a
string; a bare $path can be used directly as a child. action(...)
attaches an opaque, structured action to an element — the language
recognises the syntax but assigns it no meaning, leaving the host to map the
key onto a real event handler.
The whole pipeline is short. A template is parsed into a Program, which
is evaluated against a JSON context (and, for import(...), a library
table) into a Node; the host folds that Node into whatever it renders.
The dashed branch is what makes tramaj unusual: a program’s imports,
context reads and action keys can be read off the Program without
evaluating it.

Go straight to
specs/reference.md
on GitHub for the full spec, or explore the site below.
Explore
- What sets tramaj apart — documents as
values,
ctx(...)as a declared hole, and why that makes a program’s imports and actions inspectable without running it. - Compared to other tools — how tramaj differs from string templates, JSX and configuration languages, and when to pick something else.
- Properties — the invariants tramaj is held to: portability, cheap static analysis, determinism.
- Use cases — where a small document-composing language fits: static sites, real UIs, LLM-generated templates, constraint-driven and typed generation.
- Reference — the language specification, the
Nodewire format, and the v3/v4 extensions. - Playground — try it in the browser, nothing sent to a server.
- Getting started — install the toolchain, write and run a first template.
- Integrating tramaj — embedding it as a library in PureScript, Haskell, Rust, JavaScript, Python, or anything else.
- For coding agents — a dense syntax cheatsheet and fetch strategy for an LLM emitting tramaj templates.
- Roadmap — where the five implementations stand, and the folds and registry releases that are wanted next.