Conditionally setting one variable's value based on another - less

I've got a Less variable called #side. What I want is to set the variable #sideOpposite depending on the value of the #side variable. It can take only two values: "left" or "right".
In other words I need a Less equivalent of the JS code:
var side = "left",
sideOpposite = (side === "left")? "right" : "left";
I've tried to accomplish this using when function, but from what I understand it doesn't work that way and is only applicable to CSS properties, not variables:
when (#side = right){
#sideOpposite: left;
}
when (#side = left){
#sideOpposite: right;
}

See Mixin Guards and Ruleset Guards (aka "CSS Guards") for when usage examples. Since you need to define a variable you'll have to use mixin-based condition (rulesets do not expose their variables to an outer scope). E.g.:
.-();
.-() when (#side = right) {
#sideOpposite: left;
}
.-() when (#side = left) {
#sideOpposite: right;
}
(Use any suitable mixin name instead of .-, e.g. .define-opposite-side).
And with Argument Pattern Matching it can be further simplified just to:
.-(#side);
.-(left) {#sideOpposite: right}
.-(right) {#sideOpposite: left}

You can take help of the mixin guard feature to allow conditional mixins
.mixin (#side; #sideOpposite) when (#side = right) { #sideOpposite: left }
.mixin (#side; #sideOpposite) when (#side = left) { #sideOpposite: right }

I don't know if I've understand what you want to do, but from what I get about your question, you can do this with a mixin. See example
//This is the mixin with default value
.side(#leftORoposite: left) { float: #leftORoposite ;}
Invoke this mixin when you need it left with .side(left) or when you need right with .side(right).

Related

Less: how to grab an entire ruleset from within a map

I have a nested ruleset (map) like the one below.
#typography: {
#h1: {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
line-height: 9.6rem;
text-transform:none;
}
}
I know how to retrieve and output a single key such as [font], but is there any way of returning and outputting the whole of inner ruleset?
.myclass {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
line-height: 9.6rem;
text-transform:none;
}
"Can't work this way currently (v3.9)".
I'm afraid it's not going to work the way (specifically the map itself) it is.
Intuitively it would be something like:
#usage {
#typography[#h1]();
}
But currently this feature (cascading () and [] operators) is not implemented.
A first-guess workaround like "assign a ruleset of interest to a temporary variable and then 'call' it" also fails:
#usage {
#temp: #typography[#h1];
#temp(); // error: not callable value
}
(This one actually to be counted as a bug - I created a dedicated ticket).
This all brings us to the next section:
"Consider using mixin-based maps".
Notice that while "variable-based maps" (aka DRs) seem to be a more wide-spread pattern by now, there are five different methods to define a map in Less (and infinite number of these methods permutations to define an N-dimensional (aka "nested") map).
Each method has its pros and cons, and so far it's not clear which one to be chosen as the "go-to" one (in a long run there's tendency to unify them as tidy as possible but so far it's far from that).
Now look at the structure you're trying to represent w/o sticking to "variable -> #variable" stereotype. Does not it look like a regular CSS ruleset:
.typography {
.h1 {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
line-height: 9.6rem;
text-transform: none;
}
}
?
And this way you've already got a "mixin-based map" you can use pretty much the same way you'd use a "variable-based map". (Actually the current documentation for "Maps" also suggest both methods w/o enforcing either one as "the primary").
The only modification you'll need for this "CSS" structure is to make its inner or outer (or both) rulesets to be a parameteric mixin (by adding ()) so that the rules won't appear in the compiled CSS by default.
E.g. like this:
.typography {
.h1() {
...
Or like this:
.typography() {
.h1 {
...
(Also if you prefer for these identifiers you can use # instead of .).
Now getting back to your use-case (The Solution):
.typography {
.h1() {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
line-height: 9.6rem;
text-transform: none;
}
}
#usage-1 {
// "expand" the set of rules:
.typography.h1(); // OK
}
#usage-2 {
// use individual value from the map:
r: .typography.h1[font]; // OK
}
#usage-3 {
// iterate through:
each(.typography.h1(), <...>); // OK
}
// etc.
Not a surprise counting that expanding a set of rules is what the mixins were invented for in the first place.
The only fundamental difference (beside current limitations/issues on how they can be used) between "variable-based" and "mixin-based" maps to keep in mind is that "variables (and properties) override" and "rulesets (and thus mixins) cascade". This may affect some particular details when you'll need your CSS data to be customized/modified by "external code" (e.g. as in "theming/subtheming" etc.) - but that's another big story so I won't get into it here, although see the next section for some tips.
"Mixins and variables interop".
And one more important thing to understand about mixins (in context of the use-case).
If we'll think of variables as an abstract programming thing, i.e. "an identifier (symbolic name) associated with a value" we quickly see that a mixin is just that: a variable.
A "mixin" (its name) is really nothing but an identifier to refer to a value, i.e. -> variable.
It's just the identifier characters (# or . in front) plus a limitation on what kind of values it can hold is what makes it to be referred to by a different title, i.e. "mixin" instead of a "variable" (as in "Less #variable").
In other words, when it comes to "I have some data and I need something (i.e. "a variable") to hold/represent it", it's important to not automatically fall into the "a variable (in a generic sense) -> #variable" trap.
So getting back to the Q, another trick to have in mind is to know that mixin and variable values (specifically if it's a "ruleset" value) can be (almost) freely assigned/reassigned to each other. I.e. basically, you can create a variable to refer to a mixin-based map and create a mixin to refer to a variable-based map.
This may be valuable to overcome current issues/limitations (mostly in usage) of both methods (or if you just prefer more of #, . or # "code-look" where maps are used).
Here're a few tips:
// ................
// "Universal" map:
.typography {
.h1() {
font: roboto;
font-weight: 300;
font-size: 9.6rem;
line-height: 9.6rem;
text-transform: none;
}
#h1: {.typography.h1}; // assign mixin to variable
.h2() {#h1()} // assign variable to mixin
.h3() {.typography.h1} // assign mixin to mixin
#h2: #h1; // assign variable to variable
}
#typography: {.typography}; // assign mixin to variable
.graphytypo {.typography} // assign mixin to mixin
// etc.
// ................
// Usage:
#usage-1 {
// use individual values from the map (all roboto):
1: .typography.h1[font];
2: .typography[#h1][font];
3: .typography.h2[font];
4: .typography.h3[font];
5: .typography[#h2][font];
6: #typography[#h1][font]; // <- like your original map
7: .graphytypo.h3[font];
// etc.
}
#usage-2 {
// expand a set of .h1 rules (all the same):
.typography.h1();
.typography.h2();
.graphytypo.h3();
// etc.
}

Is there a way to read a var passed and replace its value

Is there a way to read a variable passed and replace its value.
less
.mar(#B) when (default()) {
margin:~'#{B}px';
}
.mar(#A,#B){
margin-#{A}:~'#{B}px';
}
usage
.foo1{
.mar(3);
}
.foo2{
.mar(t,3);
}
the t value passed would be replaced by the word top and so worth for other letters, b for bottom, etc...
I could pass the entire word eg:top but i'm trying to shorten the amount of text I type after the initial mixin.
You can make four overloads of the mixin:
.mar(t, #B){
margin-top: ~'#{B}px';
}
.mar(b, #B){
margin-bottom: ~'#{B}px';
}
However, abbreviating code this way is not very readable. Don't do this.

Overriding mixins in LESS

when defining a mixin multiple times in LESS, and later calling that mixin as follows
.background-color() {
background: red;
}
.background-color() {
background: yellow;
}
body {
.background-color;
}
the result will be a combined output from all the defined mixins
body {
background: red; // << output from mixin #1
background: yellow; // << output from mixin #2
}
while when you apply the same scenario in both Sass & Stylus ( using their own syntax of course ), when you call a mixin that is defined multiple times across your stylesheets, Only the last defined one will be executed ( it will override all previously defined mixins ) as follows.
result Sass and Stylus
body {
background: yellow; // << output from mixin #2
}
how can I override a mixin in LESS so that the output will be from the last defined mixin ?
You can not override them, alternatively use a variable to define the 'background-color'. For Less variables the last declared win.
Also read Pattern-matching
In Less all matching mixins are compiled in the source. You can use namespace to prevent name collisions, for instance:
#ns1 {
.background-color() {
background: red;
}
}
#ns2 {
.background-color() {
background: yellow;
}
}
than you can use:
body {
#ns2 > .background-color;
}
Double properties are also not removed to make some browser hacks possible, example:
#myElement {
width: 300px;
width: 500px\9;
}
To find a solution for your use case you should reformulate your question and explain why you have these same named mixins in the first place.

Use a mixin named parameter in LESS as a pseudo selector

My idea here is to create a LESS mixin that could add a pseudo element :before or :after to any element in my page.
I have tried to use the "Named parameter" of LESS mixins to create a generic mixin in which the user could specify whether the pseudo element should be a :before or an :after.
When I compile, I get an error: "Unrecognized input".
Here is my code:
.setInlineIcon(#iconx: 0; #icony: 0; #pos: before; #margin: 0; #margindir: right) {
&:#{pos} {
.setStructure(#h: 16px; #w: 16px; #d: inline-block; #va: middle);
.getIcon(#iconx; #icony);
content:"";
margin-#{margindir}: #margin;
}
}
Where .setStrucutreand .getIcon are two already existing and functioning mixins. Can you spot what's wrong?
Is it because I cannot use a variable as a selector in this way?
: alone is not recognized as valid selector identifier. The workaround is to concatenate it to the identifier before selector interpolation:
.mixin(#pos) {
#name: ~':#{pos}';
&#{name} {
/* ... */
}
}
usage {
.mixin(before);
}

LESS CSS Variable based on variable

I'm using LESS to build a site, and want the layout to switch based on direction, either 'ltr' or 'rtl' (direction:rtl)
I set #direction at the top. Then there are certain elements that I want floated left for ltr, and right for rtl. I also want to position absolute, and apply padding-left/right based on the #direction.
Instead of writing out separate mixins for float, pos and padding, I was trying to do something like this:
.mixin (#direction) when (#direction = ltr) {
#lr:left;
}
.mixin (#direction) when (#direction = rtl) {
#lr:right;
}
Then call it like this:
ol li {
float:#lr;
}
and/or
ol li span.date {
position:absolute;
#lr:0;
}
That's the idea, but any help would be appreciated. I've looked at guards, and parametric mixins but just can't seem to nail it.
I have an idea for your case which solve RTL problem. In each class, we define 2 mixin within it for direction stylesheet properties. The prototype like that:
// #direction variable should be put by somehow
#direction: rlt;
.foo {
// common properties;
.dir(#direction);
.dir(rtl) {
// RTL properties;
}
.dir(ltr) {
// LTR properties;
}
}
For example:
// #direction variable should be put by somehow
#direction: rlt;
.foo {
color: #000000;
.dir(#direction);
.dir(rtl) {
float: left;
padding-left: 5px;
background: "rtl.png";
}
.dir(ltr) {
float: right;
padding-right: 5px;
background: "ltr.png";
}
}
OK. After some playing and a bit of thinking this is what I've come up with. If I can't use variables as properties then I'll use #direction, and #directionOpp (opposite of rtl, ltr) to use as a layout helper.
I have 2 variables.
#direction: ltr; // Change to 'rtl' for arabic, hebrew, etc.
#directionOpp: rtl; // Make this opposite of #direction, for easier mixins
Here's my mixin for horizontal positioning.
#dir {
.dir(ltr,#dist:0) {left: #dist;}
.dir(rtl,#dist:0) {right: #dist;}
.float(ltr){float:left; }
.float(rtl){float:right;}
.margin(ltr, #dist:#a){margin-left:#dist;}
.margin(rtl, #dist:#a){margin-right:#dist;}
.padding(ltr, #dist:#a){padding-left:#dist;}
.padding(rtl, #dist:#a){padding-right:#dist;}
}
and here's how I call it.
ol li {
#dir.float(#direction);
#dir.padding(#direction);
}
If I ever need to reverse anything, then I can replace #direction with #directionOpp.
I can also specifiy how much #dist I need as it's parametric mixin, and since they're all separate I can have any combination of margin, float, padding etc I need without multiple mixins with hard coded properties.
Good solution?
Dave
Your first issue is that variables are actually constants in LESS. So once you set it, it can not be overwritten. When you set #lr to "left", then it will always have "left" as its value, even if you try to reset the variable. Which is the issue with your first idea.
As far as your second idea, LESS does not support using variables as properties, only values. However you can hack around it like so:
.mixin(#prop, #value) {
Ignore: ~"a;#{prop}:#{value}";
}
This isn't very clean, but it does the trick if you absolutely need the functionality. Word is it is in the works for 1.4.
Also, you are calling the mixin incorrectly. Try something like this:
.mixin (#direction) when (#direction = ltr) {
float: left;
}
.mixin (#direction) when (#direction = rtl) {
float: right;
}
Then call it like this:
ol li {
.mixin(ltr);
}
Which should spit this out:
ol li {
float: left;
}
Try this Mixins in your LESS files
#rtl: rtl;
#ltr: ltr;
#direction: #rtl;
then Use them like this
html {
direction: #direction;
}
body{
direction: #direction;
}
for Left and Right commands you should use these mixins
.DockItem(#location, #value) when (#location = "left") and (#direction = #ltr){
left: #value;
}
.DockItem(#location, #value) when (#location = "right") and (#direction = #ltr){
right: #value;
}
.DockItem(#location, #value) when (#location = "left") and (#direction = #rtl){
right: #value;
}
.DockItem(#location, #value) when (#location = "right") and (#direction = #rtl){
left: #value;
}
then in your less file you should call this mixins like this
.TestClass{
.DockItem('left', '100%');
}
Note that I set #direction to RTL so above style would result like this
.TestClass{
right: 100%;
}
If you set chnage the directionality of the page to LTR it would result like this
.TestClass{
left: 100%;
}
Let me know if it helps you or not
I implemented a rtl extension in dotless. Find it on github. That extension reverwses float:left to float:right and margin-left:5px to margin-right:5px.
It also supports prefixing properties to control how they are reversed.
A lot more info is available on the dotless wiki for the plugin.
You can find generic information on how to use dotless and plugins also on the wiki.