How can I interpolate a string/fraction mixin variable in LESS? - less

I'm trying to my grid system to LESS.
I'm using fractions as strings (there's a reason) and need to interpolate them in the calc() expression.
In Sass I can do this...
#mixin move($fraction: '1/1') {
position: relative;
left: calc(99.999999% * #{$fraction});
}
In LESS when I try this...
.move(#fraction: '1/1') {
#_fraction: ~'#{fraction}';
left: calc(99.999999% * #_fraction);
}
It ends up throwing a Cannot read property 'numerator' of undefined error.
Apparently LESS can tell it's a fraction but then it poops out.
Can any LESS pros enlighten me?

You need to escape multiplication, addition, division, and subtraction in LESS or it will try output the calculated result. When less sees * in your code, it is trying to multiply 99.999999% and #_fraction.
For example when LESS sees calc(5px + 5px); it will output left: calc(10px);. You can except it like this ~'calc(5px + 5px);'.
Try using this code.
.move(#fraction: '1/1') {
#_fraction: ~'#{fraction}';
left: calc(~'99.999999% *' #_fraction);
}

Related

Sass, marking a variable as a string [duplicate]

This question already has answers here:
Sass Interpolation of Mixin, Function, and Variable names
(3 answers)
Closed 8 years ago.
I'm trying to run a loop in Sass to generate the following variables:
$size-xs: 1em;
$size-sm: 1.1em;
$size-md: 1.2em;
$size-lg: 1.3em;
This is what I have so far:
$size-base:1em;
$size-increment:0.1em;
$size-list: "xs","sm","md","lg";
#each $value-size in $size-list {
$size-#{$value-size}:#{$i};
$i:$size-base + $size-increment;
}
The problems I'm having is the $size- is interpreted as a variable, but it's not, it's the root of a set of variables I'd like to generate. Any ideas on how to accomplish this?
Thanks so much for any help!
The error you were getting was because variable names can't be interpolated like that. (You were also trying to use $i before it was defined.)
But you can directly name the elements that will be styled. For example:
$size-base: 1em;
$size-increment: 0.1em;
$size-list: "xs", "sm", "md", "lg";
#each $value-size in $size-list {
$size-base: $size-base + $size-increment;
.#{$value-size} {
font-size: $size-base;
}
}
You can even just output silent placeholders instead of elements, and then extend them when needed.
$size-base: 1em;
$size-increment: 0.1em;
$size-list: "%xs", "%sm", "%md", "%lg";
#each $value-size in $size-list {
$size-base: $size-base + $size-increment;
#{$value-size} {
font-size: $size-base;
}
}
h2 {
#extend %lg;
}
This will only compile the h2 style declaration.
See the example at http://sassmeister.com/gist/a400ded59756a814c47e.

Auto-generate LESS styles for sprite icons

