I have a variable I am passing through this loop in less that will be used to name the class dynamically. Nevermind that this has the possibility of generating a heinous amount of classes, I am wondering why if I change the "red" variable to anything else in the function call or anywhere else, basically if they are not all the same, I get an "Object[object] has no method toCSS.
note: I am using the less app on mac to compile on save.
the mixin call:
.createShades (10, "red", #ff3333);
The mixin itself
// Generate our reds
.createShades (#index, #color, #base-color) when (#index > 0) {
#i: #index;
.createShade (#index, #i, #color, #base-color);
.createShades (#index - 1, #color, #base-color);
}
.createShades (0, "red", #base-color) {}
.createShade (#index, #i, #color, #base-color) when (#i > 0) {
#num: percentage((lightness(#base-color)/100) * (1 - (#i / #index)));
#newShade: darken(#base-color, #num);
(~".#{color}-#{i}-#{index}-text") {
color: #newShade;
}
(~".#{color}-#{i}-#{index}-bg") {
background-color: #newShade;
}
.createShade (#index, #i - 1, #color, #base-color);
}
.createShade (#index, 0, "red", #base-color) {}
You need to change your ending mixin (used when the value reaches 0) to have #color instead of "red"). So...
.createShades (0, #color, #base-color) {}
.createShade (#index, 0, #color, #base-color) {}
This is because it is seeking a match for "red" so when you change your call to another color, it throws an error since it does not match "red".
Related
I'm working with LESS version 1.6.0 (not by choice). I read through the LESS documentation on Mixin Guards. I created a LESS function to cycle through a key-value list of colors, to generate CSS classes. Within this function, I want to set a condition to change the properties of my styles, if my prefixes are different. (i.e. txt and bg should have the same CSS declaration, while border has a different one). I believe my code below "is" doing that, but its also compounding my CSS properties for each class. Here is what I tried...
/*MY COLORS*/
#black-n25: #bfbfbf;
#black-n75: #4d4d4d;
#black: #000;
#white: #fff;
/*MY LIST (KEY VALUE PAIRS)*/
#colors: ~'black-n25' #black-n25, ~'black-n75' #black-n75, ~'black' #black, ~'white' #white;
/*MY FUNCTION TO CYCLE THROUGH MY LIST AND GENERATE CLASSES*/
.generate-classes(#list, #prefix, #mprop) {
.iter(length(#list));
.iter(#i) when (#i > 0) {
.iter(#i - 1);
#pair: extract(#list, #i); /*#colors -> #list -> #pair*/
#key: extract(#pair, 1); /*the key from #colors above*/
#value: extract(#pair, 2); /*the value from #colors above*/
/*this is where things go wrong...*/
.mixin(#prefix) when (#prefix = txt), (#prefix = bg) {
#{mprop}: #value;
}
.mixin(#prefix) when (#prefix = border) {
#{mprop}: solid 2rem #value;
}
.#{prefix}-#{key} {
.mixin(#prefix);
}
}
}
/*CALL FUNCTION FOR EACH CASE | PARAMETERS ARE...THE LIST, PREFIX (CSS SELECTOR NAME), AND CSS PROPERTY VALUE*/
.generate-classes(#colors,txt,color);
.generate-classes(#colors,bg,background-color);
.generate-classes(#colors,border,border);
...and a visual of what's being generated in the browser...
As you can see its compounding declarations for each class except the first class. Each CSS class should only have one declaration. What am I doing wrong?
This took some further digging. but now I can see, as of LESS version 1.5.0, CSS Guards can help when iterating through a key-value paired list items in LESS. I removed my use of Mixin Guards from my original logic and instead have the following setup, which now works.
This now gives me the flexibility to add many more colors and custom CSS declarations for custom prefixes, all within a single function.
/*MY COLORS*/
#black-n25: #bfbfbf;
#black-n75: #4d4d4d;
#black: #000;
#white: #fff;
/*MY LIST (KEY VALUE PAIRS)*/
#colors: ~'black-n25' #black-n25, ~'black-n75' #black-n75, ~'black' #black, ~'white' #white;
/*MY FUNCTION TO CYCLE THROUGH MY LIST AND GENERATE CLASSES*/
.generate-classes(#list, #prefix, #mprop) {
.iter(length(#list));
.iter(#i) when (#i > 0) {
.iter(#i - 1);
#pair: extract(#list, #i); /*#colors -> #list -> #pair*/
#key: extract(#pair, 1); /*the key from #colors above*/
#value: extract(#pair, 2); /*the value from #colors above*/
/*this now works and sets a single declaration for each class...*/
.#{prefix}-#{key} when (#prefix = txt), (#prefix = bg) {
#{mprop}: #value;
}
.#{prefix}-#{key} when (#prefix = border) {
#{mprop}: solid 2rem #value;
}
}
}
/*CALL FUNCTION FOR EACH CASE | PARAMETERS ARE...THE LIST, PREFIX (CSS SELECTOR NAME), AND CSS PROPERTY VALUE*/
.generate-classes(#colors,txt,color);
.generate-classes(#colors,bg,background-color);
.generate-classes(#colors,border,border);
I need that Less compiler don't convert to HEX colors defined in rgba(), even if alpha channel is 1
Actually, the following Less code:
#color1: rgba(0,0,0,1);
#color2: rgba(0,0,0,0.1);
#color1_light: lighten(#color1,90%);
#color2_light: lighten(#color2,90%);
.a {
background:#color1;
color: #color1_light;
}
.b {
background:#color2;
color: #color2_light;
}
is processed to:
.a {
background: #000000;
color: #e6e6e6;
}
.b {
background: rgba(0, 0, 0, 0.1);
color: rgba(230, 230, 230, 0.1);
}
but I need to have (for many reasons related to further evaluations)
.a {
background: rgba(0, 0, 0, 1);
color: rgba(230, 230, 230, 1);
}
.b {
background: rgba(0, 0, 0, 0.1);
color: rgba(230, 230, 230, 0.1);
}
How to solve this?
Basically this is pretty easy, all you have to do is to escape your expression, below you will find two examples how to force less compiler to display rgba format even though alpha is 1:
#color1: rgba(0,0,0,1);
body {
color: ~"rgba("red(#color1), green(#color1), blue(#color1),~" 1)";
background: %(~"rgba(%s, %s, %s, 1)", red(#color1), green(#color1), blue(#color1));
}
Both examples will produce rgba(0, 0, 0, 1), it's up to you which one do you prefer. I bet you will find more info in the docs under string escape and string replace
//EDIT
yup, this is tricky, but still, you can extend this with a mixin so it won't look that bad in code hereafter.
#color1: rgba(0,0,0,1);
.rgba(#color) {
//#rgba: ~"rgba("red(#color), green(#color), blue(#color),~" 1)";
#rgba: %(~"rgba(%s, %s, %s, %s)", red(#color), green(#color), blue(#color), alpha(#color));
}
.torgba(#property, #color) {
#{property}: %(~"rgba(%s, %s, %s, %s)", red(#color1), green(#color1), blue(#color1), alpha(#color1));
}
body {
.rgba(#color1); // mixin returns #rgba variable that may be used later on, it's not color object however, but a string
color: #rgba;
background: #rgba;
.torgba(border-color, #color1); // do the same, but returns property with color in given format
}
I've created a .smartMargin() mixin for LESS CSS to be used in responsive design LESS.
I'm sharing it here for the benefit of others and also because I'm curious if there's any way to make it more efficient.
Idea
The mixin is designed to be called from within another mixin that represents a responsive design breakpoint. The idea is that you may want to override top, left, bottom, right margins individually by overriding one or more margins in each successive breakpoint.
In main mixin I just want one parameter for #videoMargin as opposed to #videoMarginLeft, #videoMarginRight etc so that's why I've called it 'smartMargin'.
Usage
In my main file I define a breakpoint like this, and then call this mixin several times:
.breakpoint(#width, #color, #labelsSize, #videoMargin)
{
video
{
.smartMargin(#videoMargin);
}
}
.breakPoint(10em, red, 3em, 1em auto 1em auto);
.breakPoint(10em, green, 3em, 2em unset unset unset);
.breakPoint(20em, blue, 3em, unset 3em unset unset);
Output css
So for a given value of #videoMargin here's the output css generated
generated css
-------------
.smartMargin(3em); margin: 3em;
.smartMargin(3em 1em 2em 4em); margin: 3em 1em 2em 4em;
.smartMargin(3em unset unset unset); margin-top: 3em;
.smartMargin(3em unset unset 4em); margin-top: 3em;
margin-right: 3em;
Implementation
The mixin is as follows. It works well but it just seems a little clumsy in places and you need to provide either 4 or 1 parameters. If anybody can optimize this I'd be very interested to see.
.smartMargin(#margin) when (length(#margin) = 4)
{
._smartMargin() when (extract(#margin, 4) = unset), (extract(#margin, 3) = unset), (extract(#margin, 2) = unset), (extract(#margin, 1) = unset)
{
.marginComponent(#name, #value)
{
& when not (#value = unset)
{
#{name}: #value;
}
}
.marginComponent(margin-top, extract(#margin, 1));
.marginComponent(margin-right, extract(#margin, 2));
.marginComponent(margin-bottom, extract(#margin, 3));
.marginComponent(margin-left, extract(#margin, 4));
}
._smartMargin() when (default())
{
margin: #margin;
}
._smartMargin();
}
.smartMargin(#margin) when (default())
{
& when not (#margin = ~'') and not (#margin = unset)
{
margin: #margin;
}
}
You could possibly rewrite it to something like:
.smartMargin(#margin) when (isem(#margin)),(isem(extract(#margin,1))) and (isem(extract(#margin,2))) and (isem(extract(#margin,3))) and (isem(extract(#margin,4))) {
margin: #margin;
}
.smartMargin(#margin) when (default()) and (4 = length(#margin)) {
#positions: top, right, bottom, left;
.setmargin(#position,#margin) when (isem(#margin)){
margin-#{position}: #margin;
}
.setmargins(#i:1) when (#i <= 4){
.setmargin(extract(#positions,#i);extract(#margin,#i));
.setmargins((#i + 1));
}
.setmargins();
}
But in the first place i don't think there is something wrong with your code. Personally is should consider the use of unset. I think you should use the initial keyword or even 0 in stead of unset. This enables you to do the following:
.smartMargin(#margin){
margin: #margin;
}
.one{
.smartMargin(3em);
}
.two{
.smartMargin(3em 1em 2em 4em);
}
.three{
.smartMargin(3em 0 0 0);
}
.four{
.smartMargin(3em 0 0 4em);
}
Or consider to use Passing Rulesets to Mixins, than you can use something like that shown below:
.breakPoint(#width, #color, #labelsSize, #videoMargin)
{
video
{
#videoMargin();
}
}
.breakPoint(10em, red, 3em, {margin: 1em auto 1em auto;});
.breakPoint(10em, red, 3em, {margin: 1em auto;});
.breakPoint(10em, green, 3em, {margin: 2em 0 0 0;});
.breakPoint(10em, green, 3em, {margin: 2em 0 0;});
.breakPoint(10em, green, 3em, {margin-top: 2em;});
.breakPoint(20em, blue, 3em, {margin: 0 3em 0 0;});
.breakPoint(20em, blue, 3em, {margin-right: 3em;});
I am trying to generate a number of classes in a loop based on a number of pre-defined variable snippets.
I have a variables.less document that I am importing at the top of this less file containing my color variables. I then want to generate matching classes for these, but I am unable to get less to compile the variable.
My code:
.loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
.loop-class(#list, #index: 1) when (isstring(extract(#list, #index))) {
#status: extract(#list, #index);
.button-#{status} {
color: ~'#button-#{status}';
}
.loop-class(#list, (#index + 1));
}
Which compiles to:
.button-primary {
color: #button-primary;
}
.button-success {
color: #button-success;
}
etc etc
As you can see, I get the variable name to concatenate correctly, but I can not get it to resolve, so I'm guessing that LESS has already done it's variable compilation before getting to this function?
I have already tried moving the variables into this document, as well as wrapping the variables in a mixin and adding that inside the .loop-class, but neither of these seemed to help.
I also tried something like:
#status: extract(#list, #index);
#compileClass: ~'#button-#{status}';
.button-#{status} {
color: #compileClass;
}
where I am saving the variable in a another one and then referencing that, but it yields the same result.
I looked at less css calling dynamic variables from a loop and tried implementing that as follows:
.loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
.define(#var) {
#fallback: ~'#button-#{var}';
}
.loop-class(#list, #index: 1) when (isstring(extract(#list, #index))) {
#status: extract(#list, #index);
.button-#{status} {
.define(#status);
color: ##fallback;
}
.loop-class(#list, (#index + 1));
}
But that gave me the error that ##button-danger (last in the index) is undefined, so it still can't resolve the variable.
Is it obvious to you guys what I'm doing wrong?
Thanks for your help!
Missing Brackets
You are missing a set of needed brackets to resolve the variable:
LESS
//imported from another file
#button-primary: cyan;
#button-success: green;
#button-info: orange;
#button-warning: yellow;
#button-danger: red;
//in your mixin file
.loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
.loop-class(#list, #index: 1) when (isstring(extract(#list, #index))) {
#status: extract(#list, #index);
.button-#{status} {
color: ~'#{button-#{status}}'; /* two more brackets needed */
| |
here here
}
.loop-class(#list, (#index + 1));
}
CSS Output
.button-primary {
color: #00ffff;
}
.button-success {
color: #008000;
}
.button-info {
color: #ffa500;
}
.button-warning {
color: #ffff00;
}
.button-danger {
color: #ff0000;
}
Cleaner More Friendly Code
Also, as a matter of less cluttered and more user friendly code, you can remove your multiple string interpolations needed for the mixing call by changing isstring to iskeyword in your mixin:
.loop-class(primary, success, info, warning, danger;); /* cleaner code on call */
.loop-class(#list, #index: 1) when (iskeyword(extract(#list, #index))) {
#status: extract(#list, #index);
.button-#{status} {
color: ~'#{button-#{status}}';
}
.loop-class(#list, (#index + 1));
}
I'm trying to write a mixin that returns a variable for converting px to em. I've toyed with a few things, but ultimately i'd like to call a mixin and get a return value similar to SASS's functions. Based on on return values here: http://www.lesscss.org/#-return-values, I can only define a variable once as a return value. Example:
Mixin
.px-to-emz( #size, #base: #font-size-base ){
#em: round( unit( (#size / #base), ~"em" ), 3 );
}
Call it:
.foo {
font-size: #em;
.px-to-emz(10, 16px);
height: #em;
.px-to-emz(200, 16px);
}
Fine, if you want to only return one variable, but if i want to return multiple variables I need to define new variable names. Here's what i'd ideally like to do
Mixin:
.px-to-ems( #size, #var: 'em', #base: #font-size-base ){
~'#{var}': round( unit( (#size / #base), ~"em" ), 3 );
}
Call it:
.foo {
font-size: #font-size;
.px-to-ems(10, 'font-size', 16px);
height: #height;
.px-to-ems(200, 'height', 16px);
}
#1
So far the best known solution for this problem is to put each mixin call into its own scope:
.px-to-ems(#size, #base: #font-size-base) {
#-: round(unit((#size / #base), em), 3);
}
.foo {
.-() {font-size: #-; .px-to-ems( 10, 16px)}
.-() {height: #-; .px-to-ems(200, 16px)}
.-;
}
Replace #- and .- with whatever identifiers you find suitable.
#2
The other way around is to use recently added (Less 1.6.x) property interpolation feature:
.px-to-ems(#property, #size, #base: #font-size-base) {
#{property}: round(unit((#size / #base), em), 3);
}
.foo {
.px-to-ems(font-size, 10, 16px);
.px-to-ems(height, 200, 16px);
}
It's more clean than #1 if you simply need to assign the "function" result to a property.