Error handling in SASS - 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.

Related

global var update from scss function

I have a scss function
#function darken($color,$percent) {
...
#return $calculated-color;
}
This function overrides the darken() function, but adds some extra features.
Now, I wonder if it is possible to somehow store all calls to this function in some map and then after all function calls has been made run that map trough a mixin such as:
$calc-colors:() !global;
$calc-colors:map-merge(('thisvaluewillbeexported':1),$calc-colors);
#function test($color,$percent) {
$col: darken($color,$percent);
// $calc-colors: append($calc-colors,'--'$color); --not working
// $calc-colors:map-merge(('--'+$color:$col),$calc-colors); --not working
#return $col;
}
.test {
color:test(pink,24%);
}
.test2 {
color:test(red,24%);
}
:export{
#each $bp, $value in $calc-colors {
#{$bp}: #{$value};
}
}
//gives only thisvaluewillbeexported:1
My goal would to somehow get all calls to my test function recorded into the :export{} attribute in order to be able to fetch the values from javascript.
// My preferred output would be:
{
'thisvaluewillbeexported':1,
'--pink':'#ff4666',
'--red':'#850000'
}
You should set variable !global inside function.
Sassmeister demo.
Article about variable scope in Sass.
#function set-color($color-name, $darken-ration) {
$darken-color: darken($color-name, $darken-ration);
$calc-colors: map-merge($calc-colors, ('--' + $color-name: $darken-color)) !global;
#return $darken-color;
}
$calc-colors: map-merge(('thisvaluewillbeexported': 1), ());
a {
color: set-color(green, 10%);
}
b {
color: set-color(red, 10%);
}
c {
#each $name, $value in $calc-colors {
#{$name}: #{$value};
}
}
Css output:
a {
color: #004d00;
}
b {
color: #cc0000;
}
c {
thisvaluewillbeexported: 1;
--green: #004d00;
--red: #cc0000;
}

How to conditionally override a sass variable?

I'm working on a RTL sass solution, trying keep RTL changes next to their LTR counterparts (rather than a separate stylesheet). I have some functions that depend on a $dir variable. So if it is set to 'ltr' the LTR rules are used. If set to 'rtl', the RTL rules are used.
So my code is:
$dir: ltr;
#function if-ltr($if, $else: null) {
#if $dir != rtl {
#return $if;
}
#else {
#return $else;
}
}
#function if-rtl($if, $else:null) {
#return (if-ltr($else, $if);
}
#mixin if-ltr {
#if $dir != rtl {
#content;
}
}
#mixin if-rtl {
#if $dir == rtl {
#content;
}
}
html[dir='rtl'] {
$dir: rtl;
}
p {
color: black;
}
#include if-rtl {
p {
color: red;
}
}
Logically I would think that in the case of the html tag's dir attribute being set to 'rtl', the variable $dir would be set to 'rtl' but this does not happen. I can override this by setting $dir: rtl !global but this changes it for the default ltr case as well.
So it seems that a variable in sass can only be set to one value? (It would not negate the 'variable' moniker as it would still be useful for changing the same value in multiple places at once.) Or is there a way around this? Am I missing something?

SASS mixin error because of Deprecation

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
}

Creating a sass map value from within a mixin - saved globally [duplicate]

This question already has answers here:
How to assign to a global variable in Sass?
(2 answers)
Closed 6 years ago.
Is it possible to update a value in a sass map from within a mixin so that the change is saved globally?
Eg
$obj: (
init: false
)
#mixin set($map) {
#if map-get($obj, init) != true {
// mixin hasn't been called before
$map: map-set($map, init, true);
}
#else {
// mixin has been called before
}
}
.test {
#include set($obj);
// sets the init value to true
}
.test-2 {
#include set($obj);
// init value has already been set to true
}
I'm not sure if I understood what you are trying to do, but your code seems to be fine (haven't tested it though), excepting that there is no map-set function, but you can create one or just use map-merge (check here: http://oddbird.net/2013/10/19/map-merge/). I hope that helps.
#update 1: I think I got your question now, you want to pass the reference through the mixin, so if you have multiple maps, you can send the one you want to update to the mixin, I don't think this is possible though, because no reference is kept, if you need to update the variable you have to link directly to it, for exemple, this works (tested):
$obj: (
init: false
);
#mixin set($map) {
#if map_get($map, init) != true {
$obj: map-set($map, init, true) !global;
body {
background-color: #000;
}
} #else {
body {
background-color: #ff0000;
}
}
}
#include set($obj);
#include set($obj);
But if you reference to $map instead of $obj (in this line $obj: map-set($map, init, true) !global;), then a new global map (called $map), will be created. And every time you call the mixin again, it will be replaced by the map you sent as a parameter.
#update 2: I found a way to do it, but you have to keep a global 'map of maps', and every time you update this guy, you send the name of the map you want to update as parameter, so I came up with the following code, it's tested and working fine :)
#function map-set($map, $key, $value) {
$new: ($key: $value);
#return map-merge($map, $new);
}
$maps: (
obj1: (
init: false
),
obj2: (
init: false
),
);
#mixin set($prop) {
#if map_get(map_get($maps, $prop), init) != true {
$obj: map-set(map_get($maps, $prop), init, true);
$maps: map-set($maps, $prop, $obj) !global;
body {
background-color: #000;
}
} #else {
body {
background-color: #ff0000;
}
}
}
#include set(obj1); //black
#include set(obj2); //black
#include set(obj1); //red
#include set(obj2); //red
source: myself
Following on from #Paulo Munoz
Here is the solution
#function map-set($map, $key, $value) {
$new: ($key: $value);
#return map-merge($map, $new);
}
$extend : ();
$obj : (
margin: 0,
padding: 10
);
#mixin set($map, $name) {
#if map-has-key($extend, $name) {
map: has-key;
// call placeholder class
} #else {
$extend: map-set($extend, $name, true) !global;
map: does-not-have-key;
// create placeholder class
// call placeholder class
}
}
.test {
#include set($obj, test);
}
.test-2 {
#include set($obj, test);
}
which generates
.test {
map: does-not-have-key;
}
.test-2 {
map: has-key;
}

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