CSS variable with calculation inside LESS - css

I have a CSS variable called --menuWidth. I'm trying to use this variable with an arithmetic operator but I'm having trouble.
I tried the following:
left: calc(var(--menuWidth) + 20px);
But the less processor output says "OperationError: Operation on an invalid type" (at column 2?)
I found this working codepen which actually seems to confirm the syntax above, so I'm assuming this is a LESS issue. If so, how can I fix it?
Here is my file structure:
vars.less
:root {
--menuWidth: 200px;
}
#media screen and (min-width: 1280px) and (max-width: 1919px){
:root {
--menuWidth: 250px;
}
}
#media screen and (min-width: 1920px){
:root {
--menuWidth: 300px;
}
}
layout.less
#headerContainer {
left: calc(var(--menuWidth) + 20px);
}
compiled.less
#import "vars.less";
#import "layout.less";

Updating LESS from 2.7.2 to 3.0.4 fixed the issue.

Related

How to interpolate a map value with string in Less

I can't seem to get my LESS mapping logic to work. I have followed the documentation on mapping.
#screen-size-min: {
mobile-small: 320px;
mobile-large: 480px;
tablet-small: 600px;
tablet-large: 768px;
desktop-small: 992px;
desktop-large: 1280px;
}
#min: min-width;
#desktop-small-min: ~"only screen and (#{min}: #screen-size-min[desktop-small])";
p {
color: blue;
#media #desktop-small-min {
color: red;
}
}
I'm expecting this code to compile to css, but the media query does not seem to compile.
p {
color: blue;
}
#media only screen and (min-width: 992px) {
p {
color: red;
}
}
I tried testing it on lesstester but it only supports LESS 2.7 at this time. Maps is a new feature in LESS 3.5
There are no compile errors. Where did I go wrong?
This is happening because key[value] is not actually a variable, the variable is the map #screen-size-min, therefore you can not take advantage of variable interpolation. One solution is to simply concat the KVP with the rest of the string:
#desktop-small-min: ~"only screen and (#{min}:" #screen-size-min[desktop-small] ~ ")";
This eliminates any dependency on creating another variable just to interpolate it
Additionally, per their documentation, maps were introduced:
Released v3.5.0
And the online tester you tested with only supports 2.7
Codepen usually supports their latest versions. Here's an anonymous pen demonstrating:
https://codepen.io/anon/pen/zeXmev
If you click on the little down arrow next to the 'CSS (Less)' header, you can select 'View Compiled CSS' and it will show you the LESS -> CSS output
This seems to be working:
#screen-size-min: {
mobile-small: 320px;
mobile-large: 480px;
tablet-small: 600px;
tablet-large: 768px;
desktop-small: 992px;
desktop-large: 1280px;
}
#min: min-width;
#mysize: #screen-size-min[desktop-small];
#desktop-small-min: ~"only screen and (#{min}: #{mysize})";
p {
color: blue;
#media #desktop-small-min {
color: red;
}
}
It produces the desired result. Try it here

how to override #media (max-width) using stylish

Intro
this is similar to this question but unfortunately the answer only applies to greasmonkey (which only works on firefox). Further, this was asked on the stylish forum but the answer was ambiguous.
Question
I want to remove the left column in the azure help page and
expand the main body to make it cover the widht of the screen.
The first part can easily be done by this
#sidebarContent {display:none}
How ever the second part must conver this
media (max-width: 1199.99999px)
to this
media (max-width: 100%)
But I have no idea how to do that using stylish.. ideas?
To override a media query you just need to load another media query - that also applies to your device - after it.
Well...you want a blunt media query that applies to everything. The best way is to use #media (min-width: 1px) since that includes all devices.
Now, put it all together - along with some other CSS cleanups like padding and margin removal and setting a new width for .mainContainer and you get this
#sidebar {
display: none;
}
#media (min-width: 1px) {
.mainContainer {
margin: 0 auto;
width: 100vw;
padding: 0;
}
body>.container {
padding: 0;
}
}
New code: (with different selector for width)
#sidebar {
display: none;
}
#media (min-width: 1px) {
.mainContainer { /*example styles*/
margin: 0 auto;
width: 100vw;
}
body>.container {
padding: 0;
}
body>.mainContainer>main {
max-width: 100vw!important;
}
}
You still have to adjust the padding to your preference as setting the padding to 0 breaks the design a little bit but this should be good starting point.
Before:
After:

