Append unit type to the result of a calculation in Sass

CssSass

Css Problem Overview


I've been refactoring my CSS to a SASS style sheet recently. I'm using the Mindscape Web Workbench extension for VS2012, which re-generates the CSS each time you save your SCSS. I started with code similar to this:

/* Starting point: */
h1 { font-size: 1.5em; /* 24px ÷ 16px */ }

Then I tried to refactor it first to this:

/* Recfator: */
h1 { font-size: (24px / 16px)em; }

But this unfortunately produces:

/* Result: */
h1 { font-size: 1.5 em; }              /* doesn't work, gives "1.5 em" */

Notice the extra space, which I don't want there. I've tried several alternatives, here are a few:

h1 { font-size: (24/16)em; }           /* doesn't work, gives "1.5 em" */
h2 { font-size: 24 / 16em; }           /* doesn't work, gives "24/16em" */
h3 { font-size: (24px / 16px) * 1em; } /* works but "* 1 em" feels unnecessary */
h4 { font-size: (24em) / 16; }         /* works, but without "px" it's not 
                                          really conveying what I mean to say */

I've also tried these variants with variables (because I want those anyways), but that didn't change the situation much. To keep the examples in this question sleek I've left out variables. However, I'd happily accept a solution that relies on using variables (in a clean way).

I've gone through the relevant SASS documenation on '/', and appreciate that this is a tough one for SASS because the '/' character already has a meaning in basic CSS. Either way, I was hoping for a clean solution. Am I missing something here?

PS. This blogpost does offer one solution, using a user defined function. That seems a bit heavy-weight though, so I'm interested if there's "cleaner" solutions in line with my attempts above. If someone can explain the "function approach" is the better (or even only) solution then I'll accept that as an answer too.

PS. This related question seems to be about the same thing, though that one specically wants to do further calculations. The accepted answer there is my third workaround (multiplying by 1em), but I'd love to know if there's a different (cleaner) way if I'm willing to forego the ability to do further calculations. Perhaps the method mentioned in said question ("interpolation") is useful for me?


Bottom line: how can you cleanly append the unit type (e.g. em) to the result of a calculation in SASS?

Css Solutions


Solution 1 - Css

The only way to add a unit to a number is via arithmetic.

To perform operations like concatenation (eg. 1 + px) or interpolation (eg. #{1}px) will only create a string that looks like a number. Even if you're absolutely 100% certain that you're never going to use your value in another arithmetic operation, you should not do this.

More important than not being able to perform arithmetic operations, you won't be able to use them with other functions that expects a number:

$foo: 1; // a number
$foo-percent: $foo + '%'; // a string

.bar {
	color: darken(blue, $foo-percent); //Error: "1%" is not a number!
}

> $amount: "1%" is not a number for `darken'

There is nothing to be gained by casting your numbers to strings. Always use arithmetic (multiplication by 1, or addition by 0) to add a unit:

$foo: 1; // a number
$foo-percent: $foo * 1%; // still a number! //or: $foo + 0%

.bar {
	color: darken(blue, $foo-percent); //works!
}

Output:

.bar {
  color: #0000fa;
}

Here's a mixin I wrote as part of my Flexbox mixin library that will choke if you pass in a string (for those not familiar with Flexbox, the original specification only allows integers for the box-flex property. flex: auto or flex: 30em cannot be made compatible with the comparable box-flex property, so the mixin doesn't bother trying)

@mixin flex($value: 0 1 auto, $wrap: $flex-wrap-required, $legacy: $flex-legacy-enabled) {
	@if $legacy and unitless(nth($value, 1)) {
		@include legacy-flex(nth($value, 1));
	}
	
	@include experimental(flex, $value, flex-support-common()...);
}

@mixin legacy-flex($value: 0) {
	@include experimental(box-flex, $value, $box-support...);
}

Solution 2 - Css

You can try either of these:

font-size: $size * 1px;

or

font-size: $size + unquote("px");

Where $size is the result of your calculation.

Solution 3 - Css

Choose the method you prefer

$font: 24px;
$base: 16px;

No variables

.item1 { font-size: #{(24px / 16px)}em; }

Using only variables

.item2 { font-size: #{$font / $base}em; }

One variable

.item3 { font-size: #{$font / 16px}em; }
.item4 { font-size: #{24px / $base}em; }

Output for all of them

.item1 { font-size: 1.5em; }
.item2 { font-size: 1.5em; }
.item3 { font-size: 1.5em; }
.item4 { font-size: 1.5em; }

The method used is called interpolation #{}

Solution 4 - Css

I assume the reason you're asking this is because, in the surrounding context, 1 em = 16 px, and you want to use this relationship to convert the target font size from pixels to ems.

If so, the right thing to do is to multiply the font size with the scaling factor 1em / 16px, like this:

$h1-font-size: 24px;
$body-font-size: 16px;

$body-em-scale: 1em / $body-font-size;

h1 {
  font-size: $h1-font-size * $body-em-scale;  // -> 1.5em
}

or just:

h1 {
  font-size: $h1-font-size * (1em / $body-font-size);  // -> 1.5em
}

This is exactly how it works in physics, too: if have, say, 50 moles of water, and you want to know how much that weighs in grams, you multiply the amount of water you have (= 50 mol) with the molar mass of water (≈ 18 g/mol) to find out that your sample of water weighs 50 mol × 18 g/mol ≈ 900 grams. Converting pixels to ems in SASS works just the same way: first find out how many ems there are per pixel, in the context you intend to use the rule in, and then multiply the size in px with that ratio, expressed in units of em/px.


Ps. Of course, if the units you were converting between had a fixed ratio that SASS already knew about, then you could simply let it do the conversion automatically for you, using the "zero plus trick". For example, if you wanted to convert the font size from px to cm, you could simply do:

h1 {
  font-size: 0cm + $h1-font-size;  // -> 0.635cm
}

This works because SASS allows you to add together two values given in compatible units (like px and cm), and will express the result in the same units as the first value in the sum. The reason this trick doesn't work for px -> em is that here the conversion factor is not fixed, but depends on the surrounding context, and so SASS doesn't know how to do the conversion automatically.

Solution 5 - Css

The best way to add a unit, is to use ... * 1em or ... + 0em where em is the unit you want to get:

font-size: (24px/16px) + 0em; // ... + 0px for px
//or
font-size: (24px/16px) * 1em; 
//both produce: font-size: 1.5em;

This way, it will remain as a number so you can use it in math operations or other functions.

But if you want the result as a string or don't care the numeric value of result for some reason, you can also simply get it this way:

font-size: (24px/16px) + em; //font-size: 1.5em; //em does not include quotations

no need to use unquote or #{} here (as written in other answers)!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJeroenView Question on Stackoverflow
Solution 1 - CsscimmanonView Answer on Stackoverflow
Solution 2 - CssNaoise GoldenView Answer on Stackoverflow
Solution 3 - Csselectric_gView Answer on Stackoverflow
Solution 4 - CssIlmari KaronenView Answer on Stackoverflow
Solution 5 - CssS.SerpooshanView Answer on Stackoverflow