Using LESS variables in media queries - css

When I enter the following less code: (paste it into http://less2css.org)
#item-width: 120px;
#num-cols: 3;
#margins: 2 * 20px;
#layout-max-width: #num-cols * #item-width + #margins;
#media (min-width: #layout-max-width) {
.list {
width: #layout-max-width;
}
}
... the resulting CSS is:
#media (min-width: 3 * 120px + 40px) {
.list {
width: 400px;
}
}
Notice that the same variable #layout-max-width - in the media query it produces an expression (which isn't what I want) and when used as the value for the width property it produces 400px (which is also what I want for the media query.)
Is there formal syntax in LESS which enables me to do this?
If not - is there a workaround?

Due to Math Settings
LESS by default expects math operations to be in parenthesis (strict-math=on). So your variable needs those around the values to calculate correctly, like so:
#layout-max-width: (#num-cols * #item-width + #margins);
Then your original code will output as you expect.

Related

Can I use mixins to generate new mixins in LESS?

I'm trying to use LESS to dynamically generate a set of mixins that would help me write cleaner media query code. So far in my limited knowledge of the language I've put together code that looks like this:
#sizes: xxs, xs, sm, md, lg;
.mediaQueries(#iterator:1) when(#iterator <= length(#sizes)) {
//Extract name
#sizeName: extract(#sizes, #iterator);
//Attempt to build min-width query
.MQ-min-#{sizeName} (#content) {
#media (min-width: #screen-#{sizeName}) {
#content();
}
}
//Attempt to build max-width query
.MQ-max-#{sizeName} (#content) {
#media (max-width: #screen-#{sizeName}) {
#content();
}
}
.mediaQueries((#iterator + 1));
}
.mediaQueries();
The goal is to have a set of mixins that would allow me to easily and cleanly define some CSS properties for a specific breakpoint, like so:
.generic-class {
background: black;
//Sizes #screen-sm and up
.MQ-min-sm({
background: transparent;
})
}
It doesn't work. Something to note, I'm trying to interpolate the size name into a variable name that would then output me a the px value of that variable into the #media query. Is something like this even possible?
Otherwise my compiler currently breaks on the start of the nested mixin (.MQ-min-#{sizeName} (#content) {) with the error:
Potentially unhandled rejection [2] Missing closing ')' in file ../mixins.less line no. 43
Is something like what I'm trying to achieve possible?
I think the simplest way for you to achieve this is by using a single parametric mixin like given below. This avoids the need for all those iterations, dynamic mixin creations etc.
#sizes: xxs, xs, sm, md, lg;
#screen-xxs: 100px;
#screen-sm: 200px;
.MQ(#content, #sizeName, #max-min) { /* get ruleset, size name and min/max as input */
#selector: ~"(#{max-min}-width: #{screen-#{sizeName}})"; /* form the media selector */
#media #selector { /* use it */
#content();
}
}
.generic-class {
background: black;
.MQ({
background: transparent;
}, /* ruleset */
sm, /* screen size */
max /* min/max */
);
}
If the mixins are for your own usage then this is all that you need. If it is for distribution as library then you may want to put some guards on #sizeName and #max-min variables to restrict invalid values.
Note: Less compiler always had a problem with the interpolation here - #media (min-width: #screen-#{sizeName}) also (I am not sure if it has been addressed) and that's why I created a temp variable.

Subtract from a value obtained through variable interpolation

I'm having trouble calculating a value when using variable interpolation.
Here's an example on the Less preview site: http://goo.gl/GVHXUs
Below is my code:
#breakpoint-sm: 600px;
#breakpoint-md: 800px;
.Mq(#breakpoint; #rules; #maxMin: min) {
& when (#maxMin = min) {
#query: ~"(min-width: #{breakpoint-#{breakpoint}})";
#media screen and #query {#rules();};
}
& when not (#maxMin = min) {
#break: ~"#{breakpoint-#{breakpoint}}" - 1;
#query: ~"(max-width: #{break})";
#media screen and #query {#rules();};
}
}
.test {
.Mq(sm; {
width: 100%;
height: 200px
}; max);
.Mq(md; {
width: 100%;
height: 200px
});
}
Result:
#media screen and (max-width: 600px - 1) {
.test {
width: 100%;
height: 200px;
}
}
#media screen and (min-width: 800px) {
.test {
width: 100%;
height: 200px;
}
}
So what I'm trying to achieve is that when something other than min is passed to the #maxMin it should subtract 1 from the breakpoint. I guess I'll be the laughing stock of Stackoverflow now, but hell, I can't figure it out.
The output of ~"#{breakpoint-#{breakpoint}}" is always a string and so the compiler just appends the number to the string instead of performing the math operation.
One way would be to use a temporary variable like shown below (have added only the part that needs modification) and then perform the arithmetic operation.
.Mq(#breakpoint; #rules; #maxMin: min) {
/* the rest of the mixin */
& when not (#maxMin = min) {
#temp: ~"breakpoint-#{breakpoint}";
#break: (##temp - 1); /* the braces are mandatory, without which it again appends */
#query: ~"(max-width: #{break})";
#media screen and #query {#rules();};
}
}
/* the selector blocks and mixin calls */
Below are few things that I found while working on the solution which have left me stumped. I'm trying to find the reason and will update the answer when I do find it out.
The braces play an important role in the #break variable. Without it, the output in media query is still a concatenation. However, if the same variable is used outside the media query (in a normal property-value pair like prop: #break, it prints the subtracted value).
The below code returns concatenated value (800px - 1)
#break: ~"#{breakpoint-#{breakpoint}}" - 1;
prop: #break;
whereas the below gives a "Operation on invalid type" compiler error.
#break: ~"#{breakpoint-#{breakpoint}}";
prop: #break - 1;
while I can see the reason behind them (first one results in string concatenation whereas second says subtraction can't happen on a string value), I am a bit stumped as to why the behavior is not consistent between the two.
(You are definitely not a laughing stock. Though I knew the reason for the problem, it took time for me to find a solution.)

Possible Bug in Nested Mixins in Media Queries using LESS

EDIT: RESOLVED
I am working on a way to easily write LESS code that takes parameters but still works with media queries. This is turning out to be rather convoluted, but I have gotten it working – on all sizes except one. The medium and large sizes work, but small is for some reason not printing the parameter, leaving me with css like font-size: ;.
Here I define my media sizes:
#m-small = ~"screen and (max-width: 799px)";
#m-medium = ~"screen and (min-width: 800px) and (max-width: 1299px)";
#m-large = ~"screen and (min-width: 1300px)";
Then, the main function I call, where #attr is the CSS property (e.g. font-size) and #parameter is the variable (e.g. fs-medium). To use this, I can write .media('font-size', 'fs-medium'), which is significantly less verbose than defining every media query.
Edit: There was a bug here, hence the problem; I have fixed it.
.media(#attr, #parameter) {
#media #m-small {
.small(#attr, #parameter);
}
#media #m-medium {
.medium(#attr, #parameter);
}
#media #m-large {
.large(#attr, #parameter);
}
}
These functions store the default values for parameters at various sizes, allowing me to consolidate where I define my variables, grouped by media query:
.small(#attr, #parameter) {
#fs-small : 1.4rem;
#fs-medium : 2.0rem;
#fs-large : 3.4rem;
#logo-width : 10rem;
.guards();
}
.medium(#attr, #parameter) {
#fs-small : 1.4rem;
#fs-medium : 2.4rem;
#fs-large : 3.8rem;
#logo-width : 12rem;
.guards();
}
.large(#attr, #parameter) {
#fs-small : 1.4rem;
#fs-medium : 1.8rem;
#fs-large : 5rem;
#logo-width : auto;
.guards();
}
In the above code, I call .guards() to render the content. This checks through my list of guards for one with a matching attribute, because LESS does not allow variables to be used in CSS property names. In these guards, I dynamically call the parameter, so that if I passed fs-medium, it will render #fs-medium's value.
.guards() when (#attr = 'font-size') {
font-size: ##parameter;
}
.guards() when (#attr = 'width') {
width: ##parameter;
}
Now, as I said, this works fine for the medium and large sizes, so I feel like there is either a typo in my code (I've checked) or a bug in LESS. One piece of code that uses this is as follows:
nav {
.media('font-size', 'fs-medium');
}
Which renders the following content:
#media screen and (max-width: 799px){
nav{ font-size:; }
}
#media screen and (min-width: 800px) and (max-width: 1299px){
nav{ font-size:2.4rem; }
}
#media screen and (min-width: 1300px){
nav{ font-size:1.8rem; }
}
Why is the small font-size missing?
I have discovered that I do indeed have a typo in my question, where I typed 'paremeter' under the .small mixin. I have edited it in the original post, but I am leaving it here for others trying to use media queries in LESS in a generalized way.
Verdict: typo.

