SASS mixin error because of Deprecation - sass

I am using the following mixin for REM conversions
https://gist.github.com/bitmanic/1134548
Since the latest SASS update the mixin is now showing an error:
// If the value is zero or a string or a color, return unchanged input
#if $value == 0 or type-of($value) == "string" or type-of($value) == "color" {
$rem-values: append($rem-values, $value); }
#else {
$rem-values: append($rem-values, $value / $baseline-rem); } }
The result of 0px == 0 will be false in future releases of Sass. Unitless numbers will no longer be equal to the same numbers with units.
I am a bit new to SASS, can someone help trouble shoot this?

Are you sure it's an actual error rather than a warning?
The warning is likely in reference to this issue. The jist of the problem being fixed here is where 1px == 1, which is inherently untrue.
Realistically, you should always use 0 for lengths rather than 0px, despite the fact that they are equal, simply because you would be saving a few bytes. You should be able to strip off the unit and then perform the comparison:
$baseline_px: 10px;
#mixin rem($property, $px_values) {
// Convert the baseline into rems
$baseline_rem: ($baseline_px / 1rem);
// Print the first line in pixel values
#{$property}: $px_values;
// Create an empty list that we can dump values into
$rem_values: ();
#each $value in $px_values {
// If the value is zero, return 0
#if $value / ($value * 0 + 1) {
$rem_values: append($rem_values, $value);
zero: $value;
}
// If the value is not zero, convert it from px to rem
#else {
$rem_values: append($rem_values, ($value / $baseline_rem) );
not-zero: $value
}
}
// Return the property and its list of converted values
#{$property}: $rem_values;
}
.foo {
#include rem(font-size, 10px);
#include rem(border-width, 0px);
#include rem(border-width, 0);
}
Alternately, you could check if it is within a list of zero values:
#if index((0, 0px), $value) {
// do 0 related stuff
}

Related

Output lighter and darker colour variants

I want to output some utility classes and I can't seem to get the lighter and darker tones of my base color to output both darker and lighter tones. I am new to Scss but this is what I have:
$aqua: #00ffff;
$color-map: (
background-color-aqua-light: ($aqua, background-color, lighten,),
color-aqua-light: ($aqua, color, lighten),
background-color-aqua-dark: ($aqua, background-color, darken),
color-aqua-dark: ($aqua, color, darken)
);
#each $color-class, $colour-variables in $color-map {
$class-name: nth($color-class, 1);
$color-name: nth($colour-variables, 1);
$color-type: nth($colour-variables, 2);
$color-brightness: nth($colour-variables, 3);
#for $i from 20 through 100{
#if $i % 10 == 0{
$percentage: $i*0.5%;
.#{$class-name}-#{$i}{
#{$color-type}: #{$color-brightness}($color-name, $percentage);
}
}
}
}
It looks like you're generating the method name in the CSS:
So this SCSS
#{$color-type}: #{$color-brightness}($color-name, $percentage);
Becomes this CSS
.background-color-aqua-light-40 {
background-color: lighten(#00ffff, 20%);
}
To my knowledge, you can't interpolate a SASS method name and get SASS to interpret it. But. I think you can (maybe less elegantly) get around that limitation with the #if and #elseif rules in your #for loop.
#for $i from 20 through 100 {
#if $i % 10 == 0 {
$percentage: $i*0.5%;
.#{$class-name}-#{$i} {
#if ( $color-brightness == lighten ) {
#{$color-type}: lighten($color-name, $percentage);
} #elseif ( $color-brightness == darken ) {
#{$color-type}: darken($color-name, $percentage);
}
}
}
}
EDIT: FWIW, I tested the #if/#elseif solution on sassmeister.com and it seems to crank out the CSS you are after.

Error handling in SASS

