Improve Less nesting for this rule? - less

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

Related

Less: Combining selector variables

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.

How to merge parent and child style properties using LESS

I have this less code, this is working just fine. I just want to save some spaces when the less cli compiles it.
.secondary-content {
background: #ffcc80;
color: white !important;
label, i {
background: #ffcc80;
color: white !important;
}
}
When I run less from the command prompt, the output looks like this.
.secondary-content {
background: #ffcc80;
color: white !important;
}
.secondary-content label,
.secondary-content i {
background: #ffcc80;
color: white !important;
}
as you can see they are separated on each block. I would like to have them on the same block. How could I easily merge the parent and child style properties? Like this.
.secondary-content,
.secondary-content label,
.secondary-content i {
background: #ffcc80;
color: white !important;
}
I'm still learning less, so any help would be much greatly appreciated.
Thanks in advance
You can make use of the parent selector (&) like in the below snippet. Usage of parent selector would mean that the same rules apply for .ghost .secondary-content selector as well as its child label and i tags.
.ghost .secondary-content {
&, label, i {
background: #ffcc80;
color: white !important;
}
}
Of course the solution provide by #Harry works. When you are learning Less you should keep in mind that Less helps you to write your CSS code DRY and more efficient. Less does not help you to solve issues, that you can not solve in common CSS, Less compiles into CSS and does not add any feature to the compiled CSS.
To reduce the size of your CSS for selectors which share some properties you should consider the extend feature of Less: http://lesscss.org/features/#extend-feature-reducing-css-size:
.selector1 {
color: red;
}
.selector2:extend(.selector1) {}
outputs:
.selector1,
.selector2 {
color: red;
}
To solve your issue you should reconsider the desired CSS code instead of the Less code. You can not use extend due to the nesting of the label, i, but why should you nest them to set the color and background-color?
The default value for the background-color is transparent so when you set the background-color for the parent you do not have set the background-color for the child elements (when using the same value).
Possible you override the default transparent with an other style rule with a higher specificity, see also http://www.smashingmagazine.com/2010/04/07/css-specificity-and-inheritance/
An example which gives your nested label the wrong background-color:
label {
background-color:green;
}
.secondary-content {
background-color:red;
color: white;
}
The same for the color property which always inherit from its parent, unless applied in an anchor.
You are also using !important, see: https://css-tricks.com/when-using-important-is-the-right-choice/

LESS darken() doesn't seem to work with variable resolved from interpolation

The following LESS code fails to compile, despite the fact that #color is correctly resolved to #3AD49E. (Thanks to Defining Variable Variables using LESS CSS .)
#success-color: #3AD49E;
#darken-percent: 5%;
.make-colored-div(#name) {
#color: ~'#{#{name}-color}';
&.#{name} {
background: #color;
border-color: darken(#color, #darken-percent);
}
}
button {
.make-colored-div(success);
}
Any ideas how to get darken to work?
This happens because you must convert #color in HSL space, before applying it darken function.
Key code should be:
#color1: hsl(hue(#color), saturation(#color), lightness(#color));
But it does not run as is. You need to pass through a #temp variable, in order to do a double (and intermediate) passage to obtain HSL conversion. Complete code follows:
#success-color: #3AD49E;
#darken-percent: 5%;
.make-colored-div(#name) {
#color: ~'#{#{name}-color}';
&.#{name} {
#temp:~'#{name}-color';
#final-color: hsl(hue(##temp), saturation(##temp), lightness(##temp));
background: #final-color;
border-color: darken(#final-color, #darken-percent);
}
}
button {
.make-colored-div(success);
}

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