Less: Combining selector variables - less

Given i want to simplify the following LESS code:
#title1: ~"h1.someclass > a";
#title2: ~"h1.otherclass > a";
#{title1},#{title2} {
&:after {
display: none;
}
}
which evaluates to:
h1.someclass > a:after,
h1.otherclass > a:after {
display: none;
}
I tried to merge the classes by using
#titles: ~"#{title1},#{title2}"; // Combine selectors for easier code
#{titles} {
&:after {
display: none;
}
}
This however will yield different CSS.
h1.someclass > a,h1.otherclass > a:after {
display: none;
}
Is this due to the Lazy Evaluation of the variables? If so, why does it yield the CSS this way? And is there a diffrent way of combining selector variables and later using &:after?

(almost copy-pasting from the more wide https://stackoverflow.com/a/23954580/271274)
There're two problems with your attempt:
By definition a content of escaped strings is not a subject for any kind of evaluation at all, so commas (as well as any other special ops) have no meaning there.
Variable interpolation in selectors assumes a single interpolated variable contains only a single selector element. So, strictly speaking, even ~"h1.someclass > a" is already nothing but a hack expected to have side-effects and unspecified/undefined behaviour for anything but extremely trivial cases.
So in your code above the value of #titles works just as a simple/single selector element (the same as body for example).
I.e. in summary and in general, "string-based selector manipulation" (like ~"#{title1}, #{title2}") should be avoided where possible simply because in Less selectors are not strings and strings are not selectors (nor they automatically converted to each other except in, yet again, certain extremely trivial cases).
So far the only non-hackish method to define a reusable list of selectors in Less is a mixin (mixins can be considered as "variables" too even if they have another syntax) that puts an arbitrary set of rules into a ruleset having the said list as its selector. E.g. for your example above it would be something like:
#title1: ~"h1.someclass > a";
#title2: ~"h1.otherclass > a";
.titles(#rules) {
#{title1}, #{title2} {#rules();}
}
// usage:
.titles({
&:after {
display: none;
}
});
Demo.

Related

Improve Less nesting for this rule?

I need the following CSS output. The ie* classes must be there for specificity and the body class also needs to be there without them as they won't always be added.
body.my-class,
html.ie7 body.my-class,
html.ie8 body.my-class,
html.ie9 body.my-class {
background: red;
}
I can get the same thing with this in my Less. However its not a good idea as I have to write the style of background: red twice. So if it was updated it would need to be updated in 2 places.
body.my-class {
background: red;
html.ie7 &,
html.ie8 &,
html.ie9 {
background: red;
}
}
Can I write my Less in a different way so that I'm not repeating the style, but so that the compiled CSS is exactly the same?
Simply add the & (parent selector) as one of the comma separated selector list within the top level nesting. Less compiler would automatically replace it with the full parent selector as it always does.
body.my-class {
&, /* this will replaced with body.my-class as is always the case with parent selectors */
html.ie7 &,
html.ie8 &,
html.ie9 &{
background: red;
}
}
The above code when compiled would result in exactly the same CSS output as required.
body.my-class,
html.ie7 body.my-class,
html.ie8 body.my-class,
html.ie9 body.my-class {
background: red;
}

LESSCSS: Assign a value to a property taken from another one's

In some cases is common to use same values in different properties, for example (is just an example to show purpose) the following nested rule:
.button-link
{
height:40px;
a
{
line-height:40px;
}
}
The idea is that to vertically center button text line-height and height should be equal.
Is there a way in LESS to "assign a value taken from a diffent property"?
I know that I should use a LESS #variable but in this case is not the same thing and need extra code. Instead should very interesting and useful if I should edit only button's height and then LESS will replaced the same value to line-height
UPDATE:
Another example could be the following:
.button-link
{
color:white;
background:black;
&:hover
{
color:black;
background:white;
}
}
In which "hover" status should invert color and background-color comparing to default state.
This is possible starting with v3 of LESS! Here is the documentation on it.
The example use case they provide ends up with the background-color getting the same value as the color property when compiled:
.widget {
color: #efefef;
background-color: $color;
}
You can´t :(. What i usually do is:
#buttom-height = 100px;
#a-link-height: #buttom-height;
and use that variables in your less declarations. Its a dummy example, i know, but imagine calculated data values from other variables or complex dependencies, proportional paddings/margins... that´s the way i learnt from Bootstrap LESS code.

Include a less file and pass parameters

I have a common.less file, that implements the basic CSS for different skins:
#textColor: black;
#iconSize: 16px;
.container: {
color: #textColor;
background-color: white;
}
.icon: {
width: #iconSize;
height: #iconSize;
}
// note that #iconSize is also used in this file inside mixins
The plan is to use it like so skin_1.less:
#iconSize: 32px; // override the icon size
// but leave #textColor as default
#import "common.less";
.container: {
color: red;
}
// I would now have big icons and red text
So I would like to have a common style, that I can reuse and selectively override variables.
This does not ssem to work however. I think it's because imports are always moved to the top, so variables cannot be pre-defined.
(I also read that variables are rather constants, so that may be another problem.)
Anyway: is there a better pattern to solve my use case?
You don't need to split the files up, just override the variable after the import. Variables are always resolved as the last definition, even if it is after where it is used.

less.css: escape interpolated string for multiple selectors

[ ANSWERED: the carto.css compiler does not support this feature of less.css]
I'm making a stylesheet for TileMill that will be processed with Carto (an extension of less.css). In other words, I'm making a stylesheet with LESS that uses some custom syntax.
I have a lists of selectors assigned to variables as strings, like so:
#water: "[natural='water'], [waterway='river'], [waterway='stream'], [waterway='waterfall'], [waterway='canal'], [landuse='reservoir'], [landuse='basin'], [amenity='pool'], [amenity='swimming_pool'], [amenity='fountain']";
#dirt: "[natural='beach'], [natural='sand'], [natural='scree'], [landuse='bedrock outcrop'], [landuse='quarry'], [natural='land'], [amenity='track']";
I want to be able to use these as selectors in my style sheet, more or less like this (which doesn't work):
(~'#{water}') {
polygon-fill: #bbb;
}
What I want to end up in my stylesheet is this:
[natural='water'], [waterway='river'], [waterway='stream'], [waterway='waterfall'], [waterway='canal'], [landuse='reservoir'], [landuse='basin'], [amenity='pool'], [amenity='swimming_pool'], [amenity='fountain']{
polygon-fill: #bbb;
}
And I'm hoping there's a way to join selector groups, like this:
#water, #dirt {
polygon-fill: #bbb;
}
Thanks for any advice.
the carto.css compiler does not support this feature of less.css

LESS CSS guards without mixin

I'm just getting into LESS and am trying to figure out how I can make conditional css statements without mixins. I find that I have a lot of single line css statements that only occur once but are dependent on some variable or condition and that using mixins is a bit pointless since it will never be reused.
Example.
#add-margin: true;
body {
margin-top: 20px; //I only want this if add-margin is true
}
So ideally I want this:
body when (#add-margin) {
margin-top: 20px;
}
But that doesn't work. Using a mixin works but seems silly to make one just for a one liner. Is there some alternative way I can do this?
Thanks
yes you can, it's similar to your code.
#add-margin: true;
.margin() when (#add-margin) {
margin-top: 20px;
}
body { .margin(); }
UPDATE: Using the latest versions of LESS (1.5+), usage of "guarded mixins" are not required to achieve this, and you could use "css gaurds" instead, therefore the OP's code will work out of the box
CSS Guards feature was introduced in Less v1.5.0 and hence now we can use guards the same way as mentioned in the question.
#add-margin: true;
body when (#add-margin){
margin-top: 20px;
}
If in case, you need to assign multiple such properties to different selectors based on the same variable, it can be implemented like below using the & selector.
& when (#add-margin){
body{
margin-top: 20px;
}
h1{
margin-top: 10px;
}
}
Note: As mentioned by memeLab in comments, any value for the #add-margin variable other than true are considered as falsy. This is because true is a keyword whereas 'true' is a String value. For example, the below would output nothing.
#add-margin: 'true';
body when (#add-margin){
margin-top: 20px;
}
However, the below would work because it does a String comparison.
#add-margin: 'true';
body when (#add-margin = 'true'){
margin-top: 20px;
}
If you are using Less compiler lower than v1.5.0 then the answer posted by Unicornist is the best bet.
no, it is not possible in that form.
you could use a variable equal to 0 or 1 and multiply by 20 and then always output a rule, or use JavaScript (i would advise you to avoid this) to convert true to 0 or 20 and always output a rule, but if you want the property added conditionally, you need guards.