I'd like to handle some errors in my scss code.
Imagine this code.
$color: 12;
a {
#if (type-of($color) != color) {
// trow an error
}
}
Now I use a mixin, that takes some params and calls #error or #warn.
#mixin log($type, $message) {
#if ($type == error) {
#error $message;
} #else {
//
}
}
But I don't want to call it every time via #include: #include log(error, "message");.
I'd wont something like this:
a {
#if (type-of($color) != color) {
log(error, "message");
}
}
So, is there a way to write a function (or not a function) to call it inside a selector?
Ideally mixins should be used to create property and value pairs. Functions are expected to return values so can be used any where you Sass / CSS expects a value
#function log($type, $message) {
#if ($type == error) {
#return $message;
} #else {
//return something else
}
}
a {
#if (type-of($color) != color) {
#error log(error, 'message');
// $type == error so log(error, 'message') returns 'message'
// so entire line is interpreted as #error 'message'
}
}
In this example the only difference was using #error for the function instead of #include for the mixin.
However imagine if you needed to change a colour based on a certain value like a width and log some errors at the same time.
#function get-colour($width) {
$color: green;
#if ($width < 10) {
#warn 'This size is too small';
$color: red;
}
#return $color;
}
div {
background-color: get-colour(12); //returns green colour for div
}
p {
background-color: get-colour(5); //logs warning and returns red colour for p
}
While a mixin would return property: value, a function returns ONLY a value which can be used on different properties.
I googled a bit. There is no way to handle errors without #mixins. #include log(error, "message"); is the only solution.

SASS & list iteration / delimiter

I've got a pattern where I create a list of lists to iterate over, as an basic example:
$carouselContent : "carousel-content-1" "buying_carousel_image_1.jpg",
"carousel-content-2" "buying_carousel_image_2.jpg";
My iteration (inside of a mixin) then looks like:
#each $carousel in $carouselContent {
$baseClass: nth($carousel, 1);
$image: nth($carousel, 2);
.#{$baseClass} {
....
}
}
I just came across a page that presently only has 1 item in the carousel. I'd like to keep with the pattern, but I'm not sure how to do so. If I iterate over:
$carouselContent : "carousel-content-1" "growing_carousel_image_1.jpg";
SASS treats that as a 2 item list. I could work around that by adding an empty item to my list, then adding a check against empty string, e.g.
$carouselContent : "carousel-content-1" "growing_carousel_image_1.jpg","" "";
But that seems hacky... so I figured there has to be a way to do this that I'm unaware of.
In Sass 3.3.0, all you need to do is have a trailing comma to signify that what you have is a list with one item in it:
$carouselContent : "carousel-content-1" "buying_carousel_image_1.jpg", ;
#each $carousel in $carouselContent {
$baseClass: nth($carousel, 1);
$image: nth($carousel, 2);
.#{$baseClass} {
color: red;
}
}
Generates:
.carousel-content-1 {
color: red;
}
Sass 3.3.0 is still undergoing development, but you can play with it now by upgrading to the latest edge version via gem install sass --pre. However, if you're willing to upgrade to 3.3, you may want to look at mappings instead (see: the change log)
You can use an #if directive to check if the first element of your list is also a list with type-of() (and only then use the loop). Something along these lines (I separated the block from inside your loop as a mixin):
#mixin do_car($carousel) {
$baseClass: nth($carousel, 1);
$image: nth($carousel, 2);
.#{$baseClass} {
/* ... */
}
}
#if (type-of(nth($carouselContent,1)) == list) {
#each $carousel in $carouselContent {
#include do_car($carousel);
}
} #else {
#include do_car($carouselContent);
}
DEMO
If your items are numbered sequentially, you can use a for loop instead:
$carouselImages: 2;
#for $i from 1 through $carouselImages {
.#{carousel-content-#{$i}} {
background: url(buying_carousel_image_#{$i}.jpg);
}
}
Output:
.carousel-content-1 {
background: url(buying_carousel_image_1.jpg);
}
.carousel-content-2 {
background: url(buying_carousel_image_2.jpg);
}
Alternately:
//$carouselContent : "buying_carousel_image_1.jpg", "buying_carousel_image_2.jpg";
$carouselContent : "buying_carousel_image_1.jpg";
#for $i from 1 through length($carouselContent) {
.#{carousel-content-#{$i}} {
background: url(nth($carouselContent, $i));
}
}

