30

I like to use rem units with pixel fallbacks for my CSS sizing and am trying to make mixins to help with that. For font-size, this is easy:

@mixin font-size($size) {
  font-size: $size + px;
  font-size: ($size / 10) + rem;
}

But for padding, margin, etc. the mixin needs to accept variable arguments, which is possible per the Sass documentation http://sass-lang.com/documentation/file.SASS_REFERENCE.html#variable_arguments

However, with the following mixin, instead of dividing by 10, the mixin is just adding a slash between the numbers. That is, this:

@mixin padding($padding...) {
    padding: $padding + px;
    padding: ($padding / 10) + rem;
}
.class {
    @include padding(24);
}

Outputs this:

.class {
    padding: 24px;
    padding: 24/10rem;
}

Instead of this, like I would expect:

.class {
    padding: 24px;
    padding: 2.4rem;
}

Is there a way to make sure Sass recognizes the variables as numbers and thus uses the division operator on them correctly?

Also, after testing this more, I realized the concatenation only takes place on the last variable.

Doug Hamlin
  • 996
  • 1
  • 11
  • 17
  • possible duplicate of [How to do math in a mixin with SASS?](http://stackoverflow.com/questions/18499633/how-to-do-math-in-a-mixin-with-sass) – cimmanon Oct 17 '13 at 03:50

3 Answers3

30

Try this:

padding: #{$padding / 10}rem;

Concatenating in SASS/SCSS uses ruby syntax, and you were mixing a mathematical equation followed by a concatenation, which is a variable type mix, so it's not suprising to me that it didn't work.

Expressions and variables included within #{here} are assesed as if seperate to the rest of the line, and thus don't typecast the rest of the line. Also comes in handy if your output is getting quoted when you didn't expect (as does the unquote() function)

Owen C. Jones
  • 1,435
  • 1
  • 11
  • 13
21

It seems what I really needed to use here was a list rather than a variable argument in order to manipulate each value separately.

I first tried doing this with the @each directive, but couldn't figure out how to use it inside a declaration. This throws an error:

@mixin padding($padding) {
   padding: @each $value in $padding { $value + px };
   padding: @each $value in $padding { ($value / 10) + rem };
}

So I ended up writing something much more verbose that handles each of the four possible cases separately (i.e. you pass 1, 2, 3 or 4 arguments). That looks like this and works as I wanted:

@mixin padding($padding) {
    @if length($padding) == 1 {
        padding: $padding+px;
        padding: $padding/10+rem;
    }
    @if length($padding) == 2 {
        padding: nth($padding, 1)+px nth($padding, 2)+px;
        padding: nth($padding, 1)*0.1+rem nth($padding, 2)*0.1+rem;
    }
    @if length($padding) == 3 {
        padding: nth($padding, 1)+px nth($padding, 2)+px nth($padding, 3)+px;
        padding: nth($padding, 1)*0.1+rem nth($padding, 2)*0.1+rem nth($padding, 3)*0.1+rem;
    }
    @if length($padding) == 4 {
        padding: nth($padding, 1)+px nth($padding, 2)+px nth($padding, 3)+px nth($padding, 4)+px;
        padding: nth($padding, 1)*0.1+rem nth($padding, 2)*0.1+rem nth($padding, 3)*0.1+rem nth($padding, 4)*0.1+rem;
    }
}

I made collection of rem mixins including this one as a Gist here https://gist.github.com/doughamlin/7103259

Doug Hamlin
  • 996
  • 1
  • 11
  • 17
0

One thing that might help is using calc along with interpolation (aka #{}). For example.

$height: 10px;
$padding: 2px;

height: calc(#{$height} + #{$padding * 2}); // calc(10px + 4px)

This is NOT the same as...

height: calc($height + #{$padding * 2});
// resolves to...
height: calc($height + 4px); // ERROR

height: #{$height + $padding * 2};
// resolves to...
height: 10px + 4px; // ERROR
Nickofthyme
  • 3,032
  • 23
  • 40