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
Bootstrap 3 and .Col-Xs-* - Do You Not Need Rows of 12 Units
How to Get Bootstrap Tour to Work with a Jquery Dialog
Has Anyone Created a 3D Website That Works on a 3D Monitor
Broke Page Styles of Vue.Js App (Webpack Template) When Live Changing It in Chrome Devtools
Angular 6 Load CSS Folders in Angular.JSON
Flexbox Container in Chrome Doesn't Get 100% Height
Nuxt & Vuetify: How to Control The Order in Which CSS Files Are Loaded
Put Title/Alt Attributes into CSS: After { Content: Image }
Using Flexbox Sticky Footer with Bootstrap
Svg Letter-Spacing Also Applied to Mozilla Firefox
Child Take Width % from Parents Parent
How to Highlight a Selected Row in *Ngfor
Show Truncated Text Normally, But Show Full Text on Hover
Make Element Width Stretch to Fit Its Children When Element's Parent Has Overflow: Auto;
CSS Animate a Div with Absolute Positioning from Left 0 to Right 0