Sharing SASS variables from NPM package with another app (#use / #forward syntax) - css

Here's my problem.
First, there's a design system repo, with SASS files. Those files contain variables (declared with !default syntax), functions, mixins, and class-related styling. Here's the simplified repo architecture :
/src
/components
/component1
/_component1.scss
/component2
/_component2.scss
/scss
/general
/tokens
/colors
/_colors.scss
/fonts
/_fonts.scss
/styles.scss
All partials are forwarded (using #forward at-rule) in src/scss/styles.scss. Repo has been published as a NPM package.
Then, there's another app, which aims to use this design system package. Simplified repo architecture looks like this :
/scss
/general
/tokens
/colors
/_app-colors.scss
/_colors.scss
/fonts
/_app-fonts.scss
/_fonts.scss
/styles.scss
I'd like to declare app-related tokens (in my scss/general/tokens/* files), which would override design system tokens if declared with the same identifier. I used to do that with #import at-rules, but I can't make it work with #use and #forward at-rules anymore...
I thought I could achieve it by using that kind of syntax in my tokens files :
#use "./app-color" as app;
#use "#design-system-package/src/scss/styles.scss" as ds with {
$design-system-variable-1: blue; // hardcoded value
$design-system-variable-2: app.$app-variable // app variable as value
$design-system-variable-3: ds.$design-system-other-variable // design system variable as value
}
but it seems ds namespace is not recognized, and ds.$design-system-other-variable is undefined.
Could anyone help me to fix that ? Thank you :
Silvère MAZIERE

Related

How to use the Google Closure Compiler to optimize Kotlin JS

Is it possible to use the Google Closure Compiler to minify Kotlin JS code further than what Webpack offers? If so, how can it be done?
First, you should declare an NPM dependency on the Closure Compiler (latest version):
dependencies {
implementation(devNpm("google-closure-compiler", "20210808.0.0"))
}
Then, create a task that will run after webpack minification:
tasks.create<Exec>("compileWithClosure") {
// browserProductionWebpack: production minified version by Webpack
// kotlinNodeJsSetup: needed to execute Node scripts (':' because it is on the root project)
dependsOn("browserProductionWebpack", ":kotlinNodeJsSetup")
// Get the Node installation directory
val kotlinNodeJsSetup = rootProject.tasks["kotlinNodeJsSetup"] as org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsSetupTask
workingDir = File(kotlinNodeJsSetup.destination, "bin")
// Execute the script
commandLine(
"node",
"${File(rootProject.buildDir, "js/node_modules/google-closure-compiler/cli.js")}",
"--js=${File(buildDir, "distributions/<your module name here>.js")}",
"--js_output_file=${File(buildDir, "distributions/optimized.js")}",
"-O=SIMPLE",
"--env=BROWSER",
"--warning_level=QUIET",
)
}
Full list of arguments:
-O=ADVANCED seems to break the Kotlin generated file (at least in my case with Kotlin JS 1.5.21 IR)
-O=SIMPLE removes ~600kB from the binary (2.1MB → 1.5MB, using Kotlin React, KotlinX.Serialization & Ktor)
--warning_level=QUIET because we are passing an already-minified file, console output is completely unreadable, and actually slows down compilation

Symfony encore, variables in sass and scss

I have a variable file in scss and import it with Symfony encore for my scss and sass files (see the code below).
// enables Sass/SCSS support
Encore.enableSassLoader((options) => {
options.implementation = require('sass')
options.additionalData = `#import "#/scss/variables.scss"`
});
The problem is there are sass (node-module(s)) and scss (template) files in my project which needs them.
If I run it like the snippet above it went fine for the sass files, but the scss files give an error: 'SassError: expected ";" after the #import line in the additionalData'.
However if I add the ; after the import line I get an error from the sass files 'SassError: semicolons aren't allowed in the indented syntax.'.
It's probably a small issue which I miss but I have no clue at the moment. I tried it with the added parameter indentedSyntax with true and false in the sassOptions but this was no success.
Anyone have an idea?
With kind regards

The #use feature of Sass

First of all, I hope someone can actually understand this rambling question because I'm struggling to even word what I mean in a coherent way, but here goes:
I don't know why I'm struggling so much to figure this out, but I've been using #import with SCSS for a while now and feel I have my head around it fairly well, but now I want to use the #use rule since the phasing out of #import is a thing. And I can't find any videos or any real articles explaining how to use it properly.
The problem I'm having is I can't get my head around how to actually use it, I feel like I get the basic idea, and how to use the modules in each partial (I think..), but I feel like I don't understand how to actually get all of the partials into a main .scss file to be compiled into css.. This is hard to explain.. I just feel like I would still need to #import at least the files that have #use inside them into a main file for it to be compiled.. I'm guessing this obviously isn't the case, but otherwise I can't work it out.. Do I just #use all the files I want imported into the main file or..?
If anyone could shed some light on this for me, I would be really grateful.
Thanks
The new rules #use/#forward and the remove from #import are indeed a really big impact to SASS. It leads to a complete new form to write sass. A try to make an easy explanation for the beginning to use the new technique:
(1) #use works similar to #import. It adds the code from a (config- or partial-)file or a module to your stylesheet.
(2) The difference is: SASS changes the scope of variables/mixins/functions from global (all imported files = one scope) to local files (variables are only valid in the actual file). If you need to use variables/mixins/functions from another (config- or partial-)file you need to 'include' them to the actual file first.
That means for your project(*):
//file ###> _config.scss
$columnWidth: 50%;
$projectColors: (
primary: red,
secondary: blue,
);
//file ###> _functions.scss
#use 'config' as * // --> to use config vars (here: without namespace)
#function getColor( $name ){
$color: map-get($projectColors, $name);
#return $color;
}
//file ###> _partial.scss
#use 'config' as * // --> use config vars (here: without namespace)
#use 'functions' as * // --> use functions (here: without namespace)
.class {
width: $width;
color: getColor( primary );
}
//file ###> myStylesheet.scss
// no need to #use 'config' or 'functions'
// as they are not direct needed in this file
#use 'partial' //--> write the css
---
( * ) Including files without using a namespace is a special case to make the example more easy. Normaly you will include variables/mixins/functions to a separated namespace and call them by namespace.$variable or namespace.mixin. And there are techniques to move special settings to a #used file as well so you can move variable settings to the project. Please have a look to official and excelent description: https://sass-lang.com/documentation/at-rules/use
NOTES:
(1) As it is heavily discussed: Yes. This is INDEED the intended new way to work with SASS. (https://github.com/sass/sass/issues/2750)
(2) Very interesting: The actual brandnew version from Bootstrap has moved to the new Sass Version. But as I have seen Bootstrap does not use that new feature #use and still works with #import. That may have reasons ... and it seems not to easy to change the technique.
(3) Also it seems to be a little bit complicated there are some advantages comming with that new technique. Using separate namespaces make it much mor easier to work with external modules without causing name conflicts.

Localisation and configuration in the Sass module system

I've been using Sass's new module system for a while now, and for the most part have grokked how it works. One use case that I can't work out how to translate from the old system to new however is how to produce mostly identical stylesheets with only minor variations and configuration changes—for the purposes of localisation.
(CSS Custom Properties unfortunately aren't really an option for this. That would make this way easier!)
In the past, I've achieved this by having an entry file for each locale, with the configuration info for that locale, which could be grabbed by all the following imported files and make whatever locale-specific manipulations it needs to. For example:
en.scss
$locale: "en";
$rtl: false;
$font-family: "Gill Sans", sans-serif;
#import "shared/code";
ar.scss
$locale: "ar";
$rtl: true;
$font-family: sans-serif;
#import "shared/code";
When processed I'd end up with an en.css file with only the CSS that English requires and an ar.css file with only the CSS that Arabic requires, and all is good.
This is no longer possible in the Sass module system. Because an explicit aim of the module system was to remove global variables and more tightly scope things to individual files, #import has been deprecated in favour of #use and #forward, neither of which allow "children" access to the variables of the "parent" file.
My first thought was to lean into the module system and create an importable configuration file for each locale. Doing something like this works:
#use "locale/en" as locale;
html {
font-family: locale.$font-family;
}
However, now my locale reference is hardcoded into every file that requires it. #use only accepts a string (no variables), so I can't try and be clever with it, and I can't generate CSS for multiple locales without editing several files first. Fail.
My current hack-y solution is to #use "config"; everywhere, store my locale variables in a bunch of different configuration files, and then change the name of a locale file to "config" when I need to compile for that locale. This is obviously unsustainable and prone to human error a-plenty, so I'd like a second opinion on if there's some other method of doing this that has just completely passed me by, or whether this use case just isn't possible under the new Sass module system anymore.
Thanks in advance!
This is the purpose of with (...) configuration in the module syntax. The importing file is still able to configure variables in the loaded module - that just happens with a more explicit syntax:
// en.scss
#use "shared/code" with (
$locale: "en",
$rtl: false,
$font-family: ("Gill Sans", sans-serif),
);
and:
// ar.scss
#use "shared/code" with (
$locale: "ar",
$rtl: true,
$font-family: sans-serif,
);

