Skip to main content

Advanced: variable resolution & scoping

Jess has two variable-reference forms:

ReferenceResolution model
$fooLive: reads the current binding at evaluation time.
$^fooScoped/final lookup.

$foo: live​

Use $foo when the value should follow the current binding at the point where it is evaluated.

$^foo: scoped/final​

Use $^foo when you want the scoped/final lookup explicitly.

Missing names​

Both forms report an unresolved name as a ReferenceError unless the caller is performing an explicit optional/existence operation.

Declaration and assignment lookup​

FormBehavior
$foo: value / $^foo: valueCreate or update both bindings.
$foo?: valueCheck the live binding; create/update both if absent.
$^foo?: valueCheck the scoped/final binding; create/update both if absent.
$foo := valueUpdate the live/current binding. Errors if it is unbound.
$^foo := valueUpdate the scoped/final binding. Errors if it is unbound.
$foo ::= valueUpdate the live/current binding if it exists; otherwise declare a block-local one.
$^foo ::= valueUpdate the scoped/final binding if it exists; otherwise declare a block-local one.

::=: optional shadowing​

::= reads as ": or :=". It always writes: it updates the nearest existing binding (like :=), but where := errors when the name is unbound, ::= instead declares a new block-local binding (like :). A name born this way is local to its block and does not leak past the closing brace.

Not recommended for hand-written .jess

Prefer the explicit operators when you write Jess by hand: : when you mean "a new local binding" and := when you mean "update an existing one" (and want an error if it is missing). ::= exists mainly for Sass-conversion support — a bare $x: inside a Sass control-flow block (@if/@each/@for/@while) is imperative reassignment, and the SCSS front end lowers it to ::= so accumulator loops such as $sum: $sum + $x behave the way dart-sass evaluates them.