Less mixin calling another mixin won't generate nested rules - less

I have a Less project set up with three files relevant to my question.
A config file which just holds variables and their values.
A controller file which my Less watcher is told to compile.
A mixin file, titled _buttons.less, which I import as a reference in the
controller file.
Inside the controller file, I have a rule:
.bananas {
.nesting-mixin(#color-list);
}
Inside the variable file, I have a list:
#color-list : red, blue, green, black;
Inside the mixin file, I have two mixins:
.do-the-mixin(#list) {
color: extract(#list, 1);
&.blue {
color: extract(#list, 2);
}
&.green {
color: extract(#list, 3);
}
}
.nesting-mixin(#list) {
&.colored {
.do-the-mixin(#list);
}
}
When I call the .nesting-mixin method from the controller file, the output is:
.bananas.colored {
color: #ff0000;
}
But when I move the nesting mixin into the controller file, I get:
.bananas.colored {
color: #ff0000;
}
.bananas.colored {
color: #ff0000;
}
.bananas.colored.blue {
color: #0000ff;
}
.bananas.colored.green {
color: #008000;
}
Is there some aspect of Less mixin importing or nesting that I don't understand? The second output is what I want, but I don't understand why it needs to be in the same file from which it's called.
Thanks for reading. :)

Related

Can't define reusable comma separated selector lists in Less

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');
}

Apply class to all instances of parent selector in LESS

I've got this LESS stylesheet. The idea it to alias a lot of icon classes to my local classnames in our "namespace".
// base-icons.less
.base-icon {
display: block;
font: "myFont.otf"
}
.base-icon-foo { content: 'foo' }
.base-icon-bar { content: 'bar' }
.base-icon-fiz { content: 'fiz' }
// my-icons.less
.my-icon {
&-foo { .base-icon; .base-icon-foo; }
&-bar { .base-icon; .base-icon-bar; }
&-fiz { .base-icon; .base-icon-fiz; }
}
Is there a way to prevent having to add the .base-icon class to each single line in the my-icons.less file? I want to apply the css to all classes that start with .my-icon but it would be cleaner to not have to type the .base-icon class each time.
Learn mixins and extend. E.g. (assuming you can't modify base-icons.less):
// base-icons.less
.base-icon {
display: block;
font: "myFont.otf"
}
// my-icons.less
.i(foo);
.i(bar);
.i(baz);
.i(#name) {
.my-icon-#{name} {
&:extend(.base-icon);
content: '#{name}';
}
}
Also see stuff like In LESS, can I loop over class/mixin "patterns"?

File-level less variables

I want to define block name at once at head of its file
#block: widget-a;
And then use it in selectors
.#{block} {
// my styles
}
But I can't do it when I have more than one block. Variable #block takes the last value and it breaks my selectors
// file a.less
#block: widget-a;
.#{block} {
// styles for widget-a
}
// file b.less
#block: widget-b;
.#{block} {
// styles for widget-b
}
Both rulesets there got the block .widget-b in spite of that is different files. Is there a way to persist name for each block?
Here is a gist: https://gist.github.com/just-boris/a86f3646f48683a9bf17 which can be built and reproduce it. I use less 2.3.0
I'm not sure why you have to use the same variable name twice?
You code does not compile:
you code:
// file a.less
#block: ~".widget-a";
#{block}: {
// styles for widget-a
}
should be:
// file a.less
#block: ~".widget-a";
#{block} {
// styles for widget-a
}
can be rewritten to, see variable-interpolation:
#block: widget-a;
.#{block} {
// styles for widget-a
color: red;
}
As already mentioned by #harry, when defining the same variable twice in the same scope, the last declaration wins. You could wrap your code in a mixin or use namespaces:
// file a.less
.a() {
#block: widget-a;
.#{block} {
// styles for widget-a
color: red;
}
}
.a();
// file b.less
.b() {
#block: widget-b;
.#{block} {
// styles for widget-b
color:blue;
}
}
.b();
outputs:
.widget-a {
color: red;
}
.widget-b {
color: blue;
}

LESS: Guarded Mixins like MediaQuery

