While very helpful with arithmetic, Sass falls a bit short with mathematical helper functions. There has been an open issue on the official repository to ask for more math-related functions for almost 3 years.
Some third-party vendors like Compass or SassyMath provide advanced support for math features, but they are external dependencies which could (should?) be avoided.
One of the most popular request on this matter is a power function or even a exponent operator. Unfortunately, there is still no support for this in Sass and while it is still under active discussion, it is unlikely to happen very soon.
Meanwhile, being able to raise a number to a certain power happens to be very useful in CSS. Here are a few examples where it would come in handy:
- to determine the luminance of a color;
- to fix a number to a certain amount of digits;
- to do some (inverse) trigonometry…
Sass implementation
Fortunately for us, it is possible (and quite simple) to create a power function with nothing but Sass. All we need is a loop and some basic mathematical operators (such as *
and /
).
Positive integer exponents
Our function (named pow
) in its smallest form would look like this:
@function pow($number, $exponent) {
$value: 1;
@if $exponent > 0 {
@for $i from 1 through $exponent {
$value: $value * $number;
}
}
@return $value;
}
Here is an example:
.foo {
width: pow(20, 2) * 1px; // 400px
}
Positive or negative integer exponents
However it only works with a positive `$power` argument. Allowing negative exponents wouldn’t be so hard, we just need a small extra condition:
@function pow($number, $exponent) {
$value: 1;
@if $exponent > 0 {
@for $i from 1 through $exponent {
$value: $value * $number;
}
} @else if $exponent < 0 {
@for $i from 1 through -$exponent {
$value: $value / $number;
}
}
@return $value;
}
Here is an example:
.foo {
width: pow(10, -2) * 1px; // 0.0001px
}
Positive or negative exponents
Now, what if we want non-integer exponents? Such as 4.2
for instance? The truth is that it really is not easy. It still is doable, but it requires more than just a loop and a few operations.
This has been done on the Bourbon repository in order to complete the modular-scale(..)
function from the framework (although has been declined). Here is the code:
@function pow($number, $exponent) {
@if (round($exponent) != $exponent) {
@return exp($exponent * ln($number));
}
$value: 1;
@if $exponent > 0 {
@for $i from 1 through $exponent {
$value: $value * $number;
}
} @else if $exponent < 0 {
@for $i from 1 through -$exponent {
$value: $value / $number;
}
}
@return $value;
}
@function factorial($value) {
$result: 1;
@if $value == 0 {
@return $result;
}
@for $index from 1 through $value {
$result: $result * $index;
}
@return $result;
}
@function summation($iteratee, $input, $initial: 0, $limit: 100) {
$sum: 0;
@for $index from $initial to $limit {
$sum: $sum + call($iteratee, $input, $index);
}
@return $sum;
}
@function exp-maclaurin($x, $n) {
@return (pow($x, $n) / factorial($n));
}
@function exp($value) {
@return summation('exp-maclaurin', $value, 0, 100);
}
@function ln-maclaurin($x, $n) {
@return (pow(-1, $n + 1) / $n) * (pow($x - 1, $n));
}
@function ln($value) {
$ten-exp: 1;
$ln-ten: 2.30258509;
@while ($value > pow(10, $ten-exp)) {
$ten-exp: $ten-exp + 1;
}
@return summation(ln-maclaurin, $value / pow(10, $ten-exp), 1, 100) + $ten-exp * $ln-ten;
}
Further considerations
Well, that was intense. If you happen to need support for non-integer exponents (like 4.2
), I recommend you use an external dependency which provides it (such as sass-math-pow) instead of including all this code in your project. Not that it is a bad thing per se, but it is not really the role of your project to host such a large a mount of non-authored polyfilling code (that’s why we have package managers).
Also note that all these operations are quite intensive for such a thin layer as Sass. At this point, and if your design rely on advanced mathematical functions, it is probably better to pass the implementation of these helpers from the upper layer (Node.js, Ruby, etc.) down to Sass through a plugin system (Eyeglass, Ruby gem, etc.).
But for basic pow(..)
usage, I can’t recommend the simple versions enough!
How can i set the $number as a dynamic VW value?
Here’s a helper function to deal with units:
It first strips the units then adds them back to the result.
Just a quick note, the non-integer exponent function shows the following:
To fix this, find the two summation calls and change the first parameters accordingly:
Why should external Sass dependencies be avoided? Functions and mixins from external libraries aren’t included in the compiled css, so it isn’t like the user is going to have more HTTP requests and larger file sizes to download. I can see why this is a concern for some people with say, javascript libraries that do possibly decrease optimization, but why do you think it is something to avoid with Sass?
I am using a version of these complex pow() functions to calculate color Luminance. I needed to be about to raise a number to the power of 2.4.
It works, but it is slow for these math-heavy complex calculations. Is there a way to manually leverage SASS caching when it churns through a color comparison that has not changed? I am thinking about developers waiting for long
--watch
compile times when this function’s contents have not changed. No need to compile them all again.