SASS : compare variable name within #each loop with a string

I'm trying to find a way of comparing the variable name e.g. $topLeft within the #each loop with a string which would be for instance 'topLeft' - an example would be:
#mixin getCorner($topLeft:false, $topRight:false, $bottomRight:false, $bottomLeft:false) {
#each $corner in $topLeft, $topRight, $bottomRight, $bottomLeft {
#if #{$corner} == topLeft {
border-top-left-radius: $corner;
}
}
}
The above obviously doesn't work, but is there a way of doing it in Sass?
If you use the name top-left instead of topLeft, you can reduce the amount of code you have to write.
Here I have a list which does not do EXACTLY what you want, but you can easily use this to go ahead and do the comparison you want to do.
$corners: (top-left, top-right, bottom-left, bottom-right);
#mixin getCorner($cornerName, $cornerVal) {
$max: length($corners);
#for $i from 1 through $max {
$temp: nth($corners, $i);
#if ($temp == $cornerName) {
border-#{$temp}-radius: $cornerVal;
}
}
}
body {
#include getCorner(top-left, 2px);
}
When you assign a variable, all the interpreter knows is the value it contains, not what its name is. So when you're looping over your values, $corner is getting set to one of the values in the list. It will never be topLeft unless you pass that as the value for the $topLeft argument, which is why your #if statement never evaluates to true.
If you use a default value of null instead of false, you can simplify a lot:
#mixin getCorner($topLeft: null, $topRight: null, $bottomRight: null, $bottomLeft: null) {
border-top-left-radius: $topLeft;
border-top-right-radius: $topRight;
border-bottom-right-radius: $bottomRight;
border-bottom-left-radius: $bottomLeft;
}
.foo {
#include getCorner($topLeft: 50%, $bottomRight: 50%);
}
Output:
.foo {
border-top-left-radius: 50%;
border-bottom-right-radius: 50%;
}

How to overwrite linear-gradient function in Compass?

I'd like to use the code here to overwrite the linear-gradient function that comes with Compass. How can I do this?
I think what I need is a way to import the linear-gradient function and then locally rename it to something else, so that my own linear-gradient function can call it. E.g. something like:
#import "compass/css3/images";
// somehow make `original-lg` alias the current `linear-gradient`
#function linear-gradient($args...) {
#return original-lg($args...) + ", " + fixed-lg-from-link-above($args...);
}
What you are attempting won't work, because we are dealing with prefixed values that have to be split apart into distinct properties. As the author of the linked code, here is how I recommend using it. You'll need these functions:
#function convert-gradient-angle(
$deg
) {
#if type-of($deg) == 'number' {
#return mod(abs($deg - 450), 360deg);
} #else {
$direction: compact();
#if nth($deg,1) == 'to' {
#if length($deg) < 2 {
$direction: top;
#warn "no direction given for 'to'. Using 'to bottom' as default.";
} #else { $direction: opposite-position(nth($deg,2)); }
#if length($deg) > 2 { $direction: append($direction, opposite-position(nth($deg,3)), space);}
} #else {
$direction: append($direction, to, space);
#each $pos in $deg { $direction: append($direction, opposite-position($pos), space); }
}
#return $direction;
}
}
#function convert-gradient(
$angle,
$details...
) {
#return linear-gradient(convert-gradient-angle($angle), $details...);
}
The problem is, if you use multiple-backgrounds or anything like that, you will have to repeat the functions yourself in different properties. If you just want a simple background-image gradient, you can use this to simplify:
#mixin gradient-background-image(
$gradient...
) {
#include background-image(convert-gradient($gradient...));
background-image: linear-gradient($gradient...);
}
Otherwise you will need to write those two lines by hand, adding the other layers as needed.

Resources