I have icon sprites image with each icon in a 20 by 20 pixel area. Each icon has several variants (black, colour, white small etc.). And have a significant amount of them. Instead of writing styles for each individual icon I'd rather just provide their names in my LESS file and let the processor generate styles for them.
This is what I came up with but it doesn't seem to work.
#icons: upvote,downvote,comment,new,notify,search,popup,eye,cross;
#array: ~`(function(i){ return (i + "").replace(/[\[\] ]/gi, "").split(","); })("#{icons}")`;
#count: ~`(function(i){ return i.split(",").length; })("#{icons}")`;
.iconize (#c) when (#c < #count) {
#val: ~`(function(a){ return a.replace(" ","").split(",")[0]; })("#{array}")`;
#array: ~`(function(a){ a = a.replace(" ","").split(","); a.splice(0, 1); return a; })("#{array}")`;
&.#{val} { background-position: (-20px * #c) 0; }
&.color.#{val} { background-position: (-20px * #c) -20px; }
&.white.#{val} { background-position: (-20px * #c) -40px; }
.iconize(#c + 1);
}
.iconize(#c) when (#c = #count) {}
.iconize(0);
The only thing I'd like to edit is the #icons variable where I just enter their names. And I'm using Web Essentials addin for Visual Studio 2013 to automatically process my LESS file on file save.
What am I doing wrong?
Pure LESS (assuming you're using Web Essentials 2013 which uses LESS 1.5.x):
#icons: upvote, downvote, comment, new, notify, search, popup, eye, cross;
.iconize();
.iconize(#i: length(#icons)) when (#i > 0) {
.iconize((#i - 1));
#value: extract(#icons, #i); // LESS arrays are 1-based
.#{value} {background-position: (-20px * (#i - 1)) 0}
.color.#{value} {background-position: (-20px * (#i - 1)) -20px}
.white.#{value} {background-position: (-20px * (#i - 1)) -40px}
}
I removed & from selector names since it has no effect when you generate these classes in the global scope (but put it back if you actually need .iconize to be nested in another ruleset). It is also possible to calculate array length in earlier LESS versions (that have no length function) w/o any javascript, but I don't list this method here since it's quite scary (and you don't need it anyway).
Your javascript based loop is in fact less or more correct but the problem is all values returned by LESS inline javascript are of so-called "anonymous value" type and not a numbers so that when (#c < #count) condition is always true and the loop becomes infinite. (basically the condition is expanded exactly as when (0 < ~'9') ... when (9 < ~'9') = true etc.)
I think it depends on the version of LESS you use. Different versions of LESS handle array like structures and their length different.
Since LESS 1.5 you can define an array with quotes, like:
#array: "value1","value2"; and calculate its length with length(#array).
For example see also:
Sprites LESS CSS Variable increment issue
With LESS 1.5 your code ends in an endless loop: "SyntaxError: Maximum call stack size exceeded in"

DecimalFormat : replace floats with spaces

We need to display currency amounts in a browser table and since those are numbers, they should be right aligned.
Problem is with currencies like Japan which do not have floating point currencies.
So for them, we cannot show 5.10 Yen
Now, the requirement is to show something like this:
(Note the alignment and the mixing of decimal currencies with non-decimal)
5.23
12.00
3.24
5
9
11.00
In the above, 5 and 9 are Japanese Yens while others are USD
So the requirement is to replace floating points with spaces for currencies like JPY. Spaces are required to have proper alignment of the currencies.
Offcourse, for other currencies, the 2 decimal places should be there.
Does anyone know how the above can be done?
Thanks a lot in advance!!
Based on the below replies:
1) The values are sent from a Java server and fed into a JavaScript library SlickGrid
2) We want to control values at the server level because the SlickGrid code is not very amenable for this kind of work.
3) Currencies are being stored as floats because customer wants to see them that way :( and that's kind of correct because mostly people are bothered about dollars not cents, but then, some people are concerned about cents too.
I was really hoping of some option with DecimalFormat only as that would have been the best solution.
Else I would have to resort to the ugly solution of parsing and string massaging.
Currency should never be stored as a floating point number. It should always be integer amounts of the lowest denomination you are working with (Yen, Cents, Paise).
If you need to work with amounts smaller than the lowest natural denomination, store currency as integer amounts representing multiples of a specified fraction. For example 6 tenths of a Yen.
As for the formatting, there is a discussion of simple options here.
following CSS is basically taken from the link in jsj's answer (extracting the most relevant portion)
HTML only version:
td { font-family: monospace; }
span.int {
text-align: right;
float: left;
width: 3em;
}
span.fractional {
text-align: left;
float: right;
width: 2em;
}
<table>
<tr><td><span class="int">5</span><span class="fractional">.23</span></td></tr>
<tr><td><span class="int">12</span><span class="fractional">.00</span></td></tr>
<tr><td><span class="int">3</span><span class="fractional">.24</span></td></tr>
<tr><td><span class="int">5</span><span class="fractional"></span></td></tr>
<tr><td><span class="int">9</span><span class="fractional"></span></td></tr>
<tr><td><span class="int">11</span><span class="fractional">.00</span></td></tr>
</table>
JavaScript version:
function spanInt(content){ var span = document.createElement('span');span.className = 'int'; span.textContent = content; return span; }
function spanFractional(content){ var span = document.createElement('span'); span.className = 'fractional'; span.textContent = content; return span;}
function tableCell_int(value){
var td = document.createElement('td');
td.appendChild(spanInt(value));
td.appendChild(spanFractional(''));
return td;
}
function tableCell_float(value){
var intPart = Math.floor(value);
var fracPart = value - intPart;
var td = document.createElement('td');
td.appendChild(spanInt(intPart));
td.appendChild(spanFractional('.' + Math.round(100*fracPart)));
return td;
}
function tr(cell){
var elt = document.createElement('tr');
elt.appendChild(cell);
return elt;
}
var theTable = document.getElementById('theTable');
function add_int(value){ theTable.appendChild(tr(tableCell_int(value))); }
function add_float(value){ theTable.appendChild(tr(tableCell_float(value))); }
add_float(5.23); add_float(12); add_float(3.24);
add_int(5); add_int(9); add_float(11);
td { font-family: monospace; }
span.int {
text-align: right;
float: left;
width: 3em;
}
span.fractional {
text-align: left;
float: right;
width: 2em;
}
<table id='theTable'></table>

Create a padded box mixin with Compass and Blueprint

The interface should look like this
+box(optional_padding_value_in_columns)
It shouldn't break the grid. (If placed in column that spans 7 units, then the box should stay within the 7 units.)
Compass _scaffolding.sass actually includes this little number:
// Mixin +box to create a padded box inside a column.
=box
:padding 1.5em
:margin-bottom 1.5em
:background #E5ECF9
But the padding blows up the grid.
The +box mixin is meant for a block element used inside a column. The 1.5em padding is not proportional to the blueprint column width so putting it on a column element will break the layout.
Try this:
// You can supply one padding value or all 4
=padded_column(!n, !p1, !p2=!p1, !p3=!p1, !p4=!p1)
+column(!n)
:padding= !p1 !p2 !p3 !p4
!width = !blueprint_grid_width * !n + (!blueprint_grid_margin * (!n - 1)) - !p2 - !p4
.padded_box
+padded_column(!two_columns_secondary, 10px, 0, 5px, 15px)
:background #f0f0f0

LESS - Convert negative value to a positive

I'm trying to convert a negative variable in lesscss in to a positive:
#var: -100px;
.test {
height: #var * -1; // expect result to be 100px
}
But when I try and compile this code, I get 'unrecognized input' error.
Use the Abs function:
#var: -100px;
.test {
height: abs(#var);
}