Expressions
A value is an expression: names resolve through keywords, modules, built-ins and $bindings; operators follow JS; accessors never throw.
Evaluation
- Names resolve in order: value keywords →
@useexports (bareas *exports, then namespaces) → built-in functions →$bindings. A$bindingis read by walking up from the matched element to the nearest ancestor-or-self that holds it; an unbound$nameisundefined(a wipe; while aprerendered page hydrates, what is painted is kept instead), while an unknown bare identifier is an error (a no-op). A module export shadows a built-in of the same name. - Literals: unitless numbers are numbers; a number with a unit (
10px,50%) and a color (#ccc) evaluate to strings. Strings unescape\n,\t,\r, and\x→x. Interpolated strings andurl(…)join their parts, withnull/undefinedparts rendered empty. &in an expression is the matched element's tag name.- Accessors never throw:
.fieldand[index]onnull/undefinedyieldundefined.object.$namereads the property literally named$name(namespaced variables:math.$pi). - Calls: a bare callee resolves through the name order above and must be a function. A method call on a value (
item.name.trim()) is allowed for own-property functions (a@usenamespace, a provided object) and for theallowed prototype methods ; anything else is an error. Calling a method onnull/undefinedyieldsundefined. Named arguments are passed positionally (the name is ignored);$list...spreads an array. - Operators:
and/orshort-circuit and return an operand (JS semantics, so$title or preserveworks);notreturns a boolean;==/!=are loose;+concatenates when either side is a string (prefer#{$x}interpolation for building strings);-*/%and comparisons follow JS. if()returns the value of the first arm whose condition is truthy; with no match and noelseit isundefined(a wipe).ternary()is the two-arm function form.- Lists (space or comma separated) evaluate to arrays; maps to objects, with bare identifier keys taken literally (
(name: "Ada")→{ name: "Ada" }) and other keys stringified. - Results: listeners and
$variablesapply synchronously; attributes, CSS variables, and content are painted in a batch. Module functions are called synchronously and what they return is not awaited:contentrefuses a promise (seeAsynchronous work ).
Example
[data-user] {
$user: prop("provision");
label: $user.profile.name or "Anonymous";
title: if($user.role == "admin": "Administrator"; else: $user.role);
data-tags: $user.tags.join(", ");
--progress: "#{($user.done / $user.total * 100).toFixed(0)}%";
}[data-user] {
$user: prop("provision");
label: $user.profile.name or "Anonymous";
title: if($user.role == "admin": "Administrator"; else: $user.role);
data-tags: $user.tags.join(", ");
--progress: "#{($user.done / $user.total * 100).toFixed(0)}%";
}Facts over if()
Several rules branching on the same condition with if() is a smell: derive the condition once as an attribute (data-is-admin: $user.role == "admin"; — a boolean writes "" or removes the attribute) and select on it from Quark and CSS.