how to can I prevent css code duplication with sass - css

I am working on a project as front-end developer which has focused on client side performance.
I am using sass(scss), my approach is to convert every section to specific template with specific sass file, so I can import that template whenever I needed. sometimes some templates has divided to several component. in conclusion we have:
template -> every section : import in page
component -> some template part : include with sass mixin if needed
so if you use mixin, codes can be duplicate, as I said before I need 1 css file for every page, but I have solved the duplication of mixins, my approach is:
every mixin has a variable which say, is mixin has included yet or not. if it has included so do not include again. (mixin has #if $mixin_var==false { mixin codes }). so we have these:
template code:
#include component_1;
$component_1: true; // now included, this variable has declared in file _varibale.scss in top of codes
#include ...;
mixin code:
#mixin component_1 {
#if $component_1==false {
// mixin code here ...
}
}
so if I include component_1 again in other sass file, the $component_1 is true, so mixin condition prevents it.
by this method, code duplication has solved yet.
but now I want to convert every template to single css file and finally include in page. so I cannot use variable and mixin anymore and components code duplicate again and again.
is there any approach to solve this? maybe any npm package or some other sass approach...
any help appreciated.

Related

What's up with this 'Unknown at rule #use' error, do I need a plugin/ some particular set-up besidess sass compiler?

So, I've done making all of my partials at there own folder(like base,abstracts etc) and every folder have there _index.scss where I already #forward all the partials there. Then at the main.scss file, the only thing I needed to do is #use all of my folders' index. But, instead of showing all the results at the css file, it just copies the #use comment from the main sass file, and return with the 'Unknown at rule #use', this makes sense because #use only works for sass not css. But why does the css doesnt translate the #use function from the main sass file, instead it just spit out the exact code block which is " #use 'abstracts' "(for example). Do I need to set-up something besides the sass compiler itself? Like some javascript? Because I dont really have any decent knowledge at Javascript. I hope you can answer my questions, Thank You!!
This is the screenshot, the right side is the css, which suppose to load the sass files,but instead just output the same command which is the #use

Conditional stylesheet in Nuxt SSR depending on a store value

I've been surfing StackOverflow and the Nuxt documentation, but I cannot get my head around this one.
So, I need to do RTL. My project is in Nuxt and we use SCSS. I'm setting the htmlAttrs to dir="rtl" conditionally depending on a store getter that tells me if the language is RTL or not. The requirement for this specific task is that a RTL stylesheet should be imported conditionally from the server side also if the country is RTL, so that it overrides the main.scss file.
Now, in nuxtServerInit(), I cannot set the stylesheet in the head, because the route will not direct me to the file, and, most importantly, Webpack won't compile it, as it's outside the regular flow of the application and not imported by main.scss, which is the stylesheet the Nuxt config is pointing to, and which contains all other styles. I realize that I could use a class and use it conditionally in components, but that is not the requirement. The nuxt.config.js file, in the relevant part, looks like this:
css: [
'#/assets/styles/main.scss'
]
There I obviously don't have access to the store.
What I said I tried was this:
if (isRTL) {
service.addEntryToArray('link', {
rel: 'stylesheet',
type: 'text/css',
href: '../assets/styles/main.rtl.scss'
});
}
(We use a service to add things to the head)
I understand that was naive on my part, because Webpack has no say there, so there is no transpilation, and there is no actual asset, which means the route is just wrong, and even if it were right, the browser would not be able to read it.
Any ideas on how this could be achieved at a non-component level? This needs to be part of the Webpack flow, has to be added server-side, and needs to be an import --I cannot just import it regularly and use just a class (again, I'm not stubborn, I'm working on a project, and this is a requirement by the client).
If you need more information, I'll update the post, but I don't know what else to provide.

Can CSS #import be set inside a class when using SCSS or SASS? Syncfusion example

