tramaj

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.

From template source to a rendered document: parse, evaluate against a context, fold; static analysis branches off the Program

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 Node wire 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.