LESS-variable with value == null, undefinded?

Just to get this clear, im talking about "LESS" as described on http://lesscss.org/
i read the documentation and searched a little around but i didn't find a possibilty to call an mixin with some "null" or undefined parameters.
For example this mixin from retina.js's less file:
.at2x(#path, #w: auto, #h: auto) has 2 optional parameters.
Now i only want to call it with a defined path and height. How do i do this?
things that don't work:
.at2x(EXAMPLEURL, '', 500px)
.at2x(EXAMPLEURL, null, 500px)
.at2x(EXAMPLEURL, , 500px)
.at2x(EXAMPLEURL,, 500px)
.at2x(EXAMPLEURL, undefined, 500px)
call that works of course:
.at2x(EXAMPLEURL, auto, 500px)
A Null Value
Is an escaped empty string, like so:
.at2x(EXAMPLEURL, ~'', 500px);
This would output "nothing" for the #w variable except a white space character. This will not necessarily eliminate the property it is used for, but it will make that property of no effect in the css. So you might get this if it is used for setting width: #w:
width: ;
Which would be ignored in css. Or if it is part of background-size: #w #h, you would get something like this:
background-size: 500px;
Of course, this is not truly NULL in the normal programming sense of the term, but I think it is what you are seeking for.
You can use "Named Parameters":
.at2x(EXAMPLEURL, #h: 500px);
I've the same problem with Less and i find a fix for this.
FIX :
#MyEmptyVar: e("");
Problem example :
Before :
/* Input (Style.less)*/
#IconPrefix: "";
/*
* With my less version this
* #IconPrefix: ; cause a compilation error
*/
.#{IconPrefix}glass:before {
content: "\f000";
}
/* Output (Style.css)*/
.""glass:before {
content: "\f000";
}
After:
/* Input (Style.less)*/
#IconPrefix: e("");
.#{IconPrefix}glass:before {
content: "\f000";
}
/* Output (Style.css)*/
.glass:before {
content: "\f000";
}

