SCSS - How to avoid extra space when intrapolating variable in content propery - sass

I have a simple mixin like this:
#mixin icon_fn($glyph) {
&::before {content: "\e#{$glyph}";}
}
so that I can use it as simply as #include #icon_fn("9fa") but for some reason there's always a space between \e and the variable. Can't figure out a way to get rid of it..
-- After using function from the other thread
#function str-remove-whitespace($str) {
#while (str-index($str, ' ') != null) {
$index: str-index($str, ' ');
$str: "#{str-slice($str, 0, $index - 1)}#{str-slice($str, $index + 1)}";
}
#return $str;
}
#mixin icon_fn($glyph) {
$theglyph: "\e#{$glyph}";
&::before {content: str-remove-whitespace($theglyph);}
}
The white space still persists

I guess I solved your problem. I already quite the same question some time back and thanks to Amaury Hanser for the original answer.
Original Question and Solution - https://stackoverflow.com/a/69014465/15748278
See how I used it in your case scenario.
#function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);
#if $index {
#return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}
#return $string;
}
The above function searches and replaces the string with another. So in your case what can we do is first we can search for and replace it with empty ''.
#mixin icon($glyph) {
&::before {
content: #{str-replace('"\\e#{$glyph}"', ' ', '')};
}
}
// and then you can use it like this
div {
#include icon(958);
}
And the output is as you wanted.

Related

Using sass #function inside #for

I have some sass function to create same margin, and it is inside loop like this
$short-margins: ( top: 'mt', left: 'ml', bottom: 'mb', right: 'mr' );
#for $i from 0 through 200 {
#each $position, $prefix in $short-margins{
.#{$prefix}-#{$i} {
margin-#{$position}: #{$i}px;
}
}
}
This will create margin classes like this mr-0 and so on until mr-200, the problem is in line
margin-#{$position}: #{$i}px;
There I create px in loop, but i need that to be in rem? I ahve some function like this
$base-font-size: 14px;
// Remove units from the given value.
#function strip-unit($number) {
#if type-of($number) == 'number' and not unitless($number) {
#return $number / ($number * 0 + 1);
}
#return $number;
}
// convert only single px value to rem
#function single-px-to-rem($value) {
$unitless-root-font-size: strip-unit($base-font-size);
#return $value / $unitless-root-font-size * 1rem;
}
But when i want to use function inside loop like this
#for $i from 0 through 200 {
#each $position, $prefix in $short-margins{
.#{$prefix}-#{$i} {
margin-#{$position}: single-px-to-rem(#{$i});
}
}
}
It does not compile throw me error, does any body knows how ti use sass #function inside #for?
You are doing it right, but you need to send the $i value without interpolation:
#for $i from 0 through 200 {
#each $position, $prefix in $short-margins{
.#{$prefix}-#{$i} {
margin-#{$position}: single-px-to-rem($i);
}
}
}

Get a variable name as a string

