Conditionals and iteration
Similar to JavaScript, reserves a few $ keywords to use for conditional evaluation and recursive iterations (loops).
$if, $else, $else if
These conditionals evaluate pretty much how you would expect. Note that even though control flow names start with $, they are not expressions, and expressions after them must be started with $.
Examples
1. Theme Switching
$theme: "dark";
$if ($theme = "dark") {
body { background: #111; color: #eee; }
} $else {
body { background: #fff; color: #222; }
}
2. Feature Flags for Styles
$flags: {
enable-fancy-shadows: true;
}
.card {
border-radius: 8px;
$if ($flags.enable-fancy-shadows) {
box-shadow: 0 8px 32px #0002;
}
}
3. Seasonal settings
$season: "spring";
$if ($season = "spring") {
body { background: #d8f5d1; }
} $else if ($season = "summer") {
body { background: #ffe066; }
} $else if ($season = "autumn") {
body { background: #ffbe76; }
} $else {
// winter (or whatever else you want to handle)
body { background: #a5b8e7; }
}
4. Dumb Easter Eggs (Because You Can)
$shouldTroll: false;
$if ($shouldTroll) {
h1 { font-family: "Comic Sans MS"; color: hotpink; }
}
Block scoping
Rules and declarations written inside a conditional or iteration block are merged into the containing block, so they need no extra nesting:
.box {
color: red;
$if (true) {
background: blue;
}
}
→
.box {
color: red;
background: blue;
}
Variables are scoped differently from rules. A control-flow block opens a scope of its own, so a : declaration inside one is local to that block. Reassign with := when you mean the variable outside it:
.box {
$my-color: red;
$if (true) {
$my-color := blue; // updates the outer binding
}
color: $my-color; // blue
}
With $my-color: blue inside the $if, the block would get its own $my-color and color would be red. See Scope.
$if and $whileToday only $for enforces variable scoping in control-flow blocks, and := inside $for fails to find an outer binding — jess#232. Writing := for "update the outer variable" is correct either way.
$for
$for is Jess's source-dependent iteration protocol, in the spirit of JavaScript
for…of: the source determines the entry shape, and the header says which parts
of that entry the loop names. Lists and ranges supply values in source order;
collections can supply key/value entries. This is Jess syntax, not a spelling of
Less each().
$sections: header, sidebar, footer;
$for ($section, $i of $sections) {
.box-${section} {
padding-left: $($i * 20px);
}
}
You can also iterate over a range of numbers:
$for ($i of 1 to 3) {
.box-${i} {
value: $i;
}
}
A range is inclusive of the start and end values. To exclude the end value, you can use this familiar syntax from other programming languages:
$for ($i of 1 to <3) {
.box-${i} {
value: $i;
}
}
You can also destructure a collection entry:
$collection: { header: red; footer: blue; };
$for ([$key, $value] of $collection) {
.box-${key} {
value: $value;
}
}
In the first example, i is the key of sections. Because sections is a
list, that key is its 1-based source-order position.
The single-name form names the entry value. The comma form may also name the
source key and loop counter. For a list, the key and counter have the same
source-order position; for a Jess collection, the key is the declaration name.
Bracket binding is key/value destructuring for collection entries, so
[$key, $value] names the collection key first and its value second. The
canonical header shapes are $for ($item of …),
$for ($item, $key, $counter of …), and
$for ([$key, $value] of …).