${ ... }
The logic context. Switches grammar from markup mode to expression / statement mode. Where reactive computation, control flow, and inline state writes happen.
Syntax
${ statementOrExpression }
${ @cell }
${ @count + 1 }
${ function name() { ... } }
${ if (cond) { ... } }
Inside a logic context, the grammar is statement-and-
expression scrml. Outside (markup mode), text and
elements are the grammar.
${...}
is the universal switch from markup → logic; the
closing
}
returns to markup. Inside logic, markup may re-open via
element literals like
<span>...</span>
(markup-as-value per L1).
Worked example
A counter with derived computation, an event handler, and
markup-as-value. Three uses of
${...}
in one snippet.
<count> = 0
const <label> = ${ "Clicked " + @count + " times" }
<button onclick=${ @count = @count + 1 }>
${ @label }
</button>
<p>Last bump: ${ @count > 0 ? <strong>${@count}</strong> : "never" }</p>
The button's
onclick=${...}
wraps a single assignment statement; the body's
${@label}
is a reactive interpolation; the the paragraph's ternary
returns either markup (markup-as-value) or a string. Each
use is a logic context the compiler tracks for dependency
wiring.
Semantics
-
V5-strict access inside logic.
@namereads or writes a reactive state cell;name(bare) resolves to a function-scoped local. Bare names NEVER resolve to reactive state — that is the load-bearing point of the@marker. -
Reactive interpolation tracks dependencies at
compile time. Every
${ @cell }in markup-mode position subscribes the surrounding node to that cell. Writes to the cell trigger re-emit of exactly that node. No runtime diffing. Per SPEC §31 (Dependency Graph). -
Statement vs expression position.
${...}in markup body position accepts statements (declarations, control flow, blocks) AND expressions. In attribute-value position (onclick=${...}), the body must be a single expression unless wrapped in a function (E-MULTI-STATEMENT-HANDLER on inline multi-statement). Per SPEC §5.2.3 + L19. -
Markup-as-value is bidirectional.
Inside a logic context, element literals
(
<div>...</div>) are first-class expressions. They can be passed as arguments, returned from functions, stored in cells. Per L1 (markup-as-first-class-value). -
File-level logic context. A
${...}at file top level is the canonical place for module-scope declarations (function,fn,const,type,import). Hoisting per SPEC §6.9; pinned per §6.10. -
Assignment is an expression. Per SPEC
§50,
@count = @count + 1returns the assigned value. Useful for inline event handlers and lift accumulation. The composition with markup-as-value lands in §50.14.
Common errors from logic context misuse
- E-NAME-COLLIDES-STATE — local shadows registered state cell name
- E-SCOPE-001 — undeclared identifier in logic expression
- E-MULTI-STATEMENT-HANDLER — multi-statement body in attribute-value position
- E-FN-001…009 — impure work inside a pure scope
- E-EQ-004 — === / !== forbidden; scrml uses == / != per §45
Edge cases
-
Nested
${ <el>${...}</el> }. Logic can open markup which can open more logic. The compiler tokenizes brace depth per context; arbitrary nesting is legal. Per SPEC §7.7 (logic-markup interleaving canonical form, M8). -
Bare names are locals, not state.
The most common new-adopter mistake:
${ count }instead of${ @count }. The first reads a function-localcount; if no such local is in scope, fires E-SCOPE-001 with a "did you mean @count?" hint. -
pure scope.
puremarks a logic region that cannot read reactive state, cannot perform server-fn calls, cannot have failable effects.fnis the pure-function form (no implicit reactive deps). Per SPEC §33 + §48. -
Comments.
//line comments and/* ... */block comments are valid inside logic contexts. The universal//line-comment form per SPEC §27 also works in markup mode.
Related contexts
- ?{ ... } — SQL context. Per-handler coalescing, N+1 loop hoist, multi-database adaptation.
- #{ ... } — CSS context. Inline styles + scoped classes. Reference page queued.
- ^{ ... } — meta context. Compile-time code execution + splicing. Reference page queued.
- _{ ... } — foreign code context. Level-marked braces; WASM / sidecar interop. Reference page queued.
Availability
| Surface | Since | Notes |
|---|---|---|
| ${} logic context | Core since v0.1 | The foundational context; SPEC §7 |
| V5-strict access (@ + bare) | v0.2.0 ratification | SPEC §6.1 — bare names are locals only |
| Bare-form event handlers | D4 (2026-05-04) | SPEC §5.2.3 — single-expression discipline + E-MULTI-STATEMENT-HANDLER |
| Assignment as expression composes | SPEC §50.14 / .15 | Composes with markup-as-value (L1) + bare-form handlers (L19) |
Specification
This page summarizes SPEC §7 (Logic Contexts), plus the V5-strict access discipline from §6.1 + §1.6. The normative text lives at compiler/SPEC.md in the scrmlTS repository.
Quick-lookup anchors: SPEC-INDEX.md entries — logic context, V5-strict access, markup-as-expr, file-level scope sharing, logic-markup interleaving, assignment as expression.