Skip to content

LESS Mixin Guards

LESS has no @if or @else statement. Instead, it uses guarded mixins: multiple definitions of the same mixin, each with a when condition, and only the matching definitions apply. This lesson introduces the guard mechanism in depth.

What Is a Mixin Guard?

A guard is written after a mixin's parameter list using the when keyword, followed by a condition in parentheses. When the mixin is called, LESS evaluates each guarded definition's condition and only applies the ones that evaluate to true.

This design keeps LESS declarative, similar to how @media query conditions work, rather than introducing imperative if/else control flow into a styling language.

.badge(@bg) when (lightness(@bg) >= 50%) {
  color: black;
}
.badge(@bg) when (lightness(@bg) < 50%) {
  color: white;
}
.badge(@bg) {
  background: @bg;
  padding: 0.25rem 0.5rem;
}

.warning { .badge(#facc15); } // light background, gets black text
.danger { .badge(#7f1d1d); }  // dark background, gets white text

Both guarded definitions and the unguarded definition apply together whenever their conditions match, or always, for the unguarded one.

Guard Syntax

.mixin(@a) when (condition) {
  property: value;
}
  • The when keyword introduces a guard condition immediately after the parameter list.
  • A mixin definition without a guard always matches, and can coexist alongside guarded versions of the same name.
  • Multiple guarded definitions of the same mixin name can all match and apply simultaneously if their conditions overlap.
  • The keyword true is the only truthy value; .mixin(@a) when (@a) is shorthand for .mixin(@a) when (@a = true).

Mixin Guards Cheatsheet

Core guard patterns you'll use across conditional mixins.

Pattern Example Notes
Comparison guard when (@a > 0) Numeric comparison
Type-check guard when (iscolor(@a)) Matches only if @a is a color
Boolean guard when (@a) True only if @a is exactly the keyword true
AND logic when (@a > 0) and (@a < 10) Both conditions must be true
OR logic when (@a > 10), (@a < -10) Comma acts as logical or
NOT logic when not (@a = 0) Negates the condition
Unguarded fallback .mixin(@a) { } Always matches, no when clause

Combining Guards with Logical Operators

Guards support and to require multiple conditions simultaneously, a comma , to act as a logical or (matching if any listed condition is true), and not to negate a condition.

.status(@code) when (@code >= 200) and (@code < 300) {
  color: green; // success range
}
.status(@code) when (@code >= 400), (@code >= 500) {
  color: red; // client or server error
}

Type-Checking Functions in Guards

LESS provides built-in is* functions to branch mixin behavior based on the type of an argument, useful when a mixin should accept either a color or a keyword like none.

.border(@value) when (iscolor(@value)) {
  border: 1px solid @value;
}
.border(@value) when (iskeyword(@value)) {
  border: @value;
}

.card { .border(#e5e7eb); } // border: 1px solid #e5e7eb;
.frame { .border(none); }   // border: none;

iscolor(), isnumber(), isstring(), iskeyword(), isurl(), and unit-specific checks like ispixel() are all built in.

Matching on Exact Argument Values

Guards can compare an argument against a literal keyword or value, which is a common pattern for building a small, guard-based variant system, similar to a switch statement.

.variant(@type) when (@type = primary) { background: #2563eb; }
.variant(@type) when (@type = success) { background: #16a34a; }
.variant(@type) when (@type = danger)  { background: #dc2626; }

.btn { .variant(success); } // background: #16a34a;

Common Mistakes

  • Trying to write an @else clause; LESS has no such syntax, use another guarded definition with the opposite condition instead.
  • Forgetting that multiple guarded definitions with overlapping conditions can all match and apply together, not just the first match.
  • Using = for a boolean check instead of the simpler when (@a) shorthand when a value is expected to be the keyword true.
  • Confusing and (all conditions must match) with , (any condition matches), which behave oppositely.

Key Takeaways

  • Guards use the when keyword to conditionally apply a mixin definition based on its arguments.
  • Multiple guarded definitions can coexist, and more than one can match and apply for a single call.
  • and requires all conditions to be true; a comma , acts as a logical or between conditions.
  • Built-in is* functions let guards branch based on an argument's type, not just its value.

Pro Tip

When building a guard-based variant system (primary, success, danger), always add an unguarded fallback definition too, so unexpected argument values still receive sensible default styles instead of silently matching nothing.