I am trying to use the same .formater() ruleset within itself.
I am getting syntax error.
Why cannot we use a ruleset within itself?
.formater(#className;#rules){
.#{className}{
#rules();
}
}
.formater(mainContainer;{
display:block;
line-height:2em;
& a{
colour: blue;
&.formater(link;{
colour:red;
});
}
});
Any help would be appreciated.
The reason for the syntax error is because you are appending & to a mixin call which is not allowed. & can be attached only to selectors. If you remove the & from before the mixin call then your code would compile fine.
.formater(#className; #rules){
.#{className}{
#rules();
}
}
.formater(mainContainer; {
display:block;
line-height:2em;
& a{
colour: blue;
.formater(link; {
colour:red;
});
}
});
The above Less code when compiled would produce the following CSS:
.mainContainer {
display: block;
line-height: 2em;
}
.mainContainer a {
colour: blue;
}
.mainContainer a .link {
colour: red;
}
As you would have noticed, the final selector that is output above is .mainContainer a .link and not .mainContainer a.link. If you are trying to achieve the latter then one option would be for you to add the & within the .formater mixin's code itself.
.formater(#className; #rules) {
&.#{className} {
#rules();
}
}
I wouldn't really recommend using the below option but if you need the .formater() mixin to support both variations then you could use a variable along with guard conditions and use as appropriate.
.formater(#className; #rules; #parent-append: false) {
& when not (#parent-append = false) {
&.#{className} {
#rules();
}
}
& when (#parent-append = false) {
.#{className} {
#rules();
}
}
}
You can then call the mixin as
.formater(link; {colour:red;}; true); /* if & should be appended */
or
.formater(link; {colour:red;};); /* if & need not be appended */
Related
Consider the following Less code:
.a {
button,
input[type='button'],
input[type='submit'],
input[type='reset'] {
background: red;
}
}
.b {
button,
input[type='button'],
input[type='submit'],
input[type='reset'] {
background: blue;
}
}
What I'd like to be able to do is define the four possible types of buttons in a reusable way. I used to be able to do this easily in SASS, but have switched to Less in order to use Semantic UI. I can't find a syntax to do this in Less - is it possible?
Okay, I have a solution to this now, derived from this post:
#all-buttons: {
button,
input[type='button'],
input[type='reset'],
input[type='submit'] {
.get-props()
}
};
.set-props(#selectors; #rules; #extension: ~'') {
#selectors();
.get-props() {
&#{extension} { #rules(); }
}
}
.all-buttons(#rules; #extension: ~'') {
.set-props(#all-buttons; #rules; #extension);
}
.a {
.all-buttons({
background: red;
});
}
.b {
.all-buttons({
background: blue;
});
}
// Also enables an extension such as a pseudo selector for each button type
.c {
.all-buttons({
background: green;
}, ~':hover');
}
I'm looking for some kind of if-statement to control the background-color of different div elements.
I have tried the below, but it doesn't compile
#debug: true;
header {
background-color: (yellow) when (#debug = true);
#title {
background-color: (orange) when (#debug = true);
}
}
article {
background-color: (red) when (#debug = true);
}
There is a way to use guards for individual (or multiple) attributes.
#debug: true;
header {
/* guard for attribute */
& when (#debug = true) {
background-color: yellow;
}
/* guard for nested class */
#title when (#debug = true) {
background-color: orange;
}
}
/* guard for class */
article when (#debug = true) {
background-color: red;
}
/* and when debug is off: */
article when not (#debug = true) {
background-color: green;
}
...and with Less 1.7; compiles to:
header {
background-color: yellow;
}
header #title {
background-color: orange;
}
article {
background-color: red;
}
LESS has guard expressions for mixins, not individual attributes.
So you'd create a mixin like this:
.debug(#debug) when (#debug = true) {
header {
background-color: yellow;
#title {
background-color: orange;
}
}
article {
background-color: red;
}
}
And turn it on or off by calling .debug(true); or .debug(false) (or not calling it at all).
I stumbled over the same question and I've found a solution.
First make sure you upgrade to LESS 1.6 at least.
You can use npm for that case.
Now you can use the following mixin:
.if (#condition, #property, #value) when (#condition = true){
#{property}: #value;
}
Since LESS 1.6 you are able to pass PropertyNames to Mixins as well. So for example you could just use:
.myHeadline {
.if(#include-lineHeight, line-height, '35px');
}
If #include-lineheight resolves to true LESS will print the line-height: 35px and it will skip the mixin if #include-lineheight is not true.
I wrote a mixin for some syntactic sugar ;)
Maybe someone likes this way of writing if-then-else better than using guards
depends on Less 1.7.0
https://github.com/pixelass/more-or-less/blob/master/less/fn/_if.less
Usage:
.if(isnumber(2), {
.-then(){
log {
isnumber: true;
}
}
.-else(){
log {
isnumber: false;
}
}
});
.if(lightness(#fff) gt (20% * 2), {
.-then(){
log {
is-light: true;
}
}
});
using on example from above
.if(#debug, {
.-then(){
header {
background-color: yellow;
#title {
background-color: orange;
}
}
article {
background-color: red;
}
}
});
I need to generate some CSS with some comments right above the classes and in those comments, I need to evaluate some variables. I've been successful in doing this in Sass but Less doesn't seem to have the same functionality.
Here's what I need:
/**Header*/
.Header {
font-size: 1.5em;
}
Here's my attempt in Sass:
#function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);
#if $index {
#return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}
#return $string;
}
#mixin rte_property($name) {
/**#{$name}*/
.#{str-replace($name, ' ', '')} {
#content;
}
}
#include rte_property(Header) {
font-size: 1.5em;
}
Here's my attempt in Less:
.rte_element (#name, #rules) {
#className: e(replace(#name, " ", ""));
/**#{name}*/
.#{className} {
#rules();
}
}
.rte_element("Header 2", {
font-size: 1.5em;
});
Is it possible for Less to interpolate/evaluate variables in comments? If so, how?
There is no straight-forward (non-hacky) way to achieve this in Less. Less compiler does not evaluate any variable that is present within comments and so it would continue to be printed as #{var} instead of the evaluated value.
However, that doesn't mean there is no way at all. There is a way of achieving something close. That would be to put the entire comment text into a temporary variable and print it before the selector using selector interpolation technique.
The comment would not cause any impact to how the compiled CSS works (because the UA will just ignore the comments, refer the snippet at the end - it uses the compiled CSS produced by this code) but it doesn't have a line-break.
Note: I would definitely not recommend implementing such hacky solutions. I have given it here just to show that it can be done in a different way.
Less Code:
.rte_element(#name, #rules) {
#className: e(replace(#name, " ", ""));
#comment: ~"/* #{name} */"; /* store the comment structure as a variable */
#{comment} .#{className} { /* print it before the selector */
#rules();
}
}
.rte_element("Header 2", {
font-size: 1.5em;
color: red;
});
.rte_element("Header 3", {
font-size: 1.75em;
color: blue;
});
Demo with compiled CSS:
/* Header 2 */ .Header2 {
font-size: 1.5em;
color: red;
}
/* Header 3 */ .Header3 {
font-size: 1.75em;
color: blue;
}
<div class="Header2">Header 2 text</div>
<div class="Header3">Header 3 text</div>
Code for a line break after comment:
This is even more hacky but it seems to work in the latest compiler.
.rte_element(#name, #rules) {
#className: e(replace(#name, " ", ""));
#comment: ~"/* #{name} */
" ; /* note how there is a line break inside the quotes */
#{comment} .#{className} {
#rules();
}
}
For some reason, the output of nth child is rendered with un unexpected space. Can anyone help?
Renders:
// Part of render
body.domain-bsci-fta-local #block-domain-switcher ul li:nth-child( 3) {
background-color: #e14313;
}
From code:
// Variables
#a-primary: #018f9e;
#b-primary: #2b6a7c;
#c-primary: #e14313;
#d-primary: #009966;
#domain-a: 'a-local';
#domain-b: 'b-fta-local';
#domain-c: 'c-fta-local';
#domain-d: 'd-fta-local';
#domains: #domain-a #a-primary 1, #domain-b #b-primary 2, #domain-c #c-primary 3, #domain-d #d-primary 4;
// Call
body {
.generate-menus();
}
// Functions
.generate-menus() {
.for(#domains);
.-each(#domain) {
#dn: e(extract(#domain, 1));
#dc: extract(#domain, 2);
#dr: extract(#domain,3);
.generate-menu(#dn, #dc, #dr);
}
}
.generate-menu(#domainname, #domaincolor, #domaincount) {
&.domain-#{domainname} {
#block-domain-switcher {
ul {
li {
&:nth-child(#{domaincount}) {
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
}
}
.navigation .submenu {
background-color: #domaincolor;
}
}
}
// ............................................................
// .for
.for(#i, #n) {
.-each(#i)
}
.for(#n) when (isnumber(#n)) {
.for(1, #n)
}
.for(#i, #n) when not (#i = #n) {
.for((#i + (#n - #i) / abs(#n - #i)), #n);
}
// ............................................................
// .for-each
.for(#array) when (default()) {
.for-impl_(length(#array))
}
.for-impl_(#i) when (#i > 1) {
.for-impl_((#i - 1))
}
.for-impl_(#i) when (#i > 0) {
.-each(extract(#array, #i))
}
Note: As mentioned by seven-phases-max in his comments to the question, this was a bug which has already been fixed in v2.x. Leaving this answer (with the work-around solution) as-is to help future readers who can't upgrade their compiler for whatever reason.
The problem happens only for selectors which use selector interpolation and are nested within one or more parent selectors. It can be solved by using a temporary variable which contains the pseudo-selector like below: (it uses escaped string feature)
Option 1:
ul {
li {
#selector: ~":nth-child(#{domaincount})"; /* the selector is formed here */
&#{selector} { /* and used here */
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
}
Option 2:
li {
#count: ~"(#{domaincount})";
&:nth-child#{count} { /* and used here */
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
Sample Compiled Output:
body.domain-a-local #block-domain-switcher ul li:nth-child(1) {
background-color: #018f9e;
}
Related Links:
concatenate values in less (css) without a space
Redudant space in interpolated selectors like nth(...)
As mentioned above and in the linked issue thread, the issue happens only when the selector is formed using selector interpolation and is nested under one or more parents.
This works
// Variables
#list: a 1;
#num: extract(#list, 2);
// Usage
body div:nth-child(#{num}) {
color: #444;
}
But this doesnt
// Variables
#list: a 1;
#num: extract(#list, 2);
// Usage
body {
div:nth-child(#{num}) {
color: #444;
}
}
I'm looking for some kind of if-statement to control the background-color of different div elements.
I have tried the below, but it doesn't compile
#debug: true;
header {
background-color: (yellow) when (#debug = true);
#title {
background-color: (orange) when (#debug = true);
}
}
article {
background-color: (red) when (#debug = true);
}
There is a way to use guards for individual (or multiple) attributes.
#debug: true;
header {
/* guard for attribute */
& when (#debug = true) {
background-color: yellow;
}
/* guard for nested class */
#title when (#debug = true) {
background-color: orange;
}
}
/* guard for class */
article when (#debug = true) {
background-color: red;
}
/* and when debug is off: */
article when not (#debug = true) {
background-color: green;
}
...and with Less 1.7; compiles to:
header {
background-color: yellow;
}
header #title {
background-color: orange;
}
article {
background-color: red;
}
LESS has guard expressions for mixins, not individual attributes.
So you'd create a mixin like this:
.debug(#debug) when (#debug = true) {
header {
background-color: yellow;
#title {
background-color: orange;
}
}
article {
background-color: red;
}
}
And turn it on or off by calling .debug(true); or .debug(false) (or not calling it at all).
I stumbled over the same question and I've found a solution.
First make sure you upgrade to LESS 1.6 at least.
You can use npm for that case.
Now you can use the following mixin:
.if (#condition, #property, #value) when (#condition = true){
#{property}: #value;
}
Since LESS 1.6 you are able to pass PropertyNames to Mixins as well. So for example you could just use:
.myHeadline {
.if(#include-lineHeight, line-height, '35px');
}
If #include-lineheight resolves to true LESS will print the line-height: 35px and it will skip the mixin if #include-lineheight is not true.
I wrote a mixin for some syntactic sugar ;)
Maybe someone likes this way of writing if-then-else better than using guards
depends on Less 1.7.0
https://github.com/pixelass/more-or-less/blob/master/less/fn/_if.less
Usage:
.if(isnumber(2), {
.-then(){
log {
isnumber: true;
}
}
.-else(){
log {
isnumber: false;
}
}
});
.if(lightness(#fff) gt (20% * 2), {
.-then(){
log {
is-light: true;
}
}
});
using on example from above
.if(#debug, {
.-then(){
header {
background-color: yellow;
#title {
background-color: orange;
}
}
article {
background-color: red;
}
}
});