Sass Reference - - By Hugo Giraudel

Numbers

There has yet to be created a programming language that is not heavily based on numbers, any program in the world is made of numbers and computer calculations.

On top of that, CSS is a language that makes heavy use of numeric values, starting with lengths. Because of this, it is no surprise that Sass allows authors to manipulate numbers and run calculations from within their stylesheets.

Operators

Sass supports the basic 5 mathematical operators: + (addition), - (substraction), * (multiplication), / (division) and % (modulo).

$a: 4 + 2; // 6
$b: 4 - 2; // 2
$c: 4 * 2; // 8
$d: 4 / 2; // 2
$e: 4 % 2; // 0

Just like with the native CSS calc function, it is heavily recommended to use spaces around operators so that it is crystal clear that an operation has to be performed. Some Sass engines such as LibSass are known for having issues when not wrapping operators with spaces.

Sass also understands parentheses in calculations and correctly gives precedence to them when emitting a result:

$a: 4 + 2 * 2 - 4 / 2;     //  6
$b: (4 + 2) * (2 - 4) / 2; // -6

CSS allows slashes (/) to appear in values as a way of separating numbers, such as for the font property. Since Sass (actually SassScript) is an extension of the CSS property syntax, it must support this behaviour while also allowing / to be used for division. This means that by default, if two numbers are separated by / in Sass, then they will appear that way in the resulting CSS.

However, there are three scenarios where the / operator will be interpreted as division which actually cover the vast majority of cases where division is actually used:

  • if the value or any part of it is stored in a variable or returned by a function;
  • if the value is surrounded by parentheses;
  • if the value is used as part of another arithmetic expression.
.foo {
  $width: 42em;

  font: 1em / 1.35;           // Plain CSS,     no division
  width: $width / 2;          // Variable,    does division
  height: round(13.37em) / 2; // Function,    does division
  padding: (2em / 2);         // Parentheses, does division
  margin: 4.2em + 13em/37em;  // + operator,  does division
}

When wanting to use variables as part of a fraction such as for the font property, the trick is to escape them simply so they are literal:

.foo {
  $font-size: 1em;

  font: #{$font-size} / 1.35;
}

The power operator (^) doesn’t exist in Sass, nor does pow exist as a native function, but thankfully this is easily polyfilled:

/// Power function
///
/// @author Daniel Perez Alvarez
/// @source https://unindented.org/articles/trigonometry-in-sass/
///
/// @param {Number} $number - Number
/// @param {Number} $exp - Exponent
///
/// @return {Number} $number ^ $exp
@function pow($number, $exp) {
  $value: 1;

  @if $exp > 0 {
    @for $i from 1 through $exp {
      $value: $value * $number;
    }
  } @else if $exp < 0 {
    @for $i from 1 through -$exp {
      $value: $value / $number;
    }
  }

  @return $value;
}

For more advanced mathematical functions such as cos and sin, either rely on Compass or SassyMath or check these articles: Trigonometry In Sass and Inverse Trigonometric Functions With Sass.

Along with basic operators, Sass supports five comparison operators: < (strictly inferior than), <= (inferior than or equal to), == (strictly equal to), => (superior than or equal to), > (strictly superior than).

$a: 4  < 2; // false
$a: 4 <= 2; // false
$a: 4 == 2; // false
$a: 4 => 2; // true
$a: 4  > 2; // true

Obviously Sass does not support bitwise operators as it makes no sense for CSS to be able to perform bitwise operations, yet I have created a library to handle those with Sass only, called SassyBitwise.

Units

Sass preserves units during arithmetic operations. This means that units in Sass behave exactly like units in real life and are not just strings living at the end of numbers. Actually, both numbers with units and numbers without are of type number.

In order to make calculations with numbers with units, they have to have compatible units such as px and in or deg and rad. Sass cannot perform a computation where px is mixed with em, because one is absolute and the other is relative: both are incompatible.

Below is a list of compatible groups of units:

  • px, in, cm, mm, pc and pt are compatible
  • deg, rad, grad and turn are compatible
  • s and ms are compatible
  • Hz and kHz are compatible
  • dpi, dppx and dpcm are compatible

Along the same lines, two numbers with the same unit that are multiplied together will produce square units just like in real life. Obviously, square units such as square pixels (px²) do not exist nor do they belong to CSS, so they result in a Sass error.

Although this means that to add a unit to a unitless number, the correct way of doing it is to multiply it by 1 member of the desired unit.

$number: 42;
$number-with-unit: $number * 1em; // {number} 42em

// Don't do that
$string: $number + em; // {string} 42em

Similarly, removing the unit from a number is as simple as dividing it by 1 member of its unit:

$number-with-unit: 42em;
$number: $number-with-unit / 1em; // {number} 42

// Don't do that
$number: str-slice($number-with-unit + '', 3); // {string} 42

Number Precision

All current Sass engines use a default precision of 5, meaning they round numbers at 5 digits. While there are some tricks to change this value, most of them are inconsistent and likely to cause extra issues at some point.

To fix a value to a given amount of digits, I set up a little function (basically an equivalent of the toFixed function from JavaScript. Although it cannot fix it any higher than 5 since it is the current limit enforced by the engine.

/// toFixed() function in Sass
///
/// @author Hugo Giraudel
///
/// @param {Number} $float - Number to format
/// @param {Number} $digits [2] - Number of digits to leave
///
/// @return {Number}
@function to-fixed($float, $digits: 2) {
  $sass-precision: 5;

  @if $digits > $sass-precision {
    @warn "Sass sets default precision to #{$sass-precision} digits, and there is no way to change that for now."
    + "The returned number will have #{$sass-precision} digits, even if you asked for `#{$digits}`."
    + "See https://github.com/sass/sass/issues/1122 for further informations.";
  }

  $pow: pow(10, $digits);
  @return round($float * $pow) / $pow;
}

Clamping a number

The word clamp, at least as far as computer science goes, means restricting a number between two other numbers.

When clamped, a number will either keep its own value if living in the range imposed by the two other values, take the lower value if initially lower than it, or the higher one if initially higher than it.

Sass does not provide any native way to clamp a number but fortunately this is very easy to write:

/// Clamp `$number` between `$min` and `$max`
///
/// @author Sam Richards
///
/// @param {Number} $number - Number to clamp
/// @param {Number} $min - Minimum value
/// @param {Number} $max - Maximum value
///
/// @return {Number}
@function clamp($number, $min, $max) {
  @return min(max($number, $min), $max);
}

While anecdotal, clamping might come in handy for some things, such as making sure the parameter of an opacity mixin lives between 0 and 1.

/// Opacity mixin
/// @param {Float} $value - Opacity value
/// @output `opacity`
@mixin opacity($value) {
  $clamped-value: clamp($value, 0, 1);

  @if $value != $clamped-value {
    @warn "Mixin `opacity` needs a float; #{$value} given, clamped to #{$clamped-value}.";
  }

  opacity: $clamped-value;
}

Engine compatibility

Numbers are fully compatible across all Sass engines and there is no known bug to this day about their implementation.