What is the equivalent of Scss\' emCalc()
in less?
padding: emCalc(24px);
in Scss will calculate em based on the viewpoint and
With LESS you can build your own functions. I use this function with the grunt-contrib-less package. The formatting is slightly different from the usual less functions formatting. Note you need to insert less as a parameter with this package.
em: function(less, fontsize, basefontsize){
if (less.functions.functionRegistry.get('ispixel')){
basefontsize = (basefontsize) ? basefontsize.value : 16
return fontsize.value/basefontsize+'em';
}
}
Now you can just call this function in your LESS stylesheets as
.class{
font-size:em(16px);
}
Which will compile to
.class{
font-size:1em;
}
Note ems will still be relative to the container's css. So the font-size of 1em will not be 16px if the wrapping div has a font-size set of 0.8em for example.
Update: The non-grunt-contrib-less version
If you put this in a javascript file which you include in your html before your less file, you can use the function as stated above.
$.extend(less.functions, {
em: function(fontsize, basefontsize){
if (this.ispixel(fontsize)){
basefontsize = (basefontsize) ? basefontsize.value : 16
return new(tree.Dimension)(fontsize.value/basefontsize,'em');
}
}
}
If you're compiling your LESS on the server side, you can do this with CSSHub
/* Test unit convert */
div {
.unit-convert(font, ~'bold em(14px)/1.2 @{csshub_font-stack-arial}');
// Ignore base: @csshub_font-size: 100%;
.unit-convert(margin, ~'rem(18px) rem(20px) rem(15px)', true);
// Default: 100% == 16px == 1em == 12pt == 1rem
.unit-convert(font-size, ~'px(100%)'); // Test: % to px
.unit-convert(font-size, ~'pt(16px)'); // Test: px to pt
.unit-convert(font-size, ~'em(12pt)'); // Test: pt to em
.unit-convert(font-size, ~'rem(1em)'); // Test: em to rem
.unit-convert(font-size, ~'percent(1rem)'); // Test: rem to %
}
This generates this css
/* Test unit convert */
div {
font: bold 0.875em/1.2 Arial, 'Helvetica Neue', Helvetica, sans-serif;
margin: 1.125rem 1.25rem 0.9375rem;
font-size: 16px;
font-size: 12pt;
font-size: 1em;
font-size: 1rem;
font-size: 100%;
}
Another variant is shown here.
/*
Toolkit: Rem
============
.rem() takes a @property, and @list of values (in px or unitless) and
converts to rem.
e.g.,
.selector {
.rem( margin, 20 auto eggs 666% );
.rem( font-size, 12 );
}
Makes life a little easier when working from designs specced in pixels.
*/
.rem( @property, @list, @base: 16 ) {
@n: length(@list);
// Only convert numbers that are not percentages or 0;
._merge( @px ) when ( isnumber(@px) ) and not ( ispercentage(@px) ) and not ( @px = 0 ) {
@rem: ( @px / @base );
@{property}+_: unit(@rem, rem);
}
._merge( @px ) when ( default() ) {
@{property}+_: @px;
}
._loop( @n ) when ( @n > 0 ) {
._loop((@n - 1));
@val: extract(@list, @n);
._merge( @val ); // merges values onto @property with space character.
}
._loop( @n );
}
Source: https://gist.github.com/dominicwhittle/66275e9014bec7ec1b6f
LESS doesn't have such a feature, according to documentation.
Built-in unit or convert functions do not provide such a convertion.
Note that Scss' implementation of this function assumes convertion using one global font-size value. You can easily achieve same thing in LESS with the use of variables:
@em: 16px; // LESS variable - default value for 1em
And then use it like this:
div {
height: @em;
width: 6 * @em;
}
Code above compiles to:
div {
height: 16px;
width: 96px;
}
Note that this (and Scss' version, too) is not the way that real em work, because in CSS dimensions specified in em
are computed based on font size of element on which they are used.
None of the above examples worked for me, so I ended up changing it and it works:
@basefontsize: 14px;
.em(@fontsize, @basefontsize: @basefontsize) {
@result: if(ispixel(@fontsize), unit(unit(@fontsize)/unit(@basefontsize), em),@fontsize)
}
.my-class {
font-size: .em(16px)[];
padding-bottom: .em(14px)[];
padding-top: .em(14px, 16px)[];
margin-top: .em(32px)[];
margin-left: .em(2em)[];
margin-bottom: .em(4rem)[];
}
Results in:
.my-class {
font-size: 1.14285714em;
padding-bottom: 1em;
padding-top: 0.875em;
margin-top: 2.28571429em;
margin-left: 2em;
margin-bottom: 4rem;
}
If you're compiling your LESS on the server side, and I personally would never do otherwise you can do this with LESS mixins and no custom functions.
That of course means you can't automatically calculate based on the size of the current node, but thwat's a little sounding scary to me. Here's another way to just convert pixels to ems.
@emInPx: 16; // 16px per em (global default)
.convertEm(@selector, @amt) when (isem(@amt))
{
@{selector}: @amt;
}
.convertEm(@selector, @amt, @emInPx: @emInPx) when (ispixel(@amt))
{
@{selector}: unit((@amt / @emInPx), em);
}
img.cat
{
.convertEm(max-width, 5em);
.convertEm(max-height, 3em);
}
img.dog
{
.convertEm(max-width, 100px);
.convertEm(max-height, 75px);
}
Note that I use the same .convertEm(..)
mixin for both pixels and ems, since you may not even know if the value is passed as a parameter. The guarded mixin correctly picks the right formula to convert.
This generates this css
img.cat {
max-width: 5em;
max-height: 3em;
}
img.dog {
max-width: 6.25em;
max-height: 4.6875em;
}
This all assumes 1em corresponds to 16px. If not you can change the default or pass it in depending upon the context you're in:
img.mouse
{
.convertEm(max-width, 100px, 32px);
.convertEm(max-height, 75px, 32px);
}
Disclaimer: I still don't fully understand how or if server generated LESS works can work with custom javascript functions so I'm assuming that when you generate server side you can't add custom functions. If I'm wrong please someone let me know!