Set min and max breakpoints with single variable with LESS? - less

Im trying set a media query breakpoint with LESS so I can have something like this:
#media (max-width: 1000px) {
// something
}
#media (min-width: 1001px) {
// something
}
I want a single place to control the breakpoint. The following isnt working. Is this just a syntax issue or am I going about this the wrong way?
#breakpoint: 1000;
#breakpoint-max: #breakpoint;
#breakpoint-min: #breakpoint + 1;
#media (max-width: #breakpoint-max) {
// something
}
#media (min-width: #breakpoint-min) {
// something
}

I not sure why the media query to try to compile should not work, except for the missing unit as mentioned by #SLaks already.
In you example situation you can apply 'mobile first':
#breakpoint: 1000px;
#breakpoint-max: #breakpoint;
#breakpoint-min: (#breakpoint + 1);
//something
p {
color:red;
#media (min-width: #breakpoint-min) {
// something
color:green;
}
}
The above compiles into the code shown below by default:
p {
color: red;
}
#media (min-width: 1001px) {
p {
color: green;
}
}
The color is always red, unless the screen width is wider that 1000 px.
Also notice the brackets in #breakpoint-min: (#breakpoint + 1);. The requirement of brackets in this case depend on the sm option:
-sm=on|off Turns on or off strict math, where in strict mode, math.
--strict-math=on|off Requires brackets. This option may default to on and then
be removed in the future.

You never specified a unit.
You need to set your variable to 1000px.

Related

Inconsistent Output between LESS Compilers

