Skip to main content

Functions

info

This section refers to functions defined in stylesheets. For importing functions from other languages, see the @-use and @-from at-rules.

In Jess, a function is not a new kind of statement — it is a value. You write an anonymous mixin that returns a single value, and you bind it to a variable like any other value. We indicate we want a single value with >, and the return value is then assigned with result. (This matches the CSS @function spec, where the return descriptor is result:.)

$my-function: @() > {
result: $(1 + 2);
}

.box {
value: $my-function();
}

.box {
value: 3;
}

Because the definition is a block-valued assignment, it auto-terminates at its closing brace — the ; above is optional. See block-valued assignments.

Expression bodies

Like arrow functions in JavaScript, an anonymous function can return a single expression instead of a block.

$my-function: @() > $(1 + 2);

.box {
value: $my-function();
}

.box {
value: 3;
}

This is exactly sugar for a body whose only statement is result: <expression>; nothing downstream can tell the two spellings apart.

An expression body is not a block, so it does not get the block exception: it still needs a ; to separate it from a following declaration. See block-valued assignments.

Parameters, defaults, and named arguments

@(…) takes the same parameter list a named mixin declares, and a call binds through the same argument binder a named mixin call uses. So you get positional arguments, named arguments, and parameter defaults with no extra rules to learn.

$add: @($a, $b: 10px) > {
result: $($a + $b);
}

.box {
width: $add(1px);
height: $add(1px, 2px);
margin: $add($b: 2px, $a: 1px);
}

.box {
width: 11px;
height: 3px;
margin: 3px;
}

$add(1px) lets $b fall back to its default, $add(1px, 2px) binds positionally, and $add($b: 2px, $a: 1px) binds by name — so named arguments may appear in any order.

Arity is checked

Passing the wrong number of arguments is an error (eval/bad-call-arity), not a silent fallback to the literal call text:

$double: @($n) > $($n * 2);

.box {
value: $double(1, 2); // error: function expects 1 args, got 2
}

A body that never assigns result: is also an error (eval/invalid-function) — the call has no value to yield:

$broken: @($n) > {
width: $n; // never assigns `result:`
}

.box {
value: $broken(1px); // error
}

No early returns

Functions do not have "early" returns — the last resolved result: assignment wins. A result: inside an $if branch does not short-circuit; evaluation continues, and a later result: overrides it.

$pick: @() > {
$if(true) {
result: one;
}
result: two;
}

.box {
value: $pick();
}

.box {
value: two;
}

Think of functions like "mixins that look up the final assignment to a property named result".

Functions are first-class values

A function literal is a value and $my-function is an ordinary variable that happens to hold one. Referencing it without a call parenthesis reads the function itself, so you can alias it:

$double: @($n) > $($n * 2);
$alias: $double;

.box {
value: $alias(4);
}

.box {
value: 8;
}

…and you can pass it to another function and call it there. A function received as a parameter is bound by reference, so it stays callable:

$double: @($n) > $($n * 2);

$twice: @($fn, $v) > {
result: $fn($fn($v));
}

.box {
value: $twice($double, 3);
}

.box {
value: 12;
}