less :extend() with media queries

I am trying to extend a simple class
.positionAbsolute {
position: absolute;
}
My issue here is that I can extend it like:
#something:extend(.positionAbsolute) {
something else
}
But from inside the media query itself, If i try from outside no rule is extended at all.
Is this the normal behaviour?, why is doing that?, In such case I will have to create like four equal classes to extend in each media query case, is there some workaround?
Thank you
I'm not sure I understand it right (it's always hard to guess w/o seeing an exact CSS output you need to achieve), but it looks like you need something like:
.positionAbsolute {
position: absolute;
}
#something:extend(.positionAbsolute) {
#media only screen and (min-width: 1024px) {
something: else;
}
}
The other way around is:
.positionAbsolute {
position: absolute;
}
#class-1,
#class-2 {
&:extend(.positionAbsolute);
}
#media only screen and (min-width: 1024px) {
#class-1 {
something: else;
}
#class-2 {
something-even: more else;
}
}
That way you will have to repeat selectors instead of media queries.
I was trying to do this, and was quite disappointed I couldn't put the extend within a media query. Fortunately I was able to use a mixin for what I wanted. This may work for some situations. I can't really tell what you're actually doing with your case.
.centerVertically()
{
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
margin-right: -0.25em;
border: 2px solid red;
.content
{
display: inline-block;
vertical-align: middle;
}
}
used in media query
#media screen and (min-width: #break_tablet)
{
/* doesn't work
&:before:extend(.centerVertically_wrapper){}
.content:extend(.centerVertically_child){}
*/
.centerVertically();
The best I came up with I turned into a GIST here. Basically, I created an externalized reference mixin for the query and imported all the possible contexts into it. LESS sees through the query when you are referencing a class to extend but it will export it.
Reference:
.narrow {
#media screen and (max-aspect-ratio: 1/1) {
#import "position";
#import "z";
}
}
Export:
#import (reference) "narrow";
.style-1:extend(.position .abs, .narrow .position .rel) {
}
.style-2:extend(.narrow .position .rel) {
}
.style-3:extend(.narrow .z .index-1) {
}

Can you create custom breakpoints with LESS mixins?

