sass script variables in a #each directive - sass

I have this snippet of code in my scss file:
$ttk-bg-green: #99FF00;
$ttk-bg-orange: #FFBE00;
$ttk-bg-purple: #CD66FF;
$ttk-bg-blue: #55BBFF;
$ttk-icon-green: #80D500;
$ttk-icon-orange: #FFAF37;
$ttk-icon-purple: #CD66FF;
$ttk-icon-blue: #62C0FF;
$color-types: "bg" "icon";
$color-styles: "green" "orange" "purple" "blue";
#each $color-type in $color-types {
#each $color-style in $color-styles {
.ttk-#{$color-type}-#{$color-style} { color: $ttk-#{$color-type}-#{$color-style}; }
}
}
it's supposed to generate something along the lines of:
.ttk-bg-green: #99FF00;
The compass compiler fails to compile the code, saying $ttk variable does not exist.
it just fails to recognize it as a whole variable.
ideas? solutions?

Related

Sass combine #forward and #use

I'm trying to migrate to the new modular sass, and have two files I want to export from a common utils module:
utils/_functions.scss
#function color($key) {
#return map-get($colors, $key);
}
utils/_variables.scss
$colors: (
primary: 'red',
secondary: 'blue',
);
I'm trying to export it like this:
utils/_index.scss
#forward 'variables';
#forward 'functions';
But I get a compilation error SassError: Undefined variable, because $colors is not defined in function.
I would like to have a way to combine #forward and #use like this:
utils/_functions.scss
#use '../variables'
#function color($key) {
#return map-get(variables.$colors, $key);
}
Is that possible?
Indeed you have to #use the variables file in every file you need it. In this case you needd utils/variables in utils/functions but you do not #use it there.
Additional hint:
There are two additional issuses in your code, - but they do not affect the error in your question:
You should setup variables in modules as !defaults so they can be changed/overwritten when you #use them.
You have an error in your map. You setup the colors as string (= primary: 'red'). The result is that the value will be handeld as string and not as color which leads to a wrong css code.
Following code should work:
//###> file: utils/variables
$colors: (
primary: red,
secondary: blue,
) !default;
//##> file: utils/functions
#use 'variables' as *;
#function color($key) {
#return map-get($colors, $key);
}
//##> file: utils/index
#forward 'variables';
#forward 'functions';
//##> file main
#use 'utils' as *;
.class {
color: color( primary );
}
//---> compiles to css
.class {
color: red;
}

Importing external stylesheets globally with CSS modules

I'm struggling to add SASS with an external stylesheet (Bulma) to my React application. So far I have set up Parcel with CSS modules via postcss. This is my .postcssrc config:
{
"modules": true,
"plugins": {
"autoprefixer": {
"grid": true
},
"postcss-modules": {
"generateScopedName": "[name]_[local]_[hash:base64:5]"
}
}
}
I have installed node-sass and successfully added a .scss file to one of my components. External (Bulma) styles are added via #import "~bulma/bulma"; and are correctly resolved.
Unfortunately, imported styles are not applied globally and instead the class names are modified similarly to local definitions, e.g.:
/*! bulma.io v0.8.0 | MIT License | github.com/jgthms/bulma */
#-webkit-keyframes container_spinAround_28_Bz {
from {
transform: rotate(0deg); }
to {
transform: rotate(359deg); } }
#keyframes container_spinAround_28_Bz {
from {
transform: rotate(0deg); }
to {
transform: rotate(359deg); } }
Note the added prefixes and suffixes.
Ideally, I would like to import the stylesheet globally and do not modify their names. I'd like to continue using CSS modules and I think I also have to use SASS in order to globally modify Bulma stylesheet by overriding SASS variables.
Anyway, things I've tried so far:
Using postcss-nested and wrapping the import with :global block:
:global {
#import "~bulma/bulma";
}
However, this throws an exception:
main.scss:5018:5: Missing whitespace before :global
Creating a separate scss file included directly in HTML file via <link> rather than imported in a jsx/tsx file to avoid using CSS modules.
This seems to break Parcel entirely, as it fails to link correct files in the compiled HTML files, i.e. <LONG-HASH>.css instead of generated main.<HASH>.css.
Using postcss-import.
Either my setup is incorrect or it has no effect on SASS files.
You can define regular expressions to mark matched files as global stylesheets with the globalModulePaths setting.
"postcss-modules": {
"globalModulePaths": [
".*\\.global\\..*"
]
}
The example above would mark all files with .global. in their name, e.g. main.global.css.
Managed to figure it out while writing the question.
The only solution that worked for me to load global CSS styles from rollup (when applying preserveModules: true) was using the 'rollup-plugin-styles' plugin and the following configuration:
// rollup.config.js
plugins: [
styles({
extensions: ['.css'],
use: ['css'],
}), …
]
// In the package.json you have to add the proper side effects
{
"sideEffects": [
"**/*.css",
"**/*.css.js" //this one is very important too
],
}
// MyComponent.tx
import '../styles/myGlobal.css';

sass — how to convert parent- to id-selector

I have this:
.a-btn {
//rules here
##{&}__label {
//more rules
}
}
so I am after such an output:
.a-btn {…}
#a-btn__label {…}
BUT That compiles to this error:
SassError: Invalid CSS after "#": expected selector, was "#.a-btn"
So I need to convert .a.btn to a-btn. Therefore I have tried to use str-slice like that:
.a-btn {
//rules here
##{str-slice(&, 2)}__label {
//more rules
}
}
But that yields:
SassError: argument `$string` of `str-slice($string, $start-at, $end-at:-1)` must be a string
Can you try this
.a-btn
#at-root ##{str-slice(#{&},2)}__label
height: 200px
width: 200px
background: red

