LESS variables don't behave like variables in most programming languages. This lesson explores lazy evaluation in depth: LESS resolves a variable's final value based on its last declaration in scope, not the order code executes.
What Does Lazy Evaluation Actually Mean?
In most languages, a variable's value at any point is whatever was most recently assigned up to that point in execution order. LESS works differently: within a given scope, the value used everywhere is determined by the last declaration written in that scope, regardless of whether that declaration appears before or after the line using the variable.
This design exists because LESS resolves a stylesheet more like a declarative document than a sequential script, similar to how CSS itself lets a later rule override an earlier one regardless of how you mentally read the file top to bottom.
.box {
width: @size; // does NOT use 10px, despite appearing after this line
}
@size: 10px;
@size: 20px; // this is the LAST declaration in scope, so it wins
// .box compiles to: width: 20px;
Even though the usage appears textually before either declaration, LESS resolves @size using the last declaration anywhere in the same scope.
The Lazy Evaluation Rule
// Rule: within one scope, a variable's usage always
// resolves to its LAST declaration in that same scope,
// regardless of source order.
Declaration order relative to usage doesn't matter within the same scope.
The last declaration in a given scope always wins, even if it appears after every usage.
Nested scopes (inside a selector) can locally override an outer variable without affecting the outer scope.
This behavior differs sharply from Sass, where variable assignment is evaluated eagerly, in top-to-bottom order.
Lazy Evaluation Cheatsheet
Quick reference for how lazy evaluation resolves in common scenarios.
Scenario
Result
Why
Two declarations, same scope
Last one wins
Order relative to usage doesn't matter
Declared after usage
Still resolves correctly
LESS looks at the full scope, not execution order
Declared inside a nested selector
Local to that selector only
Nested scopes don't leak outward
Same variable, unrelated selectors
Each selector's own last declaration applies
Scoping is per selector block
Why LESS Was Designed This Way
Lazy evaluation allows a later part of a file, or an imported file, to override an earlier default value cleanly, which is useful for building configurable, override-friendly mixin libraries and themes without needing an explicit !default-style flag on every variable.
// _library-defaults.less
@library-color: blue; // library's built-in default
// main.less
@import "library-defaults";
@library-color: green; // overrides the default before it's used anywhere
Scope Boundaries Still Apply
Lazy evaluation operates within a scope, it doesn't mean a variable declared anywhere in a file affects every other file or selector. A variable declared inside a selector's block is still local to that block and any nested rules within it.
The most common source of confusion is assuming a variable declared partway through a file only affects code after that point, as it would in JavaScript. In LESS, that later declaration can retroactively change what an earlier-appearing usage compiles to.
A variable redeclared near the bottom of a file can silently change behavior earlier in that same file.
This is rarely an issue in small files, but becomes important in long files with many redeclarations.
Keeping one variable declared only once per scope avoids the ambiguity entirely.
Common Mistakes
Assuming a LESS variable behaves like a JavaScript variable, only affecting code that appears after its declaration.
Redeclaring the same variable multiple times within one large file and losing track of which declaration actually wins.
Not realizing a variable declared inside a nested selector is scoped locally and doesn't leak to sibling selectors.
Relying on lazy evaluation's override-friendliness without documenting which values are meant to be overridden.
Key Takeaways
LESS resolves a variable's value based on its last declaration within a scope, not execution order.
This allows later code (or an importing file) to override an earlier default cleanly.
Variables declared inside a selector's block remain scoped to that block and its nested rules.
Declaring the same variable only once per scope avoids the most common lazy-evaluation confusion.
Pro Tip
If a mixin library is meant to expose overridable default variables, declare each default only once, clearly commented, and document that consumers should override it via a later @import-order declaration, this makes the override-friendly lazy evaluation behavior an intentional feature rather than a surprise.
You now understand LESS's lazy evaluation model in depth. Next, learn how the LESS plugin system extends the compiler with custom functions.