Doing Math on Variable Argument SASS Mixins

Doing math on variable argument Sass mixins

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

How to define Sass mixins with variable arguments

You can set your second parameter to be false as the default value, and do a @if statement inside your mixin.

@mixin myMixin( $param1, $param2:false ){
@if $param2 == false{
// Do something
}
@else{
// Do something with both parameters
}
}

Edit: this a custom function I made to transform px unit into em unit. You can pass a list or a single value as parameter. You can also set a base and a line-height. Maybe you can find few tricks inside this function.

You can use it like this: emify(10px 16px, 16px).

This will output: 0.625em 1em

/**
*
* Emify
* Transform px unit into em
* defaults:
* base: 16
* usage:
* emify(unit)
* emify(unit, base)
* emify(unit, line-height, base)
* examples:
* emify(16px) = 1em
* emify(16px, 10px) = 1.600em
* emify(16px, 10px, 10px) = 1.600em / 1em
*
**/
@function emify( $target, $lineheight: null, $base: null ) {

@if $base == null and $lineheight == null { $base: 16 }
@if $base == null and $lineheight != null { $base: $lineheight }

[...]

}

SASS mixin not taking variables

To call a mixin you use the @include directive, so instead of doing:

@mixin define-square(45px);
@mixin define-square(45);
@mixin define-square("45px");
@mixin define-square("45");

You should be doing:

@include define-square(45px);
@include define-square(45);
@include define-square("45px");
@include define-square("45");

See http://sass-lang.com/documentation/file.SASS_REFERENCE.html#including_a_mixin


Re: the error you're getting:

The SASS parser sees @mixin define-square( and assumes you're creating a new mixin, so it correctly expects an argument list, i.e. ($foo, $bar), but instead it gets "45px"... -- and so ends up giving you that error.

Convert Scss to Sass mixin

You can use sass-convert to convert your file from scss to sass

your code in sass is

@function calculateRem($size)
$remSize: $size / 16px

@return $remSize * 1rem

=font-size($size)
font-size: $size
font-size: calculateRem($size)

Reference
http://sass-lang.com/documentation/file.SASS_REFERENCE.html#syntax

SASS Functions with Variables

You can use a @for loop that generates a set of classes:

$class-slug: pad-top-b;
$base-spacing-unit: 10;
$classes-amount: 10;

@for $i from 1 through $classes-amount {
.#{$class-slug}#{$i} {
padding-top: ($base-spacing-unit * $i);
}
}

Demo on SassMeister.

Sass @each pass variable to mixin

Modified example from the docs to suit your case. And I used a @for loop in place of the @each, although you could do it with the @each as well. The Problem is that you pass the variable as a string, resulting in math operations on a string, you have to remove the #{} from the variable.

$class-slug: span !default;

@for $i from 1 through 12 {
.#{$class-slug}#{$i} {
@include span-column($i);
}
}

EDIT:
Play with this gist on SassMeister.

Append unit type to the result of a calculation in Sass

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...);
}

How to pass a mixin as a parameter of another mixin in SASS

You can't add two mixins together the way you'd like. So you just have to make the rem mixin do what you want it to do. So I wrote new code to handle that for you.

@function parseInt($n) {
@return $n / ($n * 0 + 1);
}

@mixin rem($property, $values, $prefix: false) {
$px: ();
$rem: ();

@each $value in $values {

@if $value == 0 or $value == auto or unit($value) == "%" {
$px: append($px, $value);
$rem: append($rem, $value);
} @else {
$unit: unit($value);
$val: parseInt($value);

@if $unit == "px" {
$px: append($px, $value);
$rem: append($rem, ($val / 16 + rem));
}

@if $unit == "rem" {
$px: append($px, ($val * 16 + px));
$rem: append($rem, $value);
}
}
}

@if $px == $rem {
#{$property}: $px;
} @else if $prefix == true {
#{-moz- + $property}: $px;
#{-moz- +$property}: $rem;
#{-webkit- +$property}: $px;
#{-webkit- +$property}: $rem;
#{$property}: $px;
#{$property}: $rem;
} @else {
#{$property}: $px;
#{$property}: $rem;
}
}

Now all you have to do add prefixes to any property is add the value true to the end of the mixin like so...

@include rem(border-radius, 10px, true);

Otherwise if you don't want any prefixs on property like fon-size or something you just don't add a last value like so...

@include rem(font-size, 10px);

I have a working demo here...

*Also on a side note I modified this mixin to handle percentages too.



Related Topics



Leave a reply



Submit