Can't define reusable comma separated selector lists in Less - 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');
}

Related

Why cannot we use ruleset within the same ruleset?

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 */

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 mixin calling another mixin won't generate nested rules

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. :)

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