How to use sass string replace in data:image string? - sass

I'm using string replace mentioned on CSS Tricks
But I'm trying to do string replace within background image data:image/svg+xml string
I'm am trying to replace one of my color vars hex # with url encoded %23.
I don't think the stack snippet works with SASS but getting same results in jsfiddle.
You can see better in sassmeister the css output https://www.sassmeister.com/gist/7cf11bf6f3ee4951cf67e0e6074d1d67
#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;
}
$wm-black : #202020;
.modal-close {
&:before {
background: {
image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 320 512"><style>.a{fill:%23' + str-replace( $wm-black, '#', '%23' ) + '}</style><path class="a" d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>');
repeat: no-repeat;
position: calc(100% - 25px) 25px;
size: 36px 36px;
color: rgba(#000,.95);
}
opacity: 1;
transition: opacity .25s ease;
content: '';
display: block;
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 99;
}
}
<button class="modal-close"></button>
In PHP storm, the string parameter var is not working as it should so not valid.
My code currently fails to compile, so something is up but not sure why str-replace function wont return here.
Error code.
Module build failed (from ./node_modules/css-loader/index.js):
ModuleBuildError: Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: $string: #202020 is not a string.
╷
4 │ $index: str-index($string, $search);
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
╵
src/scss/_functions.scss 4:11 str-replace()
Thanks
Update: I've also tried this method, syntax looks better but still not compiling.
See it compiling in Sassmeister... https://www.sassmeister.com/gist/c9569c0373b52f0a9b3f8ae07860f8af

Interpolate and convert the color value into a string:
#{str-replace('' + $wm-black, '#', '%23')}
Example usage...
$wm-black : #202020;
.bg-svg {
background-image: url('data:image/svg+xml;utf8,<svg><style>.a{fill:#{str-replace('' + $wm-black, '#', '%23')};}</style></svg>');
}

Thanks #MiguelPynto for your answer, this is my solution which I am currently using, but same principle.
#{str-replace(#{$wm-black}, '#', '%23')}
Example usage below...
$wm-black : #202020;
.bg-svg {
background-image: url('data:image/svg+xml;utf8,<svg><style>.a{fill:#{str-replace(#{$wm-black},'#','%23')};}</style></svg>');
}

Related

Sass manipulate with #content

Is it possible to manipulate with #content magic variable in SASS?
I would like to replace some stuff in here before output.
Or maybe can I fill some variable with it?
The conclusion is that, I want to make an mixin #important that create both versions. Important, and no-important.
Input
.test {
#include important {
color: red;
text-align: left;
}
}
Expected output
.test {
color: red;
text-align: left;
}
.test-i {
color: red !important;
text-align: left !important;
}
No, you can't. But I quickly wrote you a mixin to make it work. It doesn't accepts multiple properties (yet).
First Note: I changed the mixin it now does accept multiple properties. Here is the Codepen.
Second Note: I updated the mixin adding multiple properties does no longer compile to different classes for each property, instead you get two versions, one without the !important suffix and one with.
This is the mixin:
#function return($state) {
#return if($state == '', '', '-i');
}
#mixin loop($name, $items...) {
#each $item in $items / 2 {
#each $state in ('', '!important') {
$suffix: return($state);
.#{$name}#{$suffix} {
#for $i from 1 through (length($items) / 2) {
#{nth($items, ($i * 2) - 1)}: #{nth($items, ($i * 2))} #{$state};
}
}
}
}
}
This is how you include it:
// #include loop([classname], [property], [value]);
#include loop(whateverClassname, color, red);
This is what it compiles to:
.whateverClassname {
color: red ;
}
.whateverClassname-i {
color: red !important;
}
This is what it now compiles to, when you use multiple properties at once:
#include loop(whateverClassname, color, red, background-color, green, display, flex);
.whateverClassname {
color: red ;
background-color: green ;
display: flex ;
}
.whateverClassname-i {
color: red !important;
background-color: green !important;
display: flex !important;
}
Conclusion: it works as expected and does no longer bloat your CSS.
Hope I could help you at least a little ;-)

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 Multiplication with units

