One signal namespace holds all state.data-signals:ns="{ ... }" on the
wrapper. Everything reads/writes $ns.field.
Bake the structurally-constant DOM. Datastar has no client-side loop or
template. A month grid always has 42 day cells; a numpad has 12 keys; a
combobox list has N options. Emit those nodes at author/build time (a Python
f-string loop, a server template — anything that runs before the page
ships). The DOM shape is fixed; only its content and state change.
Props down via pure helpers; events up via data-on. Plain JS functions
take signal values as arguments and return a value/boolean
(cellIsToday($cal.v, i), cbFilter($cb.q)). They never read globals or
mutate — they're props-down. data-on:click is events-up: it mutates the
signal, the bindings recompute.
Layout from system.css primitives in the markup; the rest from the API.
See "Don't hand-roll layout" below.