Creating scss variable in config.rb for scss files - ruby

How to define a SCSS variable in Config.rb for SCSS file[ COMPASS project ]
My Use-case
In Config.rb file something like
a = true
In style.scss i like to use the variable like
#if a == true{
background: #fff;
}
#else {
background: #000;
}
One of the solution
http://worldhousefinder.com/?p=124141
But its not worked for me.

You can't/shouldn't do this. You will precompile your assets so there is no way of dynamically adapting to changing variables. This might work for your config.rb variable, but it is a bad pattern to use and you'd have to recompile your assets every time you change the variable, this defeats the purpose of doing if else checks in your sass.
A much easier approach is to either add a class .active .inactive on your elements (body). Or output inline css in your head for things like custom colors etc depending on users that are signed in.
What are you trying to do? It sounds like something you'd do to check whether you are in production or development? In which case you could do something like:
<body class='<%= "development" if Rails.env == 'development' %>'>
or even
<body <%= "style='background-color: red;'" if Rails.env == 'development' %>
You should never need to use ruby in your css and javascript, if you find yourself doing it you are probably approaching it in the wrong way. At least that is what I have found after many attempts to do this nicely.
p.s. data-attributes are a very effective way of passing variables, etc to javascript

First of all, have you looked at variables and mixins?
If you're REALLY trying to pass outside variables to SASS, you must understand that it's not aware of your config file when it's compiling CSS. According to Nathan Weizenbaum (one of the main developers of Haml), the best way to do it is by creating custom user-defined functions.
If you go that route (again, quoting Nathan):
You'll want to make sure that your stylesheets
get regenerated whenever the data changes. You can do so by calling
Sass::Plugin.force_update_stylesheets.

Related

How can I provide configuration variables to a Sass/SCSS file before including it?

I'm migrating a Stylus library to SCSS since Angular 12 has deprecated Stylus and I'm in that impacted 0.3%. I've run into something we were doing that I'm not sure how to convert to SCSS—maybe it's impossible.
Let me lay this out simply: I work on several projects that all use loads of the same styles, so we put those styles together into one style sheet in its own NPM package. We can then just grab #import '#company/design/styles'; and suddenly we've got all of our regular styles and variables and mixins available in the project, or we can import #import '#company/package/styles/common'; for just the variables and mixins.
The thing is, our projects might need to configure the library before we import it. Suppose the library contains this bit:
// #company/package/styles/_forms.scss
input:invalid {
background: url('/assets/input-error.svg') no-repeat center right;
}
Not every project will have /assets/input-error.svg at that exact location. Maybe one of my projects has to use /subfolder/static/input-error.svg.
I could include this then overwrite input:invalid { background-image: url(...) } to supply it with the correct location, but there may be many references to this particular file and many other assets on top of that to correct. So we instead, in our Stylus library, we introduced an $asset-input-error variable that points to /assets/input-error.svg by default and did something like this:
// #company/package/styles/_forms.scss
input:invalid {
background: url($asset-input-error) no-repeat center right;
}
// the local project
$asset-input-error: '/subfolder/static/input-error.svg';
#import '#company/package/styles';
The above is heavily simplified and isn't actually legitimate SCSS, but I hope it conveys what we're trying to do: we want to set up what are effectively environment variables in our SCSS, include the common style sheet, and have it use those variables.
The thing is, I'm not sure what the legitimate or idiomatic approach is to do this in SCSS. Unlike Stylus, which has a global scope for its variables, SCSS would have me #use '../config'; and reference config.$asset-input-error, and from outside the library there's no way I see to change the configuration to point that asset to a different location. I'm sure SCSS has a way for me to do this, but I'm not sure what it is. Do I convert the entire library into a giant mixin to which I pass optional configuration? Do I do something with global variables? Something else?
How can I provide variables to my SCSS style sheet to configure it as part of including it in a project?
Ultimately the end goal here is just to be able to say to the library things like: “the assets to reference are here” (very important) or “the error color is this in this particuilar project” (less important).
Using #import
You can use global variables declared before the #import as you stated.
SCSS Documentation for this method
#company/package/styles/_forms.scss
$asset-input-error: '/subfolder/static/input-error.svg' !default;
input:invalid {
background: url($asset-input-error) no-repeat center right;
}
#company/package/styles/styles.scss
#import 'forms';
local.scss
$asset-input-error: '/different/path/input-error.svg';
#import '#company/package/styles';
CodeSandbox Demo
Using #use [...] with
You can also hop aboard the #use train if you prefer to future-proof your library.
SCSS Documentation for this method
SCSS Documentation for using mixins
SCSS Documentation for configuring forwards
#company/package/styles/_forms.scss
$asset-input-error: '/subfolder/static/input-error.svg' !default;
input:invalid {
background: url($asset-input-error) no-repeat center right;
}
#company/package/styles/styles.scss
#forward 'forms';
local.scss
#use 'styles' with (
$asset-input-error: '/different/path/input-error.svg'
);
Sadly CodeSandbox and StackBlitz don't support dart-sass, so I don't have a live demo for this but I tested it on the latest version of sass from npm.