Most of the time, I use LESS variables with preset breakpoints for media queries like this:
#s-max : ~"screen and (max-width: 40em)";
#m-max : ~"screen and (max-width: 50em)";
#l-max : ~"screen and (max-width: 60em)";
USAGE
.some-class {
color: red;
#media #s-max {
color: blue;
}
}
But sometimes, I would like to be able to refer to an arbitrary breakpoint in my .less stylesheet without having to set a new preset value in my separate mixin file.
You can do this in SASS. The mixin looks like this:
#mixin bp-min($canvas) {
#media only screen and (min-width:$canvas) {#content;}
}
USAGE
#include bp-min(750px) {
//responsive styling for min-width of 750px
}
In LESS, I'm imagining the equivalent mixin would look something like this:
.bp-min(#min) {
#media only screen and (min-width:#min)...
}
The only problem is, the lack of the {#content} argument in LESS, which grabs the rest of the styling inputted by the developer. I love SASS, but I can't use it at work.
Does anyone know of a LESS-based solution to this problem?
It is now similar to SASS
As of 1.7.0 (2014-02-27) you can now use #rules in place of the sassy #content.
For example:
.breakpoint-small(#rules) {
#media screen and (min-width: 40em) { #rules(); }
}
ul {
width: 100%;
.breakpoint-small({
width: 50%;
});
}
outputs, as expected:
ul {
width: 100%;
#media screen and (min-width: 40em) {
width: 50%;
}
}
The differences being:
function takes #rules as an argument
additional parenthesis when invoking the function
'.' syntax as opposed to '#include'
This can be combined with an additional argument to provide syntax equivalent to a nice bit of sass:
.breakpoint(#size, #rules) {
#media screen and (min-width: #size) { #rules(); }
}
#large: 60em;
ul {
.breakpoint(#large, {
width: 50%;
});
}
edit: To be honest I prefer a way more simple approach in less:
#break-large: ~"screen and (min-width: 60em)";
ul {
#media #break-large {
width: 50%;
}
}
Source: I too use sass at home and less at work
Using Pattern Matching
I believe this achieves what you want:
LESS
/* generic caller */
.bp-min(#min) {
#media only screen and (min-width:#min) {
.bp-min(#min, set);
}
}
/* define them */
.bp-min(750px, set) {
test: (#min - 300px);
}
.bp-min(400px, set) {
test: (#min - 100px);
}
/* call them */
.bp-min(750px);
.bp-min(400px);
Output CSS
#media only screen and (min-width: 750px) {
test: 450px;
}
#media only screen and (min-width: 400px) {
test: 300px;
}
By defining a set pattern mixin for the various sizes, and then using that pattern within the generic .bp-min(#min) mixin, I believe we have the same abstraction in LESS that you have in SCSS, with slightly more code because I believe SCSS defines and calls in one #include statement, whereas here we need two.
(In addition to the prev. answer) Or something like this:
.bp-min(#canvas) {
#media only screen and
(min-width: #canvas) {.content}
}
// usage:
& { .bp-min(900px); .content() {
color: red;
}}
& { .bp-min(600px); .content() {
color: blue;
}}
// more usage examples:
.class-green {
.bp-min(450px); .content() {
color: green;
}}
& { .bp-min(300px); .content() {
.class-yellow {
color: yellow;
}
.class-aqua {
color: aqua;
}
}}
Replace .content with .- if you prefer shorter stuff.
In my case I needed my variables to reference other variables, so some of these solutions did not work. Here is what I went with.
#bp-xs: ~"screen and (max-width:"#screen-xs-max~")";
#bp-sm: ~"screen and (max-width:"#screen-sm-max~")";
#bp-md: ~"screen and (max-width:"#screen-md-max~")";
#bp-lg: ~"screen and (max-width:"#screen-lg-max~")";
and then use them like so
#media #bp-sm {
...
}

LESS: Subtract from variable

Using LESS, how can I subtract values with "px" at the end of the variable. I have the following variable:
#bpMobile: 600px
What I want to do is subtract this by 1px
#media only screen and (max-width: #bpMobile - 1px ) {
}
How can I achieve this with LESS?
Sorry for answering this late, but I had this very problem and it seems LESS is picky about the spacing. You also need () around your calculation.
This will not work:
#media screen and (max-width: (#widthSmall-2)) { }
However, this will (notice the space between the variable and the digit):
#media screen and (max-width: (#widthSmall - 2)) { }
You can always use the calc function for this.
Syntax:
calc(expression)
Eg:
abc {
width:calc(100%-20px)
}
Here are the list of browser that supports this function
EDIT 1:
you can use it in the following two ways:
LESS Input:
#bpMobile: 600px;
max-width: calc(~'#{bpMobile} - 1px');
CSS Output:
max-width: calc(600px - 1px);
2nd Way:
Less Input:
#bpMobile: 600px;
max-width: calc(#bpMobile - 1px);
CSS Output :
max-width: calc(599px);
With the first option,the calc arguments are escaped in order to prevent them from being evaluated on compilation.The values will remain totally static until it's evaluated by the client.
With the second option, the calc value will be evaluated on compilation. And it would be the same as
max-width: #bpMobile - 1px;
which will result in
max-width: 599px;
The fix from freejosh does not work for me on lesscss 1.7.0.
What does the trick is simply adding paranthesis around every variable or calculation inside a media-query:
#media only screen and (max-width: (#bpMobile - 1px) ) { ... }
or
#other: #bpMobile - 1px;
#media only screen and (max-width: (#other) ) { ... }
The problem isn't the math function, it's that you're trying to use it in a media query. The docs say that you need to make the whole query one variable:
#bpMobile: 600px;
#bpMobile1: #bpMobile - 1px;
#singleQuery: ~"only screen and (max-width: #{bpMobile1})";
#media #singleQuery {
}
————————————————————————————————
In my case .
.loop(#i) when (#i > 0) {
#index : #i + 1;
.abc_#{i}{
z-index : #index;
}
.loop(#i -1);
}
.loop(8);
will give output:
.abc_8{
z-index : 8 + 1;
}
————————————————————————————————
Another case :
.loop(#i) when (#i > 0) {
#index : pow(#i,1) + 1;
.abc_#{i}{
z-index : #index;
}
.loop(#i -1);
}
.loop(8);
will give output:
.abc_8{
z-index : 9;
}

Resources