If I try to multiply two value with units I get an unexpected error.
$test: 10px;
.testing{
width: $test * $test;
}
result: 100px*px isn't a valid CSS value.
I have used interpolation in the past when I want to do math with variables, and I think it is the simplest solution. If this doesn't work for you, perhaps it is due to a difference in compilers?
$test: 10px;
.testing{
width: #{$test * 2};
}
In fact width: $test * 2; compiles to width: 20px for me, you don't even need to use interpolation for simple math. I am using ember-cli-sass, which uses broccoli-sass-source-maps, which uses node-sass, which wraps libsass, to compile my SCSS to CSS. But it seems to work fine in this jsbin which uses SCSS with Compass.
Where interpolation really helps is if you need to use calc.
$test: 10px;
.testing{
width: calc(50% + #{$test * 2}); // results in calc(50% - 20px)
}
Multiplying units in SASS works like multiplying units in physics / engineering / chemistry / [insert science here].
(see more about this at https://www.sitepoint.com/understanding-sass-units/)
Multiplying two pixel values, will get you px^2, which is an area, not a distance.
What can you do? If you are certain you will be multiplying pixels, use a function and divide by 1 pixel.
$test: 10px;
#function multiply-px($value1, $value2) {
#return $value1 * $value2 / 1px;
}
.testing {
width: multiply-px($test, $test);//100px
}
If you don't know which units you'll be using in advance, you can strip the units from $value2, such that you always get the units of $value1.
(read more on that at https://css-tricks.com/snippets/sass/strip-unit-function/)
$test: 10in;
#function strip-unit($number) {
#if type-of($number) == 'number' and not unitless($number) {
#return $number / ($number * 0 + 1);
}
#return $number;
}
#function multiply-use-first-unit($value1, $value2) {
#return $value1 * strip-unit($value2);
}
.testing {
width: multiply-use-first-unit($test, $test);//100in
}
//try this
$test: 10;
.testing{
width: $test * $test px;
}
You cannot multiply two px values. Better way of doing it is function, but you have to use add in it to achieve it: -
$test: 10px;
#function calc-width($value1, $value2) {
#return $value1 + $value2;
}
.testing {
width: calc-width($test, $test);
}

Dividing percentage variable in Sass?

I have a variable which is a number and a % eg 10%. How can I use it as a value in my SASS but apply a division on it?
I have this:
$value: 0.1;
$value-percent: $value * 1%;
$value-from-50: (50 - $value) * 1%;
.test {
padding-left: $value-percent;
}
.test2 {
width: $value-from-50;
}
Which outputs this:
.test {
padding-left: 10%;
}
.test2 {
width: 40%;
}
What I now need to do is apply half of the value of $value-percent:
.test3 {
padding-left: $value-percent / 2;
}
So that I can output:
.test3 {
width: 5%;
}
Ive tried various combinations of that example code with normal and curly brackets. I can get the correct number of 10 outputted into the CSS but the % is always missing from it.
If your initial var isn't a percentage and is just a number you may need to try this:
.test {
padding-right: ($var / 2) + 0%
}
Which is better practice as it'll convert the value you pass it into what you're adding it to, in this case a percentage.

Passing a variable from inside a mixin declaration into the attached content block?