Rails SCSS global variables

I've set up my project to use Bootstrap and scss with Webpacker, however, whenever I start my server locally I get this error:
ActionView::Template::Error (Error: Undefined variable: "$secondary-accent".
on line 76:23 of app/assets/stylesheets/_hero.scss
>> border: solid 0.5px $secondary-accent;
This error actually goes away locally if I do a hard refresh, but of course, Capistrano is not as forgiving and I want to figure out the issue anyway.
In my /app/javascscript/ folder I have a src/style.scss file which imports the required stylesheets.
#import '../../assets/stylesheets/_globals.scss'; //import globals first so the values propagate to Bootstrap
#import '~bootstrap/scss/bootstrap'; //import bootstrap
#import '../../assets/stylesheets/application.scss' //import everything else;
/assets/stylesheets/application.scss looks like:
#import '_navbar';
#import 'actiontext';
#import '_hero';
#import 'comments';
#import 'static_pages';
body {
font-family: $body-font;
font-size: $standard;
}
Of course, the easy way to get rid of this is to just add #import 'globals' to each of the partials but that does not seem to fit with the sass way. I don't really want to add #import 'globals' at the top of every single .scss file, not a big deal at the moment but as the project grows and the complexity of the styles increases maintainability could become a headache. I thought that Webpacker would take care of this, am I wrong? Am I missing something in the setup?
Ps. I realize this question has been asked dozens of times, but they all seem to be for older versions of Rails, or the solutions don't apply to me (such as removing the require tree from application.css
This was resolved by tightening my Webpacker set up a bit.
The biggest issue was installing scss I thought sass-rails in my gemfile was sufficient but I also needed yarn add scss. I didn't need to import the globals inside of src/style.scss just in application.scss.

AdminLTE themes: small disagreement with coworker (soft skills)

I have a quick question regarding implementation of a small change in our system, and I want to hear your opinion about my little disagreement with another developer in our company.
Our working environment:
Laravel
AdminLTE
Two laravel guards for 'partner' and 'staff'. Each type of user (partner/staff) has access to a different set of pages, using a different set of controllers and a different subdomain.
Admin LTE comes with some skins that you can apply to your <body>, for example 'skin-blue' theme. This is what our page looks like. Just for a comparison, if you remove the 'skin-blue' class, our website looks like this.
We were asked by our client to change the color of the top navbar for the Staff side. So, because the colors at the moment are being added by an adminLTE skin, I thought it was better to create a second theme for the staff side, calling it "skin-staff", and then in our base blade file, check for which guard is being used, and add the class accordingly.
<body class="#if(get_guard() === 'partner') skin-blue #else skin-staff #endif" ...>
I made a copy of the original skin-blue file, renamed it to skin-staff, and just changed the color of the necessary elements. I thought this was the best way to go about it, but the developer which had to review my github Pull Request said that because this was such a small change, it wasn't necessary to create a new skin. His proposed solution was to simply add the css classes in the blade file, something like:
<head>
…
<style type="text/css">
#if (get_guard() === 'staff')
.skin-blue .main-header .navbar{
background-color:#bdac3c
}
.skin-blue .main-header .navbar .sidebar-toggle:hover{
background-color:#ac9b2b
}
.skin-blue .main-header .logo{
background-color:#bdac3c;
}
… // and other classes
#endif
</style>
Now, to me this is not correct, because we are mixing the logic for staff and partner side without a clear way to differentiate them. If we use skins, we can simply say something like "The top navbar is yellow because we are using class skin-staff". And "We are using class skin-staff because we are on the Staff guard". The propositions are clear and simple. However, by adding raw CSS to our blade file, we end up with something like "The top navbar is yellow because we are using skin-blue and also we are on the Staff guard and also we have added some custom CSS for the Staff guard". The extra changes we introduce to the system don't follow the pattern used by adminLTE, to me they just look like noise. If we had to for example do this five more times, we would end up with a lot of CSS in our base blade file, which I think would look bad and would force us to eventually decide to use the skin system of adminLTE, something we could just do right away.
But, being as stubborn as I know I am, I don't know if I have the right idea or if I just want to do things my way.
What do you guys think? Is it better to create a new skin, even if most of the CSS code inside the skin file will be duplicated, but it allows us to stick to the existing way of doing things, or is it better to just add the code in the blade file and don't think more about it?
Thanks for your ideas
This is very much an opinion based question, there is no clear right or wrong answer here.
Personally, I agree with your coworker, why copy the whole theme, that is hundreds of lines long, just to change a handful of classes?
That said, I don't personally like the styles living in the DOM under a style tag.
Why not create a new CSS file that contains the styles:
.skin-blue .main-header .navbar{
background-color:#bdac3c
}
.skin-blue .main-header .navbar .sidebar-toggle:hover{
background-color:#ac9b2b
}
.skin-blue .main-header .logo{
background-color:#bdac3c;
}
… // and other classes
And then as long as you include this file after the base skin-blue CSS theme, your updated staff skin changes will take precedence.
Something like this:
<link rel="stylesheet" href="{{ asset('css/skin-blue.css') }}">
#if (get_guard() === 'staff')
<link rel="stylesheet" href="{{ asset('css/skin-staff.css') }}">
#endif
This keeps the abstraction of your CSS inside CSS files (and out of the DOM), yet only overwrites exactly what it needs to.
It also means that if you need to update a common style between the two themes, you don't need to make the change in two different files; you just need to modify the skin-blue.css file.

SCSS if function without false (else) clause

Anyone know of a way to use the SASS if function (not the #if directive) without a false (aka else) clause?
Use Case: I have a SASS library that's normally used on its own and includes a full CSS reset. Someone wants to take an individual file from the library and add it to a project that includes Zurb Foundation, which, of course, totally destroys the CSS reset. I'd like to accommodate that use case with something like:
$css-reset: false !default;
.form__input {
border-radius: 4px if(not $css-reset, !important);
}
That's a SASS compile error, though, since the compiler requires a third parameter to the if() function.
Thanks in advance
Not super elegant, but this works:
border-radius: 4px if(not $css-reset, !important, unquote("");

Generating dynamic CSS using templates

I want to generate dynamic CSS using templates and Tilt andSASS in Rails 4.
Let's say I have the following css.sass template:
$class_name: test
$width: 329
$height: 425
.view-#{$class_name}
width: #{$width}px
height: #{$height}px
I need master.css.sass.erb (I am not sure about the format) in which I am going to render my template as many times as I like with different parameters.
In application.css, I am going to have something like this
*= require master.css.sass.erb
Each time I use the template with different parameters, I am going to add a line in my master.css.sass.erb file. I do not know how to pass parameters using Tilt to the css.sass template. Could anyone assist and tell if this is possible?
This is what I have till now:
The template test.css.sass.erb:
$color: <%= color %>
body
background: #{$color} !important
The master.css.sass.erb file:
<%
require 'erb'
config_path = File.expand_path("../test.css.sass.erb", __FILE__)
template = Tilt.new(config_path)
template.render(self, :color => 'yellow')
%> .thisisrendered
color: red
Note that the two files in one folder. The issue is that only the following css is renrered:
.thisisrendered
color: red
To answer your question: when erb is compiled, it only outputs ruby code contained in a <%= code %> wrapper. Your current code is probably running fine (I'm not familiar with Tilt or straight SASS), but you aren't telling it to output the result to the sass file. Change the first line of master.css.sass.erb from <% to <%= and then you can debug from there.
That being said, I would recommend against this process. You're going to be wasting resources compiling the stylesheet every time it is called.
An alternate method would be to just keep your stylesheets static as in your opening example so they can be precompiled and cacheable, and then create a partial like layouts/_conditional_styles.html.erb using stock html and css like:
<% unless #your_sanitized_style_object.nil? %>
<style>
body{
background: <%= #your_sanitized_style_object.background_color.html_safe %> !important;
}
</style>
<% end %>
Which could override the application stylesheet by being rendered after the application css file in your layouts/application.html.erb file such as:
<%= stylesheet_link_tag "application" %>
<%= render "layouts/conditional_styles" %>
To answer your question as to why you use less resources using precompiled assets and then overriding them, consider your example test.css.sass.erb
$color: <%= color %>
body
background:$color !imporant
Assuming the color variable is red, the process would go something like this... First your application will run through it with its erb compiler and give you a test.css.sass file such as:
$color: #ff0000
body
background:$color !important
Then your application will run through the code again with its sass compiler to give you a test.css file such as:
body{ background:#ff0000 !important; }
And after all of that, it will serve up the file. In your development environment you will not see that much of a performance difference as your application defaults to rebuilding the assets for every request. The difference comes when it's time to serve your application to the web in production.
If your assets aren't dependent on variables in the application, the stylesheets can be precompiled. What this means is that your application compiles the asset once, and after it compiles the asset you get a stylesheet like test-f25ab2b1286feb7cc98375sac732f7d0.css.
The stylesheets will be identical but what is unique is all that jargon that rails throws on the end of the file name when it precompiles something. That jargon is known as a fingerprint and its purpose is to tell the server on an incoming request whether or not there is a newer version of the file. If the file hasn't been modified, and the system making the request has already downloaded that file to its cache, then your browser will use the cached version instead of downloading the stylesheet over again.
Now lets say your test.css.sass.erb file is something huge like 50kB for example's sake.
Without precompilation your resource cost is:
having to traverse that 50kB file 2 times on every request to compile it from erb > sass > css
having to serve up that 50kB file on every request.
With precompiled assets that are overridden by conditional styles embedded in the html of the layout as explained in my first answer:
Your compilation cost goes down to almost zero because the stylesheet is precompiled so there's nothing to do to it and your whatever.html.erb template that contains the conditional styles is already being compiled by your erb compiler so you're just adding the work of rendering variables.
You only serve up the precompiled stylesheet once meaning you save ~50kB in bandwidth on every request, using only the bytes needed for the rendered dependent styles.
Hope this helps, for more information on how all of this works I would recommend starting with the Asset Pipeline Rails Guide.

Resources