LESS combine ruleset into two with different variables - css

I'm trying to combine one ruleset into two different rulesets with variable values swapped. Main purpose is LTR/RTL internationalization.
Usage:
h1 {
margin-top: 10px;
.directions({
margin-#{left}: 5px;
});
}
Expected output:
h1 {
margin-top: 10px;
}
.ltr h1 {
margin-left: 5px;
}
.rtl h1 {
margin-right: 5px;
}
I was able to get some results with the Passing Rulesets to Mixins function available in Less 1.7
.directions(#rules) {
#left: left;
.ltr & { #rules(); }
#left: right;
.rtl & { #rules(); }
}
The problem is that the #left variable is always set to the last value used in .directions() mixin (right in this case). Is there any way how to swap variable or pass it back outside of the mixin?
Note: I do not want to output LTR/RTL to two separate files, I'm trying to combine them into one file.

To understand Less variables scope and life-time see:
Lazy Evaluation (aka Lazy Loading).
Variable Semantics
Most Misunderstood
Scope
Last Declaration Wins
The solution for your particular case is as simple as:
.directions(#styles) {
.ltr & {
#left: left;
#styles();
}
.rtl & {
#left: right;
#styles();
}
}

Related

Name clash between Less mixin and CSS selector

I have this simplified Less script
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
.placeholder {
margin-top: 20px;
}
The output when I run this through my local compiler or winless online less compiler is
input {
margin-top: 20px;
}
input::-webkit-input-placeholder {
color: #333333;
}
.placeholder {
margin-top: 20px;
}
Insted of the desired output
input::-webkit-input-placeholder {
color: #333333;
}
.placeholder {
margin-top: 20px;
}
Is this a bug or am I missing something here?
By the result it looks to me like I can't have CSS-selectors with the same name as mixins with default values.
I'm running into this problem when compiling Bootstrap with my site specific code. In this particular case I can work around it, but as the project grows and I include other projects I can't imaging I have to keep track of any mixins with default values?
Edit: I see now that I should have read the manual and pretty much seen on the first page of the docs that everything can be treated as a mixin.
In Less, everything is technically a mixin irrespective of whether we write it with parantheses (as in with parameters) or without parantheses (as in like a CSS class selector). The only difference between the two is that when the parantheses are present, the properties present within it are not output unless called from within a selector block.
Quoting the Less Website:
It is legal to define multiple mixins with the same name and number of parameters. Less will use properties of all that can apply.
In this case, since the other mixin has a default value for its only parameter, both the properties can apply when called without any parameter and hence there is no way to avoid it from happening.
Workaround Solution: One possible solution to work-around this problem is to enclose all such conflicting rules within a parent selector (like body).
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
body{
.placeholder{
margin-top: 20px;
}
}
Compiled CSS:
input::-webkit-input-placeholder {
color: #333333;
}
body .placeholder {
margin-top: 20px;
}
Option 2: Extracted from the solution posted by seven-phases-max in the Less GitHub Issue thread.
For the particular use-case one of possible workarounds is to isolate conflicting classes in unnamed scope so they won't interfere with external names:
.placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
.placeholder();
}
& { // unnamed namespace
.placeholder {
background: #ffffff;
}
} // ~ end of unnamed namespace
Note: The above is a straight copy/paste from the GitHub thread without any modifications so as to not tamper with the information.
#mixin placeholder(#color: #333333) {
&::-webkit-input-placeholder { color: #color; }
}
input {
#include placeholder();
}
.placeholder {
margin-top: 20px;
}
that should work.
So if i understood right, you just want to add 20px on top of the placeholder ? Add padding-top to input instead.
input {
padding-top: 20px;
}

LESSCSS - use calculation and return value

H i,
Hoping you can help.
Is there a way for LESS to return just a value - feel like I'm missing something very obvious
Say I have:
#unit:em;
#basevalue:1;
Can I use something to give me a shorthand return for -
.someClass { padding: ~'#{basevalue}#{unit}'; }
Like say:
.returnUnit() { ~'#{basevalue}#{unit}'; }
.someClass { padding: returnUnit(); }
because what I'm ultimately hoping for is:
.returnUnit(#val) { #basevalue*#val#{unit}; }
.someClass { padding:returnUnit(0.5); }
Using a mixing I have to define the style property, however the value of this return function would be used for many different css properties.
Hope I made sense and I am just lacking deeper rtfm.
Many Thanks if you can.
Update as #Chococrocs pointer to the docs, thanks.
.average(#x, #y) {
#average: ((#x + #y) / 2);
}
div {
.average(16px, 50px); // "call" the mixin
padding: #average; // use its "return" value
}
Looks like what I need ? - just seeing if I can always tag on the unit variable to it....
Update: That gets part way ...
.unitRelative(#val) {
#value : #basevalue*#val;
#relative: ~'#{value}#{unit}';
}
/* usage */
.someClass {
.unitRelative(2);
padding: #relative;
}
But not when
.someClass {
.unitRelative(2);
padding:#relative;
.unitRelative(3);
margin:#relative;
}
Is there another way ?
LESS has no way as of yet to create a true "function," so we cope with it.
First
You can just use the unit function, like so:
LESS
.someClass { padding: unit(#basevalue, #unit); }
.someOtherClass { padding: unit(#basevalue*0.5, #unit); }
CSS
.someClass {
padding: 1em;
}
.someOtherClass {
padding: 0.5em;
}
Second
The mixins as functions is okay in some situations, but as you discovered, has the limitation of only setting the value once on the first call (and that is assuming a variable of the same name does not exist in that scope already).
LESS (first works right, second doesn't)
.returnUnit(#val:1) {
#return: unit(#basevalue*#val, #unit);
}
.someThirdClass {
.returnUnit(0.4);
padding: #return;
}
.someOoopsClass {
.returnUnit(0.4);
padding: #return;
.returnUnit(0.3);
margin: #return;
}
CSS Output
.someThirdClass {
padding: 0.4em;
}
.someOoopsClass {
padding: 0.4em;
margin: 0.4em; /* Ooops! Not 0.3em! */
}
Third
Limitation of the Second idea can be avoided by a second wrapping, as it isolates the scope for each variable returned by .returnUnit(), like so:
LESS
.someAccurateClass {
& {
.returnUnit(0.4);
padding: #return;
}
& {
.returnUnit(0.3);
margin: #return;
}
}
CSS Output
.someAccurateClass {
padding: 0.4em;
margin: 0.3em; /* Yes! */
}
Fourth
It may be better to merge ideas from the First and Third by adding some global variables and doing this:
LESS
#unit:em;
#basevalue:1;
#val: 1;
#setUnit: unit(#basevalue*#val, #unit);
.someAwesomeClass {
& {
#val: .2;
padding: #setUnit;
}
& {
#val: .1;
margin: #setUnit;
}
}
CSS Output
.someAwesomeClass {
padding: 0.2em;
margin: 0.1em;
}
So here we are using the unit function still as the First idea, but have assigned it to the variable #setUnit, so each time the variable is called, it runs the function. We still isolate our property blocks using the & {} syntax like in the Third solution, but now we just set the #val to what we want and call the #setUnit where we want.
There is a hack that is mentioned here by fabienevain using a global js function. Seems to be good option if you want a function with actual return value.
#fn: ~`fn = function(a) { return a; }`;
#arg: 8px;
p {
font-size: ~`fn("#{arg}")`;
}
I think you look for this, Mixin as a function
http://lesscss.org/features/#mixins-as-functions-feature
Reading your question, I think is what you're wishing, ;D
// mixin
.parseInt(#string) {
#parseInt: unit(#string, );
}
Usage:
.selector {
.parseInt(100px);
width: #parseInt + 10; // px will automatically be appended
}
Result:
.selector {
width: 110px;
}
one of the simplest work around would be to pass the property and the value.
mixin.less
.lighter(#property, #color) {
#{property}: multiply(white, fade(#color, 10%));
}
use.less
.my-class{
.lighter(background-color, #FF0000);
}
Results:
.my-class{
background-color: #fbe8eb;
}

How do I write any positioning into a LESS variable?

So I was writing up some code when I decided I wanted to make a variable for margin and padding, along with some other positioning too. But when I try I get errors.
This is what my code looks like:
//Positioning
#margin_t: margin-top:0;
#margin_r: margin-right:0;
#margin_l: margin-left:0;
#margin_b: margin-bottom:0;
#padding_t: padding-top:0;
#padding_r: padding-right:0;
#padding_l: padding-left:0;
#padding:_b: padding-bottom:0;
#center: text-align: center;
#left: text-align: left;
#right: text-align: right;
#relative: position: relative;
#justify: position: justify;
Can anyone give me their two-cents? Thank you!
As of LESS 1.7
You can include property values in variables by rulesets, like so (note bracketing and following semicolon; p.s. position: justify is not valid, I've changed it to absolute):
//Positioning
#margin_t: {margin-top:0;};
#margin_r: {margin-right:0;};
#margin_l: {margin-left:0;};
#margin_b: {margin-bottom:0;};
#padding_t: {padding-top:0;};
#padding_r: {padding-right:0;};
#padding_l: {padding-left:0;};
#padding:_b: {padding-bottom:0;};
#center: {text-align: center;};
#left: {text-align: left;};
#right: {text-align: right;};
#relative: {position: relative;};
#absolute: {position: absolute;};
A variable assigned ruleset is use much like a mixin (note the parentheses on the call), so:
.yourClass {
#relative();
}
Produces:
.yourClass {
position: relative;
}
The differences of a variable ruleset to a mixin are that you cannot pass parameters to a ruleset (directly anyway), but a ruleset can itself be passed as a parameter to a mixin. Also, variable rulesets will overwrite a previous definition (no matter properties defined), whereas mixins will merge property values. Rulesets work well for fixed values, like some of your items. They can be parametrized indirectly, something like so:
#margin_t: {margin-top: #tm;};
.yourClass {
#tm: 0;
#margin_t();
}
Varible rulesets actually work best for when one wants to pass to a mixin a group of properties or a single, unknown dynamic property. As an example of the latter, suppose you know you will want to set a single margin for an element, but depending on some context, you don't know which you want to set, so you want a single way to handle it. Then something like this can be done:
#margin_t: {margin-top: #value;};
#margin_r: {margin-right: #value};
#margin_l: {margin-left: #value;};
#margin_b: {margin-bottom: #value;};
.set-a-margin(#prop; #value: 0;) {
#prop();
}
.yourClass {
.set-a-margin(#margin_r; 10px);
}
.anotherClass {
.set-a-margin(#margin_b; 5px);
}
Produces:
.yourClass {
margin-right: 10px;
}
.anotherClass {
margin-bottom: 5px;
}
Basically, variable assigned rulesets just offer another way LESS can be used to code the way you may want to code. They can offer some functionality like mixins, with certain limitations and advantages different from those as related to variables.
Firstly, define your mixins like this:
.margin_t {
margin-top: 0;
}
.margin_b (#value: 0) {
margin-bottom: #value;
}
and then just use it!
body {
.margin_t();
.margin_b(15px);
}
You don't set the whole style. you would do #margin_t: 0;
Then use it
.myClass {
margin-top: #margin_t;
}

Overriding nesting, to stay in scope

I was wondering if I could override the nesting in LESS, to stay in my scope - just because it's simpler... :-)
If I have an element which has two states, depending on let's say a body-class, I need to define two CSS objects. I would like to have only one, per module.
.module {
h1 {
float: left;
}
}
body.secondary {
.module {
h1 {
float: right;
}
}
}
Like when you define the parent with &, is there any way to overwrite that parent?? So maybe it could look like this:
.module {
h1 {
float: left;
}
&=body.secondary {
h1 {
float: right;
}
}
}
Where the & is defined as another selector...
This could be awesome, and make my CSS more simple in terms of one CSS object per module.
Thanks... :-)
I've done some extensive commentary on this both in this answer and this answer. In your particular case, it would be this:
LESS
.module {
h1 {
float: left;
body.secondary & {
float: right;
}
}
}
CSS Output
.module h1 {
float: left;
}
body.secondary .module h1 {
float: right;
}
The key is to realize that the & is not just for targeting the "parent" per se in the html structure, but rather is a string replacement for the entire nested structure it sits within. So in my solution above, the nested LESS "parent" is .module h1 which is the string replaced at the & point in the body.secondary & construct nested within it.

Less - How to insert an #variable into property (as opposed to the value)

In less.js, I'm able to replace values with variables with no problems.
#gutter: 20px;
margin-left:e(%("-%d"), #gutter);
When trying to replace properties with variables, I get errors. How would I perform the following in Less?
#gutter: 20px;
#direction: left;
e(%("margin-%d"), #direction):e(%("-%d"), #gutter);
Thanks to Alvivi for the solution and research (you get the reward for that). I decided to add the following as the actual answer since this is a real way to set it up instead of looking at .blah() pseudo code..
Here's a real strategy for setting it up:
#gutter: 20px;
#dir: left;
#dirOp: right;
then create mixins to enhance margin and padding like so:
.margin(left, #dist:#gutter) {
margin-left:#dist;
}
.margin(right, #dist:#gutter) {
margin-right:#dist;
}
.padding(left, #dist:#gutter) {
padding-left:#dist;
}
.padding(right, #dist:#gutter) {
padding-right:#dist;
}
.lr(left, #dist: 0) {
left: #dist;
}
.lr(right, #dist: 0) {
right: #dist;
}
.. then you can just
#selector {
.margin(#dir);
}
or
#selector {
.margin(#dirOp, 10px);
}
all together:
#selector {
.margin(#dir);
.margin(#dirOp, 50px);
.padding(#dir, 10px);
.padding(#dirOp);
float:#dir;
text-align:#dirOp;
position:absolute;
.lr(#dir);
}
Easy breezy LTR/RTL with LESS! Woot!
Escaping, as says the documentation, is used to create CSS values (not properties).
There is a discussion with some workarounds here. One would be using parametric mixins. For example:
.g () { /* Common properties */ }
.g (right) { margin-right: e(...) }
.g (left) { margin-left: e(...) }

Resources