I have the following structure but can't get it to work.
The first CSS rules get always overwritten by the second import, no matter if the body class is light or dark.
Is there a way to do it purely with SCSS?
base.scss:
body.light {
#import './light';
}
body.dark {
#import './dark';
}
_light.scss:
#import '../../node_modules/#syncfusion/ej2-base/styles/material.css';
_dark.scss:
#import '../../node_modules/#syncfusion/ej2-base/styles/material-dark.css';
Greetings from Syncfusion.
We have checked this and we would let you know that we have already logged a feature request for this support that we can track using the below feedback portal.
Feedback: https://www.syncfusion.com/feedback/8141/provide-latest-sass-version-support-with-ej2-components
We expect that it will be available with our Volume 3, 2020 release and we let you know the details for dynamically changing theme using SCSS. Until then, we request you to use the below method to change the theme dynamically.
https://www.syncfusion.com/kb/10868/dynamic-theme-change
Please get back to us if you need any further assistance on this.
Syncfusion Components are capable of dynamically generating theme file in compile time as in other third party components. But, we are not able to dynamically generate the themes in runtime as requested in the first query of this thread. We request you to use the methods
suggested in the below KB link for dynamic theme changing in the application.
https://www.syncfusion.com/kb/10868/dynamic-theme-change

Bootstrap 4 Sass - changing theme dynamically

New to sass I stuck with the problem how to enable the dynamic change of a website theme - lets say a dark and a light theme - through user interaction. The template I use as a base (coreui) has a _custom.scss file in which various variables are defined
...
$navbar-bg: #fff;
...
The values of these variables would need to be set dynamically depending on the user choice of the theme and I have no clue how to get this done. Any pointer how to implement this would be highly appreciated.
SASS is a preprocessor which means essentially it gets compiled down into regular CSS and then shipped to the client. If you want to change themes you'll have to dynamically load different stylesheets using javascript.
Case 1
In the case that you want the user to pick between multiple prepackaged themes. This can easily be done with multiple "theme" style sheets which import the various parts of your style. First import the different colors, then import the main bodies of your sass. For example:
theme1.sass:
#import 'theme1';
#import 'base';
#import 'other';
theme2.sass:
#import 'theme2';
#import 'base';
#import 'other';
Then in javascript you could remove the old stylesheet and add the new one when the user does whatever is needed to change the theme. For example inside the onclick of a button you could put:
document.getElementById('cssTheme').setAttribute("href", "/path/to/theme");
It's probably best to take a bit of care and put the element in the head of the document, but this is a good starting point. That could be made to look a lot nicer with Jquery, but I didn't want to assume you'd have that.
Case 2
In the case that you want the user to dynamically change colors of individual element colors it might be worth looking into CSS Variables. Current support in IE/Edge is crumby but it is pretty interesting. Then as the user changes the fields you could just be changing the css variable in a <style> tag somewhere on the page and it should propagate through the document.
If you want more browser support then I think really the best way would be with OK sure's answer. This one gives you the benefit of just changing a variable and not having to reset each element style that uses that variable.
You have 2 options I think.
Option 1) Recompile the styles whenever a change is made by running a command serverside to generate a new CSS file for the user. This will be potentially very resource hungry and probably not recommended.
Option 2) Take the variables you want to be accessible, find where they are mentioned in the bootstrap source and either generate a file or just inline these styles after the stylesheet is included in the template.
So for your example here, depending what language you're coding in, or templating (this is a twig example) you're using, you could inline the following in the head of your template:
<style>
.navbar {
background-color: {{ user_theme.navbar-bg | default('#eeeeee') }}
}
</style>
It's tough to tell you exactly how to do this without knowing what frameworks/languages/infrastructure you're using.
If you were using Twig & PHP for example, you could send a user_theme object to the template, and have an include file which contains all the styles that need modifying with default values as above.

Dotless MVC bundling issue when importing same less file twice