I have written some LESS code that resizes text based on browser width. Multiple different elements and their parameters can be sent to the reusable mixin.
All online LESS compilers output the desired result. But I am getting different output from Squarespace's LESS compiler.
Squarespace's compiler appears to "hang on" to the old variable values when called a second time.
Can you see how Squarespace's LESS compiler is reaching its output and, if so, share changes that can be made to make the output consistent with all other compilers?
Output from online compilers: (desired)
#media screen and (max-width: 1280px) {
.homesCommunitiesLayout #pageHeroWrapper {
font-size: 120px;
}
}
#media screen and (max-width: 640px) {
.homesCommunitiesLayout #pageHeroWrapper {
font-size: 60px;
}
}
#media screen and (max-width: 1280px) {
#divisionTitle {
font-size: 85px;
}
}
#media screen and (max-width: 853.3333333333334px) {
#divisionTitle {
font-size: 56.666666666666664px;
}
}
#media screen and (max-width: 426.6666666666667px) {
#divisionTitle {
font-size: 28.333333333333332px;
}
}
Output from Squarespace compiler: (undesirable)
#media screen and (max-width: 1280px) {
.homesCommunitiesLayout #pageHeroWrapper {
font-size: 120px;
}
}
#media screen and (max-width: 640px) {
.homesCommunitiesLayout #pageHeroWrapper {
font-size: 60px;
}
}
#media screen and (max-width: 1920px) { //<---Gone wrong! Continuing to use element1!
.homesCommunitiesLayout #pageHeroWrapper {
font-size: 180px;
}
}
#media screen and (max-width: 1280px) {
.homesCommunitiesLayout #pageHeroWrapper {
font-size: 120px;
}
}
#media screen and (max-width: 640px) {
.homesCommunitiesLayout #pageHeroWrapper {
font-size: 60px;
}
}
LESS Source Code and Link to code on Less2Css.org:
#maxSiteWidth: 1280px;
#fullWidth: #maxSiteWidth;
//Element 1 Parameters & Function Call
#fitTextElement1: ~".homesCommunitiesLayout #pageHeroWrapper";
#fitTextElement1Max: 120px;
#fitTextElement1Min: 50px;
#fitTextElement1BreakPoints: 2;
.fitText(#fitTextElement1; #fitTextElement1Max; #fitTextElement1Min; #fitTextElement1BreakPoints);
//Element 2 Parameters & Function Call
#fitTextElement2: ~"#divisionTitle";
#fitTextElement2Max: 85px;
#fitTextElement2Min: 26px;
#fitTextElement2BreakPoints: 3;
.fitText(#fitTextElement2; #fitTextElement2Max; #fitTextElement2Min; #fitTextElement2BreakPoints);
//Primary Looping Mixin
.fitText(#targetElement; #targetElementMaxSize; #targetElementMinSize; #targetElementBreakPoints) {
.mixin-loop (#loopIteration) when (#loopIteration > 0) {
#{targetElement} {
.setBreakPointWidth(#loopIteration; #targetElementBreakPoints);
#media screen and (max-width: #breakPointWidth) {
.setFontSize(#loopIteration; #targetElementMaxSize; #targetElementMinSize; #targetElementBreakPoints);
font-size: #targetElementFontSize;
}
}
.mixin-loop(#loopIteration - 1);
}
.mixin-loop(0){}
.mixin-loop(#targetElementBreakPoints);
}
//Function to set font size
.setFontSize(#loopNumber; #maxSize; #minSize; #breakPoints) {
#targetElementFontSize: (#maxSize/#breakPoints)*#loopNumber;
.resetFontSize(#targetElementFontSize; #minSize);
}
//Function to reset font size if below minimum desired
.resetFontSize(#calculatedSize; #minSize) when (#calculatedSize < #minSize) {
#targetElementFontSize: #minSize;
}
//Function to set break point
.setBreakPointWidth(#loopNumber; #breakPoints) {
#breakPointWidth: (#fullWidth/#breakPoints)*#loopNumber;
}
Note that Squarespace uses LESS 1.3.3 so you'll need to manually switch Less2Css to that version (though it doesn't seem to change anything if you don't).
Having put much more time into this, I've discovered there are a lot of issues with the code as I posted it. In older versions of LESS, variables would "leak" up to parent scopes, which is the only reason any of this code was working at all.
In the end, the solution was to abandon the old 1.3.3 version and write for the latest version, rewriting the entire code NOT to depend on such "leaks". Then to precompile using an online compiler until Squarespace updates their compiler someday. For now, I just have to precompile it before saving it to the file that is on the Squarespace Server.
Without getting in the specifics of exactly what went wrong, I'll just mention that the top reason I've had issues with LESS and Squarespace's compiler is because it's not the same as LESS. Squarespace previously used a Node implementation of Less.js, and then rebuilt the compiler in Java to gain performance over Node/Less.js.
So the key takeaway is that Squarespace's LESS compiler is based off of Less.js and not identical to the same LESS compilers a developer would use. You'll definitely find odd scenarios where things won't compile the same.
I would submit any bugs you find to the official repo here: https://github.com/Squarespace/less-compiler. Their engineers are pretty responsive!

In LESS, how to use a variable inside another?

Variable #screen-xs-max is a Bootstrap 3 variable. In my mixin, I'd like to use #class variable to get the Bootstrap variable, dynamically.
Here is a not working solution:
.make-btn-block (#class) {
#media (max-width: #screen-#{class}-min) {
// Code
}
}
.make-btn-block(xs);
Another not working solution:
~"#{#screen-#{class}-min}"
See referencing variables by name. E.g.:
.make-btn-block(#device) {
#max: "screen-#{device}-max";
#media (max-width: ##max) {
color: red;
}
}

Is ~'' really the best way to pass a null parameter to a LESS mixin?

I am creating a series of mixins for LESS css where I use a mixin like this with media queries:
.animal(#color: ~'', #imgWidth: ~'')
{
& when not (#color = ~'')
{
color: #color;
}
& when not (#imageMargin = ~'')
{
img
{
width: #imgWidth;
}
}
}
As you can see the parameters default to ~'' and then I only output the property if a real value is passed in. This allows me to 'inherit' from the previous media query when nothing is passed in :
#media screen and (min-width: 20em)
{
.cat(#color: red, #imgWidth: 3em);
.tiger(#color: blue, #imgWidth: 5em);
}
#media screen and (min-width: 20em)
{
.cat(#imgWidth: 5em);
.tiger(#imgWidth: 7em);
}
The color doesn't need to be re-set for the second media query, only the #imgWidth parameter. So in the output CSS it isn't included.
This works fine, but its very cumbersome. It this really the only way to do optional parameters that should be ignored if not provided. Why does LESS even output parameters when they're empty? Is there a setting to disable this?

Is there any (good) way to extend a class within a mixin, and then use that mixin within a media query, using Less?

I've been working on building out some Less files to help speed up my CSS workflow, and also to help produce more efficient, cleaner CSS.
The way I see it:
Mixins are a great way to help speed up the workflow, but they have the drawback of potentially making the outputted CSS longer than necessary.
Extending classes is the ideal solution for ensuring the amount of duplicate style declarations is minimized, helping clean that up...
So, to help balance things out I wrote out a set of standard, commonly used styles, using dummy classes (they are stored in a file which is imported by reference, so the styles are only output if they get extended).
I set all of my Mixins to extend these classes wherever possible, which worked great for the most part.
However, I realized my pitfall once I got to my media queries... I can't extend those classes within the media query, which would be fine normally, I would just remember not to do so.. But since the Mixins also now use my extends, I can now no longer use them inside media queries either.
I'm not willing to avoid using the Mixins inside of the media queries because of this, but I'd really love to be able to find a way to keep extending classes within them to keep my output as clean as possible.
The only idea I've thought of so far is to add an extra parameter to every Mixin to specify wether it should extend or not, but that's less than ideal.
My hope is that someone can come up with a much more clever solution, that would allow me to maintain the benefit of Mixins which extend base style classes, but also maintain easy usability, without over complicating things. Might be a tall order, but here's hoping.
In case my explanation was hard to follow, this is what I would have hoped to be able to do, but is not currently possible:
Ideal Input
// extensions.less
.block {
display: block;
}
// mixins.less
#import (reference) "extensions";
.mixin {
&:extend(.block);
margin: auto;
}
// styles.less
#import "mixins";
.element1 {
.mixin();
}
.element2 {
.mixin();
}
#media only screen and (max-width: 768px) {
.element3 {
.mixin();
}
.element4 {
.mixin();
}
}
Ideal Output
// styles.css
.element1, .element2 {
display: block;
}
.element1 {
margin: auto;
}
.element2 {
margin: auto;
}
#media only screen and (max-width: 768px) {
.element3, .element4 {
display: block;
}
.element3 {
margin: auto;
}
.element4 {
margin: auto;
}
}
In short, yes, currently it is somewhat possible but requires some additional wrapping for a top level classes:
// extensions.less
.block {
display: block;
}
// mixins.less
#import (reference) "extensions";
.mixin() {
&:extend(.block);
margin: auto;
}
// styles.less
#media all { // !
#import "mixins";
.element1 {
.mixin();
}
.element2 {
.mixin();
}
}
#media only screen and (max-width: 768px) {
#import (multiple) "mixins";
.element3 {
.mixin();
}
.element4 {
.mixin();
}
}
.element1 and .element2 (and any other class to extend .block) have to be put into #media all because currently:
Top level extend matches everything including selectors inside nested media
So if .element1 and .element2 stay in the global scope they leak into every other #media .block declaration.
(Hmm, actually for me this "top level extend matches everything" thing looks questionable and contradicts another "extend inside a media declaration should match only selectors inside the same media declaration" rule (obviously because global scope = #media all thus they should work identically)).

LESS variables in #media queries

How to use LESS variables in #media queries in Adobe CQ5?
myFile.less:
#myVar = 10px;
span {
width: #myVar;
}
works fine.
But:
myFile.less:
#myVar = 400px;
#media all and (min-width: #myVar) {
span{
color:red;
}
}
Results with:
clientLib.css:
#media all and (min-width: #myVar) {
span{
color:red;
}
}
This code is perfectly valid LESS since version 1.2.0 (released two years ago):
#myVar: 400px;
#media all and (min-width: #myVar) {
span {
color:red;
}
}
So assuming #myVar = 400px; is just your typo here, it looks like you did not update your CQ5 for a while. Consider upgrading it to 5.6.1 which includes LESS v1.3.3 (pretty ancient too but at least it supports variables in media query).
I also suspect that it is possible to manually update LESS script included with the CQ simply by replacing the "less.js" file (found somewhere among other CQ files) by its newer version.
From: http://flippinawesome.org/2013/05/20/less-tips-and-tricks/
/* note '~' in the beginning - media query must be escaped */
#singleQuery: ~"tablet and (min-width: 500px)";
#media screen, #singleQuery {
set {
padding: 3 3 3 3;
}
}
--
#myVar: ~"400px";
#media all and (min-width: #myVar) {
span{
color:red;
}
}
compiles to:
#media all and (min-width: 400px) {
span {
color: red;
}
}