Mixin (PXTOEM) provides error in CSS output - sass

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

Related

What should I use with sass to make this code run well?

$frameSize: 100 / $chCount;
#keyframes type {
#for $ch from 1 to $chCount {
$frame: $ch * $frameSize;
#{$frame}% {
width: $ch * $chWidth;
}
}
100% {
width: ($chCount - 1) * $chWidth;
padding-right: 0;
}
}
this is some code which I took from codepen for animating text, but this section of the code was not valid in vscode even in SASS

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 :)

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

Sass function to convert pixels to ems

Take this function in Sass:
#function pem($pxval, $base: 16) {
#return #{$pxval / $base}em ;
}
(source: https://gist.github.com/2237465)
pem(16) returns 1em and it's ok, but pem(16px) returns 1pxem.
how can this function accept both types of input?
thanks
This seems like a good use for SASS's unitless() function.
#function pem($pxval, $base: 16) {
#if (unitless($pxval)) {
$pxval: $pxval * 1px;
}
#if (unitless($base)) {
$base: $base * 1px;
}
#return $pxval / $base * 1em;
}

Resources