I have a class with the name .text-black, I also have a map with some values.
$black: #000;
map:
dark: $black;
I want to loop through this map and create a new class with the $key and then extend they new class with using the value text-black.
There are 2 problems I have. The first I think I have solved, if I can get the $value as $black instead of #000. Then I can remove the $ using string replacement.
The second challenge however is proving a headache for me. I need to get $black in stead of #000.
Here is my code showing my approach so far.
// String Replacement to remove '$' from varaible name.
#function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);
#if $index {
#return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace)
}
#return $string;
}
// get color from map
#function text-color($key: "weekly-digest") {
#return map-get($text-colors, $key);
}
$black: #000000;
// map text-colors
$text-colors: () !default;
$text-colors: map-merge(
(
"news": $black,
),
$text-colors
);
// Extendable classes.
.text-black{
color: $black;
}
// Loop function
#each $name, $value in $text-colors {
&--#{$name} {
background-color: $value;
#extend .text-#{$value} // This should return '.text-black' not '.text-#000000'
}
}
I try to give you 3 different solutions. In all these solutions I used 2 colors (black & red) only to see if they could work in combination:
1. Using a function str-split() (maybe the most intricated, but use your code)
I found a magical function that splits a string in 2 elements How to split a string into two lists of numbers in SASS?.
So my idea is to use that function (thanks to #dayenite: if you like this solution, please upvote him ;)) in a string using a character (in my example "_") to split your maps in 3 different value (something like 2 keys and 1 value):
1. "news"
2. "black"
3. "#000"
Your map could become something like this:
$text-colors: map-merge(
(
"news_black":$black,
"info_red": $red
),
$text-colors
);
This is all the code in action:
#function str-split($string, $separator) {
// empty array/list
$split-arr: ();
// first index of separator in string
$index : str-index($string, $separator);
// loop through string
#while $index != null {
// get the substring from the first character to the separator
$item: str-slice($string, 1, $index - 1);
// push item to array
$split-arr: append($split-arr, $item);
// remove item and separator from string
$string: str-slice($string, $index + 1);
// find new index of separator
$index : str-index($string, $separator);
}
// add the remaining string to list (the last item)
$split-arr: append($split-arr, $string);
#return $split-arr;
}
/* Example with 2 colors */
$black: #000000;
$red: #ff0000;
$text-colors: () !default;
$text-colors: map-merge(
(
"news_black":$black,
"info_red": $red //my add
),
$text-colors
);
// Extendable classes.
.text-black{
color: $black;
}
.text-red{
color: $red;
}
// Loop function
.my-div{
#each $name, $value in $text-colors {
$list: (); // Create every time an empty list with my 2 argoments, for example "news" and "black"
$split-values: str-split($name, "_"); //use the function to split the string
#each $valore in $split-values {
$list: append($list, str-split($valore, " "));
}
//set here the first part of the string (i.e. news/info)
&--#{nth($list, 1)} {
background-color: $value;
#extend .text-#{nth($list, 2)} // set here the second part: black/red
}
}
}
https://www.sassmeister.com/gist/08f699dba4436d3bae6a4d8b666e815b
2. Using a nested list
This time I created a simple nested list with 3 value ("news", "black", $black), the result is the same.
$black: #000000;
$red: #ff0000;
// list text-colors
$text-colors: (
( "news", "black", $black ),
( "info", "red", $red )
);
// Extendable classes.
.text-black{
color: $black;
}
.text-red{
color: $red;
}
.mydiv{
// Loop function
#each $item in $text-colors {
&--#{nth($item, 1)} {
background-color: nth($item, 3);
#extend .text-#{nth($item, 2)}
}
}
}
https://www.sassmeister.com/gist/59adf5ee60ea46dd7a24e94d7db91d85
3. Using a nested map
Here I use nested maps, but the structure is different from yours and I don't know if it's ok for you.
$black: #000000;
$red: #ff0000;
// map text-colors
$text-colors: (
news:(black: $black),
info:(red: $red)
);
.text-black{
color: $black;
}
.text-red{
color: $red;
}
.mydiv{
// Loop function
#each $name, $value in $text-colors {
&--#{$name} {
#each $name-color, $value-color in $value {
background-color: $value-color;
#extend .text-#{$name-color}
}
}
}
}
https://www.sassmeister.com/gist/8ddec08755befc84f6e4846fbc625130
Well, I haven't another ideas. I hope at least one way could help you to solve your problem.
Cheers :)

Mixin (PXTOEM) provides error in CSS output

I use Drupal FortyTwo theme. The theme provides a mixin named PXTOEM:
// PXTOEM
// Calculate percentage with font-size as context
#function pxtoem($pixels...) {
$result: '';
#each $item in $pixels {
$result: $result + ($item + 0) / $default-font-size + em + ' ';
}
#return #{$result};
}
In my scss file I use it like:
.header-menus {
padding: pxtoem(0, $grid-gutter-width);
}
But after compiling it doesn't get the proper output?
padding: 0/pxem 0.75/pxem; (see screenshot)[![Screenshot][2]][2]
Instead of + 0 you should add pixels: + 0px. And instead of + em use + 0em.
Sassmeister demo.
If you can not modify source code of the theme, create your own function.
$default-font-size: 16px;
// PXTOEM
// Calculate percentage with font-size as context
#function pxtoem($pixels...) {
$result: '';
#each $item in $pixels {
$result: $result + ((($item + 0px) / $default-font-size) + 0em) + ' ';
}
#return #{$result};
}
.header-menus {
padding: pxtoem(0, 30, 30px);
}
Css output:
.header-menus {
padding: 0em 1.875em 1.875em ;
}

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
}

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