Append unit type to the result of a calculation in Sass

I've been refactoring my CSS to a SASS style sheet recently. I'm using the Mindscape Web Workbench extension for VS2012, which re-generates the CSS each time you save your SCSS. I started with code similar to this:
/* Starting point: */
h1 { font-size: 1.5em; /* 24px ÷ 16px */ }
Then I tried to refactor it first to this:
/* Recfator: */
h1 { font-size: (24px / 16px)em; }
But this unfortunately produces:
/* Result: */
h1 { font-size: 1.5 em; } /* doesn't work, gives "1.5 em" */
Notice the extra space, which I don't want there. I've tried several alternatives, here are a few:
h1 { font-size: (24/16)em; } /* doesn't work, gives "1.5 em" */
h2 { font-size: 24 / 16em; } /* doesn't work, gives "24/16em" */
h3 { font-size: (24px / 16px) * 1em; } /* works but "* 1 em" feels unnecessary */
h4 { font-size: (24em) / 16; } /* works, but without "px" it's not
really conveying what I mean to say */
I've also tried these variants with variables (because I want those anyways), but that didn't change the situation much. To keep the examples in this question sleek I've left out variables. However, I'd happily accept a solution that relies on using variables (in a clean way).
I've gone through the relevant SASS documenation on '/', and appreciate that this is a tough one for SASS because the '/' character already has a meaning in basic CSS. Either way, I was hoping for a clean solution. Am I missing something here?
PS. This blogpost does offer one solution, using a user defined function. That seems a bit heavy-weight though, so I'm interested if there's "cleaner" solutions in line with my attempts above. If someone can explain the "function approach" is the better (or even only) solution then I'll accept that as an answer too.
PS. This related question seems to be about the same thing, though that one specically wants to do further calculations. The accepted answer there is my third workaround (multiplying by 1em), but I'd love to know if there's a different (cleaner) way if I'm willing to forego the ability to do further calculations. Perhaps the method mentioned in said question ("interpolation") is useful for me?
Bottom line: how can you cleanly append the unit type (e.g. em) to the result of a calculation in SASS?
The only way to add a unit to a number is via arithmetic.
To perform operations like concatenation (eg. 1 + px) or interpolation (eg. #{1}px) will only create a string that looks like a number. Even if you're absolutely 100% certain that you're never going to use your value in another arithmetic operation, you should not do this.
More important than not being able to perform arithmetic operations, you won't be able to use them with other functions that expects a number:
$foo: 1; // a number
$foo-percent: $foo + '%'; // a string
.bar {
color: darken(blue, $foo-percent); //Error: "1%" is not a number!
}
$amount: "1%" is not a number for `darken'
There is nothing to be gained by casting your numbers to strings. Always use arithmetic (multiplication by 1, or addition by 0) to add a unit:
$foo: 1; // a number
$foo-percent: $foo * 1%; // still a number! //or: $foo + 0%
.bar {
color: darken(blue, $foo-percent); //works!
}
Output:
.bar {
color: #0000fa;
}
Here's a mixin I wrote as part of my Flexbox mixin library that will choke if you pass in a string (for those not familiar with Flexbox, the original specification only allows integers for the box-flex property. flex: auto or flex: 30em cannot be made compatible with the comparable box-flex property, so the mixin doesn't bother trying)
#mixin flex($value: 0 1 auto, $wrap: $flex-wrap-required, $legacy: $flex-legacy-enabled) {
#if $legacy and unitless(nth($value, 1)) {
#include legacy-flex(nth($value, 1));
}
#include experimental(flex, $value, flex-support-common()...);
}
#mixin legacy-flex($value: 0) {
#include experimental(box-flex, $value, $box-support...);
}
You can try either of these:
font-size: $size * 1px;
or
font-size: $size + unquote("px");
Where $size is the result of your calculation.
Choose the method you prefer
$font: 24px;
$base: 16px;
No variables
.item1 { font-size: #{(24px / 16px)}em; }
Using only variables
.item2 { font-size: #{$font / $base}em; }
One variable
.item3 { font-size: #{$font / 16px}em; }
.item4 { font-size: #{24px / $base}em; }
Output for all of them
.item1 { font-size: 1.5em; }
.item2 { font-size: 1.5em; }
.item3 { font-size: 1.5em; }
.item4 { font-size: 1.5em; }
The method used is called interpolation #{}
I assume the reason you're asking this is because, in the surrounding context, 1 em = 16 px, and you want to use this relationship to convert the target font size from pixels to ems.
If so, the right thing to do is to multiply the font size with the scaling factor 1em / 16px, like this:
$h1-font-size: 24px;
$body-font-size: 16px;
$body-em-scale: 1em / $body-font-size;
h1 {
font-size: $h1-font-size * $body-em-scale; // -> 1.5em
}
or just:
h1 {
font-size: $h1-font-size * (1em / $body-font-size); // -> 1.5em
}
This is exactly how it works in physics, too: if have, say, 50 moles of water, and you want to know how much that weighs in grams, you multiply the amount of water you have (= 50 mol) with the molar mass of water (&approx; 18 g/mol) to find out that your sample of water weighs 50 mol × 18 g/mol &approx; 900 grams. Converting pixels to ems in SASS works just the same way: first find out how many ems there are per pixel, in the context you intend to use the rule in, and then multiply the size in px with that ratio, expressed in units of em/px.
Ps. Of course, if the units you were converting between had a fixed ratio that SASS already knew about, then you could simply let it do the conversion automatically for you, using the "zero plus trick". For example, if you wanted to convert the font size from px to cm, you could simply do:
h1 {
font-size: 0cm + $h1-font-size; // -> 0.635cm
}
This works because SASS allows you to add together two values given in compatible units (like px and cm), and will express the result in the same units as the first value in the sum. The reason this trick doesn't work for px -> em is that here the conversion factor is not fixed, but depends on the surrounding context, and so SASS doesn't know how to do the conversion automatically.
The best way to add a unit, is to use ... * 1em or ... + 0em where em is the unit you want to get:
font-size: (24px/16px) + 0em; // ... + 0px for px
//or
font-size: (24px/16px) * 1em;
//both produce: font-size: 1.5em;
This way, it will remain as a number so you can use it in math operations or other functions.
But if you want the result as a string or don't care the numeric value of result for some reason, you can also simply get it this way:
font-size: (24px/16px) + em; //font-size: 1.5em; //em does not include quotations
no need to use unquote or #{} here (as written in other answers)!

Resources