Im trying to accomplish following structure using dotless:
styles/variables.less - contains, well, all variables like below
#color:green;
styles/component1.less - some random component specific style which imports variables.less
#import "variables";
body {
background:#color;
}
styles/component2.less - some more styles which also imports the global variables.less file
#import "variables";
a {
color:#color;
}
BundleConfig.cs - declaring the bundle like below. Im using this bundling addition: https://gist.github.com/benfoster/3924025
bundles.Add(new Bundle("~/styles/css", new LessTransform()).Include("~/styles/component1.less", "~/styles/component2.less"));
Everything works fine when Debug is set to true
But When Debug is set to false
Only the first file passed in Include method of bundle resolves #import "variables". The rest just fail.
Below is the output of declaring "~/styles/component1.less" first
bundles.Add(new Bundle("~/styles/css", new LessTransform()).Include("~/styles/component1.less", "~/styles/component2.less"));
Output when "~/styles/component2.less" is declared first
bundles.Add(new Bundle("~/styles/css", new LessTransform()).Include("~/styles/component2.less", "~/styles/component1.less"));
Strangely - it works if i import different files in component1 and component2
For instance, if i rename "varibales" to "variables.less" in either file just to make those imports look a bit different. It works. Like below
styles/component1.less
#import "variables.less"; // added extension here
body {
background:#color;
}
Any thoughts? Ive been fidling with this for days..
Edit
Reasons for using this structure:
To send seperate less files in debug mode, as it makes its easier to debug. Line number comments aren't very helpful
To concatenate and minify all the less files when served in production.
Adding #import "variables" on top of every file is ugly.
So, tried declaring variables.less as part of .Include("variables.less", file-dependant-on-variables.less, ...)
Which apparently doesnt work because of some scoping issues mentioned here: Dotless - Can't reference less variable in separate file with MVC Bundling
There is a fix for that, concatenating contents of every single less file and use Less to parse that concatinated file instead. Example here, https://groups.google.com/forum/?fromgroups#!topic/dotless/j-8OP1dNjUY
But in that case, i dont seem to be able to get minified version of the parsed file.
According to the docos:
In v1.3.0 - v1.3.3 #import imports a file multiple times and you can
override this behaviour with #import-once.
In v1.4.0 #import-once has been removed and #import imports once by
default. This means that with the following
The second import of variables is ignored and the variable #color:green; is only defined in the scope of the first component; being undefined in the scope of the second component it would probably end up with the rule or even the entire less file ignored (I'm not that intimately familiar with the default behavior, you could just add in extra properties and rules and see what happens). You could confirm this by inspecting preprocessor logs or otherwise allowing its errors to be traced in the console.
Importing variables at a "higher" level into shared scope like #Hans suggested (+1) should fix this. A tentative alternative could be to downgrade the preprocessor to v1.3.0-v1.3.3 so that #import fires multiple times. Since my preference when dealing with css pre-processors revolves almost exclusively around their variable and mixin functionality I myself could possibly see this as an acceptable option.
I might be missing something here since you didn't state why you are trying to achieve this structure, but you could easily avoid the problem AND generate a smaller resulting bundle by rearranging your file structure. Create a 4th less file with the following content:
#import "variables.less";
#import "component1.less";
#import "component2.less";
And just throw this file into the bundler. The bundle will end up smaller because variables.less is included only once instead of twice, and this definetly works with Dotless.
#o.v. is absolutely right. Dotless generates the next error during parsing the second *.less file in the bundle:
variable #color is undefined on line 4 in file
'..\styles\component2.less':
[3]: a { [4]: color:#color;
----------^
If you look at dotless sources you will find CheckIgnoreImport method in dotless.Core.Importers.Importer class which is called for each imported file:
/// <summary>
/// returns true if the import should be ignored because it is a duplicate and import-once was used
/// </summary>
/// <param name="import"></param>
/// <returns></returns>
protected bool CheckIgnoreImport(Import import, string path)
{
if (_rawImports.Contains(path, StringComparer.InvariantCultureIgnoreCase))
{
return import.IsOnce;
}
_rawImports.Add(path);
return false;
}
Currently import.IsOnce value is always true (see dotless.Core.Parser.Parsers class, line 1080). And you don't have opportunity to change this behavior outside the dotless library.

Resources