I would like to use LESS Guarded Mixins in a way similar to MediaQuery concept, so declaring a condition that, if verified, contains a set of css rules and declaration.
For the moment I wrote this example code:
.color-theme(#color-type) when (#color-type='cold')
{
color:gren;
}
.color-theme(#color-type) when (#color-type='hot')
{
color:red;
}
text-container
{
.color-theme('hot');
width:960px;
}
My intention is to write a set of classes that must be used only if a particular condition is satisfied, in a way very similar to MediaQueries logic.
This lines of code runs... but in this wa I should repeat the parameter value 'hot' for each new css class.
I would like to have something like
when (#color-type='hot')
{
body { ... }
.myclass { ... }
...
}
How could I obtain this?
This is not really possible exactly like that (as you can not pass a block into a mixin you are calling ... what you are trying to do is possible in Sass with the #content directive). What you could do in Less instead, is define a mixin that outputs a particular block (cold or hot) depending on the #color-type variable passed.
a) Mixins with specific arguments:
First you can make a general output mixin, that does not show anything, no
matter what the #color-type is (so you don't get an error if an undefined block is called):
.show(#color-type) { }
Then you define the blocks (similarly to how you would do with media
queries, except here you will need an extra mixin call):
.show('cold') {
body {
color: blu;
}
.myclass {
background: url(cold.png);
}
}
.show('hot') {
body {
color: red;
}
.myclass {
background: url(hot.png);
}
}
Now you just need to call the mixin. And depending on what the variable you pass, the right block will be
shown (or if no block with that variable has been defined, there
will be no output). For example now you can call show(), passing a
variable that you defined somewhere earlier:
#color-type: 'hot';
.show(#color-type);
or directly
.show('hot');
and the CSS output will be:
body {
color: red;
}
.myclass {
background: url(hot.png);
}
b) Guards:
Instead of defining the mixins with particlular arguments (e.g. .show('hot'){ ... }), you can use guards, like so: .show(#color-type) when (#color-type = 'hot') { ... }), or alternatively like so: .show() when (#color-type = 'hot') { ... } if you define the variable somewhere earlier and the just have to call the mixin .show() to return the respective block:
.show() when (#color-type = 'cold') {
body {
color: blue;
}
.myclass {
background: url(cold.png);
}
}
.show() when (#color-type = 'hot') {
body {
color: red;
}
.myclass {
background: url(hot.png);
}
}
// setting the variable
#color-type: 'hot';
// calling the mixin
.show();
Maybe also of interest - some discussion connected to this topic:
Issue #965: Mixins should accept LESS blocks

How to pass a property name as an argument to a mixin in less

I want to make a function/mixin that will make a color darker if it is already dark but lighter when it is light (normalize/extremeize?)
Is it possible to do this by passing a property name (color, background-color, border-right-color, etc)?
.normalize(#color, #amount, #prop: "color") when (lightness(#color) >= 50%)
{
#prop:lighten(#color, #amount);
}
.normalize(#color, #amount, #prop: "color") when (lightness(#color) < 50%)
{
#prop:darken(#color, #amount);
}
This is currently a feature request on less.js github. So look out for it in less.js 1.4.. until then you can hack it like so...
.mixin(#prop, #value) {
Ignore: ~"a;#{prop}:#{value}";
}
Not very nice and you get an extra property but its the only way at the moment.
Guarded Mixins should be what you are looking for, however you can not use variables to define properties, only their values. So you can do it like this:
.normalize(#color, #amount) when (lightness(#color) >= 50%)
{
color:lighten(#color, #amount);
}
.normalize(#color, #amount) when (lightness(#color) < 50%)
{
color:darken(#color, #amount);
}
So this:
.class1 {
.normalize(#ddd, 10%);
}
Will output this:
.class1 {
color: #f7f7f7;
}
But you can not actually pass a property name as a variable. This is a limitation of LESS unfortunately, and while I've seen ways around it for things like margin direction, there is not a way to just pass any ol' property using a variable.
In the corresponding issue on Less' GitHub there is a workaround suggested by cloudhaed:
.blah () { color: black } // All blahs
.blah(right) { padding-right: 20px } // Right blahs
.blah(left) { padding-left: 20px } // Left blahs
#side: left;
.class { .blah(#side) }
Output
.class { color: black; padding-left: 20px;}
Maybe this will do?
This feature was added since v1.6.0:
#property: color;
.widget {
#{property}: #0ee;
background-#{property}: #999;
}
Compiles to:
.widget {
color: #0ee;
background-color: #999;
}
See http://lesscss.org/features/#variables-feature-properties