In Ruby, you can easily pass a variable from inside a method into the attached code block:
def mymethod
(1..10).each { |e| yield(e * 10) } # Passes a number to associated block
end
mymethod { |i| puts "Here comes #{i}" } # Outputs the number received from the method
I would like to do the same thing in SASS mixin:
=my-mixin
#for $i from 1 to 8
.grid-#{$i}
#content
+my-mixin
color: nth("red green blue orange yellow brown black purple", $i)
This code won't work because $i is declared inside the mixin declaration and cannot be seen outside, where the mixin is used. :(
So... How do i leverage variables declared inside the mixin declaration?
When i work with a grid framework and media queries, i need this functionality badly. Currently i have to duplicate what's inside the mixin declaration every time i need it, violating the DRY rule.
UPD 2013-01-24
Here's a real-life example.
I have a mixin that cycles through breakpoints and applies the provided code once for every breakpoint:
=apply-to-each-bp
#each $bp in $bp-list
+at-breakpoint($bp) // This is from Susy gem
#content
When i use this mixin i have to use this $bp value inside #content. It could be like this:
// Applies to all direct children of container
.container > *
display: inline-block
// Applies to all direct children of container,
// if container does not have the .with-gutters class
.container:not(.with-gutters) > *
+apply-to-each-bp
width: 100% / $bp
// Applies to all direct children of container,
// if container has the .with-gutters class
.container.with-gutters > *
+apply-to-each-bp
$block-to-margin-ratio: 0.2
$width: 100% / ($bp * (1 + $block-to-margin-ratio) - $block-to-margin-ratio)
width: $width
margin-right: $width * $block-to-margin-ratio
&:nth-child(#{$bp})
margin-right: 0
But this won't work, because the value of $bp is not available inside #content.
Declaring the variable before calling the mixin won't help, because #content is parsed once and before the mixin is parsed.
Instead, EACH time i need that, i have to do two ugly thighs:
declare an ad-hoc mixin,
write the cycle, violating the DRY principle:
// Each of the following mixins is mentioned in the code only once.
=without-gutters($bp)
width: 100% / $bp
=with-gutters($bp)
$block-to-margin-ratio: 0.2
$width: 100% / ($bp * (1 + $block-to-margin-ratio) - $block-to-margin-ratio)
width: $width
margin-right: $width * $block-to-margin-ratio
&:nth-child(#{$bp})
margin-right: 0
// Applies to all direct children of container
.container > *
display: inline-block
// Applies to all direct children of container,
// if container does not have the .with-gutters class
.container:not(.with-gutters) > *
#each $bp in $bp-list
+at-breakpoint($bp) // This is from Susy gem
+without-gutters($bp)
// Applies to all direct children of container,
// if container has the .with-gutters class
.container.with-gutters > *
#each $bp in $bp-list // Duplicate code! :(
+at-breakpoint($bp) // Violates the DRY principle.
+with-gutters($bp)
So, the question is: is there a way to do this Ruby-style?
Variables in Sass have scope to them. They're only visible in the block they were created in. If you want the variable to be accessible both inside and outside of the mixin, it has to be defined in the global scope:
$var: 0;
#mixin test {
$var: $var + 1;
color: red;
}
.test {
$var: 5;
#include test;
#debug $var; // DEBUG: 6
}
As long as you don't care about the state of $var for very long, this should work out ok for your purposes.
For your example, this won't work because it looks like the #content is processed first. What you need is a mixin that's written differently:
#mixin test($properties...) {
#for $i from 1 to 8 {
.grid-#{$i} {
#each $p in $properties {
$list: nth($p, 2);
#if length($list) > 1 {
#{nth($p, 1)}: nth($list, $i);
} #else {
#{nth($p, 1)}: $list;
}
}
#content;
}
}
}
.test {
#include test(color (red green blue orange yellow brown black purple));
}
The generated CSS:
.test .grid-1 {
color: red;
}
.test .grid-2 {
color: green;
}
.test .grid-3 {
color: blue;
}
.test .grid-4 {
color: orange;
}
.test .grid-5 {
color: yellow;
}
.test .grid-6 {
color: brown;
}
.test .grid-7 {
color: black;
}
A mixin like this can be fed any number of arguments and still allows you to use #content if you wish.
I have run into this problem myself and AFAIK this is a current limitation in SASS.
So this is currently unavailable in Sass.
There's a relevant ticket in the Sass issue queue: https://github.com/nex3/sass/issues/871 It's in the planned state but will probably not make it until at least Sass 4.0.

Resources