Add timestamps to compiled sass/scss

Is it possible to automatically add a timestamp on the compiled CSS file using SASS?
e.g.
/* CSS Compiled on: {date+time} */
...compiled css goes here...
I've checked the SASS and Compass docs but no mention of such a feature.
I don't know of any built-in SASS or Compass feature for this, but it's not too hard to add a custom Ruby function to do it. (See the section entitled "Adding Custom Functions" in the SASS reference here.)
Your file with the Ruby function would look like this (let's call it "timestamp.rb"):
module Sass::Script::Functions
def timestamp()
return Sass::Script::String.new(Time.now.to_s)
end
end
And you'd reference the new timestamp function in your SASS file like this:
/*!
* CSS Compiled on: #{timestamp()}
*/
You just need to make sure your "timestamp.rb" file is loaded when you compile the SASS, either by requiring it from a Compass config file, or by using the --require parameter with the SASS command line. When all is said and done, you should get output like the following:
/*
* CSS Compiled on: 2012-10-23 08:53:03 -0400
*/
If you are using the command line version of SCSS you can do the following:
Install sass-timestamp
gem install sass-timestamp
Use it within your code like (see documentation for more information)
/* Compiled on #{timestamp()} */
Require it on the command line
scss -r sass-timestamp ...
Output will be
/* Compiled on 2015-02-02 13:01:40 +0800 */
Note: Use #{unix_timestamp()} for a unix timestamp
I don't know if everyone needs it (cause the question is a long time answered ago),
but a simple solution is to write the timestamp/date
to a single sass/scss file as SASS variable,
import them to the location where the timestamp should be
and then let a comment in sass write them out.
Nothing to install, compile or anything else - just using scripts and sass:
1.) Write the timestamp to a separate sass file: (Here a dos-script, but you can also use any other script/language to generate the simple file):
echo $BuildTimeStamp : "%date% %time%"> _timestamp.scss
2.) Import the generated file with the timestamp:
#import '_timestamp.scss';
3.) Write the header out as comment:
/*! Automatic build at: #{$BuildTimeStamp} */
Write the timestamp before you call the original sass command
and it will also work without the need to install, build or do anything else.

Resources