Skip to content

Less Language Detached Rulesets

Andrey Alexandrov edited this page Nov 9, 2019 · 41 revisions

Detached ruleset is a group of css properties, nested rulesets, media declarations or anything else stored in a variable. You can include it into a ruleset or another structure and all its properties are going to be copied there. You can also use it as a mixin argument and pass it around as any other variable.

Simple example:

// declare detached ruleset
@detached-ruleset: { background: red; };

// use detached ruleset
.top {
    @detached-ruleset(); 
}

compiles into:

.top {
  background: red;
}

Parentheses after detached ruleset call are mandatory. The @detached-ruleset; call would NOT work.

Content

Detached ruleset can contain everything an ordinary ruleset can: properties, nested rulesets, media declarations, variables declarations, mixins, etc. Detached ruleset call unlocks (returns) all its mixins into caller the same way as mixin calls do. However, it does NOT return variables.

Media inside detached ruleset:

// detached ruleset with nested media
@detached-ruleset: { 
    @media print {
    	.nested-ruleset {
            color: blue;
    	}
    }
};
// call detached ruleset
.caller {
    @detached-ruleset(); // use detached ruleset
}

compiles into:

@media print {
  .caller .nested-ruleset {
    color: blue;
  }
}

Detached ruleset copies all its mixins into caller:

// detached ruleset with a mixin
@detached-ruleset: { 
    .mixin() {
        color:blue;
    }
};
// call detached ruleset
.caller {
    @detached-ruleset(); 
    .mixin();
}

compiled css:

.caller {
  color: blue;
}

It does NOT return variables, following results in an error:

detached-ruleset: { 
    @color:blue; //this variable is private
};
.caller {
    color: @color; //syntax error
}

Passed As Argument

Detached ruleset can be used as mixin argument. Next mixin wraps its argument into media declarations:

//mixin assumes that @rules parameter contains detached ruleset
.big-screen-exception(@rules) {
    @media screen and (min-width: 1200) {
    	//use detached ruleset from parameter
        @rules();
    }
}
.caller {
    color: red;
    //detached ruleset is sent as parameter
    .big-screen-exception({ color:blue a; });
}

compiles into:

.caller {
  color: red;
}
@media screen and (min-width: 1200) {
  .caller {
    color: blue a;
  }
}

Detached ruleset as default parameter:

.mixin(@parameter: {default: default;}) {
  @parameter();
}

Scoping

Detached ruleset can use all variables and mixins accessible on place where it is defined and where it is called. Otherwise said, both definition and caller scopes are available to it. If both scopes contains the same variable or mixin, declaration scope value takes precedence.

Declaration scope is the one where detached ruleset body is defined. Copying detached ruleset from one variable into another can not modify its scope. Ruleset does not gain access to new scopes just by being referenced there.

Lastly, detached ruleset can gain access to scope by being unlocked (imported) into it.

Definition and Caller Scope Visibility

Detached mixin sees callers variables and mixins:

@detached-ruleset: {
  caller-variable: @callerVariable; // variable is undefined here
  .callerMixin(); // mixin is undefined here
};

selector {
  // use detached ruleset
  @detached-ruleset(); 

  // define variable and mixin needed inside the detached ruleset
  @callerVariable: value;
  .callerMixin() {
    variable: declaration;
  }
}

compiles into:

selector {
  caller-variable: value;
  variable: declaration;
}

Variable and mixins accessible form definition win over those available in caller:

@variable: global;
@detached-ruleset: {
  //will use global variable, because it is accessible
  //from detached-ruleset definition
  variable: @variable; 
};

selector {
  @detached-ruleset();
  @variable: value; //variable defined in caller - will be ignored
}

compiles into:

selector {
  variable: global;
}

Referencing WONT Modify Detached Ruleset Scope

Ruleset does not gain access to new scopes just by being referenced there:

@detached-1: { scope-detached: @one @two; };
.one {
  @one: visible;
  .two {
    @detached-2: @detached-1; // copying/renaming ruleset 
    @two: visible; // ruleset can not see this variable
  }
}

.usePlace {
  .one > .two(); 
  @detached-2();
}

throws an error:

ERROR 1:32 The variable "@one" was not declared.

Unlocking WILL Modify Detached Ruleset Scope

Detached ruleset gains access by being unlocked (imported) inside a scope:

#space {
  .importer1() {
    @detached: { scope-detached: @variable; }; // define detached ruleset
  }
}

.importer2() {
  @variable: value; // unlocked detached ruleset CAN see this variable
  #space > .importer1(); // unlock/import detached ruleset
}

.usePlace {
  .importer2(); // unlock/import detached ruleset second time
   @detached();
}

compiles into:

.usePlace {
  scope-detached: value;
}
Clone this wiki locally