LESS CSS guards without mixin - less

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.

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.

Is it possible to manipulate css variables using LESS?

With preprocessor variables it's easy to set up one variable and manipulate it so that I can use it to set multiple properties. (demo)
While experimenting with native css variables, I noticed that I could combine them with preprocessor variables, so in the following example: (use firefox)
h1 {
--length: 40px;
#length: var(--length);
line-height: #length;
border: 5px solid tomato;
}
line-height was correctly rendered at 40px
But, when I tried to manipulate the preprocessor variable - like this:
h1 {
--length: 40px;
#length: var(--length);
#length2: #length*2;
line-height: #length;
padding: #length2;
border: 5px solid tomato;
}
... the code failed.
Is this possible somehow?
As mentioned in my comment, my understanding of CSS variables is that the variable is resolved into its actual value by the UA. This happens after the Less compiler compiles the file and thus it wouldn't be aware of what is the actual value contained by the CSS variable.
To the compiler, the value of #length is only var(--length). Since this is not a number, an error is thrown during compilation indicating that the math operation is being done on an invalid type.
OperationError: Operation on an invalid type on line 4, column 3:
One way to fix this would be to make the Less compiler output the variable name as it is and have the multiplier appended to it (like string concatenation). This would then leave the control to the UA.
But since all CSS math operations have to be given within calc() function, the entire thing has to be wrapped within it. So, the below code would work fine.
h1 {
--length: 40px;
#length: var(--length);
#length2: ~"calc(#{length} * 2)";
line-height: #length;
padding: #length2;
border: 5px solid tomato;
}
Or, even the below would be enough if --strict-math is enabled during compilation:
h1 {
--length: 40px;
#length: var(--length);
#length2: calc(#length * 2);
line-height: #length;
padding: #length2;
border: 5px solid tomato;
}
Above code when compiled produces an output similar to the one in Example 11 of the specs and so it should be a reasonably good way of doing this :)
... Note, though, that calc() can be used to validly achieve the same thing, like so:
.foo {
--gap: 20;
margin-top: calc(var(--gap) * 1px);
}
var() functions are substituted at computed-value time...

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

Is there a way to add a mixin into a variable in LESS

Can I add a mixin to a variable in LESS?
Something like this
#input-border-radius: .rounded();
or
#h1: .font-size(46) // This pulls from the rem calculator mixin.
Looked at the LESS Docs but can't see a way to do it.
There is a way.
You can define properties of a (possibly immaginary) class and recall the properties of that class in the style of a different class. For example:
.fontstyling {
font-weight: bold;
color: black;
}
h1 {
font-size: 46px;
.fontstyling;
}
h2 {
font-size: 38px;
.fontstyling;
}
(thats not the best way to format the headings - but for other exemples it is really useful!)

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.