Reactive Markdown

This page opts into the runtime because it declares state. Static pages ship zero framework JavaScript.

The count is 0.

The details are hidden.

Keyed loops over real objects

The same @loop that unrolls static data also drives reactive lists. Items are objects, bindings use dotted paths, and the runtime patches the list by key instead of re-rendering it:

Conditionals inside a loop, nested

:if works per row, and conditionals nest — the inner branch is decided after the outer one, for every item, and stays reactive. Add a "done" task and watch which badge each row shows:

Draft the spec

done ✓

Wire the runtime

blocked ✗

Write the demo

in progress …

Each row picks one of three states from two nested conditionals, all in plain Markdown — no component, no ternary, no JavaScript.

Live filtering — search in pure Markdown

@loop … where filters a list, and because the predicate reads a :state value, the loop re-filters live as you type. :bind is the input bound two-way to that state. Type below and watch the list narrow — no event handlers, no JavaScript:

Aurora Lamp — $49

Briza Fan — $39

Cove Speaker — $89

Dune Mug — $12

Ember Kettle — $64

The predicate whitelist is compile-time validated — item fields, declared state, numbers, and strings only. An item-only predicate (say where p.price < 50) would instead filter at build time and ship zero JavaScript.

…and an editable cart

A :button inside a loop can act on its own row. Add to cart carries the row into another list; Remove drops a line — a whole add/remove flow with no JavaScript:

Section-scoped state

Two sections can each own a state value named tally without colliding. Buttons resolve to the nearest scope:

Left section

The left tally is 0.

Static pages like the docs stay runtime-free.