How to attach `\x` at the beginning of some value and make string out of it?

I have written below code in sass / scss. Here i am facing an issue i.e. when i'm trying to loop and concatenate value with \ . I'm getting space when my value contains alpha numeric. Kindly suggest how to overcome form this
SCSS =>>
Input :
$data: (
a:2766,
b:27B3,
d:1F48C
);
#each $value1, $value2 in $data {
.#{$value1}{
content: str-slice("\x",1,1)+($value2);
}
}
Output :
.a {
content: "\2766";
}
.b {
content: "\27B 3";
}
.d {
content: "\1F 48C";
}
It looks like this is a bug in Sass itself. Update your Sass and your problem should be fixed.
You can see this is the case on sass playground. Copy and paste your sass code there and then select Sass v3.3.14 and Sass v3.4.21 (or any later/newer) and you'll see the difference. :)
Edit:
In order to add \x at the beginning, you can do it this way:
SCSS (Sass v3.4.21):
$data: (
a: 2766,
b: 27B3,
d: 1F48C
);
#each $value1, $value2 in $data {
.#{$value1}{
content: str-insert(#{$value2}, "\\x", 1);
}
}
CSS output:
.a {
content: \x2766;
}
.b {
content: \x27B3;
}
.d {
content: \x1F48C;
}
str-insert puts a string inside other string. \\ in \\x escapes to single \x. Oh, and as the reference states, first character in sass is 1, not 0.

Sass variable interpolation with backslash in output

I'm creating some icon font rules for using in my site. Using Sass I wanted to list all the icons in a list variable and use #each to loop through them all.
Code looks like this:
$icons:
wifi 600,
wifi-hotspot 601,
weather 602;
#each $icon in $icons {
.icon-#{nth($icon, 1)},
%icon-#{nth($icon, 1)} {
content: "\#{nth($icon, 2)}";
}
}
The problem is the backslash on the content: line. I need it for the character encoding, but it escapes the variable interpolation, outputting CSS that looks like this:
.icon-wifi {
content: "\#{nth($icon, 2)}";
}
Adding one more backslash like this: content: "\\#{nth($icon, 2)}"; outputs this CSS:
.icon-wifi {
content: "\\600";
}
Is there a way to get the Sass to output CSS with only a single backslash while keeping the variable interpolation?
I got this to work by messing with the interpolation
sassmesiter demo
// ----
// Sass (v3.4.21)
// Compass (v1.0.3)
// ----
$icons:
wifi 600,
wifi-hotspot 601,
weather 602;
#each $icon in $icons {
.icon-#{nth($icon, 1)},
%icon-#{nth($icon, 1)} {
content: #{'"\\' + nth($icon, 2) + '"'}; // <------ See this line
}
}
compiles to
.icon-wifi {
content: "\600";
}
.icon-wifi-hotspot {
content: "\601";
}
.icon-weather {
content: "\602";
}
If you include the backslash in the actual variable, then when the sass generates the css, it will actually generate the calculated unicode character instead of outputting the unicode in the css output. This still usually works but it's hard to debug if something is going wrong and it is a bit more prone to cause issues in the browser in rendering the icon.
To output the actual unicode in the generated CSS, you can do this:
#function icon($character){
#return unquote('\"') + unquote(str-insert($character,'\\', 1)) + unquote('\"');
}
$icon-thing: "e60f";
.icon-thing:before {
content: icon($icon-thing); //outputs content: "\e60f";
}
You can add the backslash to the parameter in the $icons variable. That is,
$icons: wifi "\600", wifi-hotspot "\601", weather "\602";
#each $icon in $icons {
.icon-#{nth($icon, 1)}, %icon-#{nth($icon, 1)} {
content: "#{nth($icon, 2)}";
}
}
Generated CSS:
.icon-wifi {
content: "\600";
}
.icon-wifi-hotspot {
content: "\601";
}
.icon-weather {
content: "\602";
}
Use unquote and double slash
$var:123 → content:"\e123"
content:#{unquote('\"')+("\\")+("e")+$var+unquote('\"')};
If you are using Gulp to compile your Sass files, installing this Gulp plugin is probably the easiest way to get around the issue:
https://www.npmjs.com/package/gulp-sass-unicode
var sass = require('gulp-sass');
var sassUnicode = require('gulp-sass-unicode');
gulp.task('sass', function(){
gulp.src('style.scss')
.pipe(sass())
.pipe(sassUnicode()) // <-- This is the bit that does the magic
.pipe(gulp.dest( "css/" ));
});
There is no need to make any code alterations in your Sass files. Write out your Sass code how you want and the unicode characters are decoded back into regular escaped strings in the output CSS automatically.
Input SCSS
$testContent: "\f26e";
#test {
content: $testContent;
}
Output CSS
#test {
content: "\f26e";
}
Unfortunately, these solutions were not entirely working for me but I was finally able to get it working with SASS maps
//node-sass 4.11.0
//libsass 3.5.4
$hexes: (
checkmark: \2714
);
#function out-content($var) {
#return unquote("\"#{ $var }\"");
}
#each $mod, $code in $hexes {
.#{$mod}-after {
&:after {
content: out-content($code);
}
}
}
//output
//.checkmark-after:after {
//content: "\2714";
//}

Resources