lesscss: how to reparse everything from javascript? - css

My less file is dependent on many variables such as:
#fontRegular: "AvenirNextCondensed-Regular";
And classes make use of those variables, as follows:
.thisClass {
font-family: #fontRegular;
}
As you can expect, I need to retheme on-the-fly.
The theme itself redefines those variable, as follows):
#fontRegular: "HelveticaNeue";
How can I require less to reparse everything (I mean my less file) and how can I change the variables first?

Simply invoke less from you javascript (in this case, it's from my controller) as follows:
less.modifyVars(
$scope.config.fontStyleHelveticaNeue? {
'#fontRegular': "HelveticaNeue",
'#fontUltraLight': "HelveticaNeue-UltraLight",
'#fontDemiBold': "HelveticaNeue-CondensedBold"
}
: {
'#fontRegular': "AvenirNextCondensed-Regular",
'#fontUltraLight': "AvenirNextCondensed-UltraLight",
'#fontDemiBold': "AvenirNextCondensed-DemiBold"
}
);
Where on the example above, all #-prefixed words are my less variables I update (actually, there are two set there).
Less will rebuild all the css automatically.

Related

SASS/SCSS variable not working with CSS variable assignment

I have the following SCSS code:
#mixin foo($bar: 42) {
--xyzzy: $bar;
}
bar {
#include foo;
}
I would expect that I get CSS variable --xyzzy set to 42 on all bar elements. Instead of this, I get CSS stating bar { --xyzzy: $bar; }. The variable was not interpreted. I would need to use #{…} syntax instead to get the variable set.
Is this a feature of the SCSS/SASS? A bug? Can I get the interpretation working without enclosing the variable name in #{…}?
Actual result:
bar {
--xyzzy: $bar;
}
Expected:
bar {
--xyzzy: 42;
}
It's not a bug, it's how the Sass compiler works regarding CSS custom properties, known as CSS variables. The syntax #{…} is called interpolation, and it is the only way to inject dynamic values into a custom property. Here is a quote from the doc:
CSS custom properties, also known as CSS variables, have an unusual declaration syntax: they allow almost any text at all in their declaration values. What’s more, those values are accessible to JavaScript, so any value might potentially be relevant to the user. This includes values that would normally be parsed as SassScript.
Because of this, Sass parses custom property declarations differently than other property declarations. All tokens, including those that look like SassScript, are passed through to CSS as-is. The only exception is interpolation, which is the only way to inject dynamic values into a custom property.
That's the reason why you have that behavior, and only doing so works:
#mixin foo($bar: 42) {
--xyzzy: $bar; // does not work
--xyzzy: #{$bar}; // works
}

Is it possible to define and overwrite Sass/SCSS variables based on conditions

I have an I have a _overrides.scss file in which I want to provide a global variable based on conditions.
The variable should be true if my navigation with the .side-navigation class also contains the .-open class. If it contains the -closed class, the variable should have the value false.
Something like this:
$navbarOpen: false;
.side-navigation {
&.-open {
$navbarOpen: true;
}
&.-closed {
$navbarOpen: false;
}
}
I want to use the variable within another SCSS module in React, like:
#import 'overrides';
#if $navbarOpen == true {
footer {
background: red;
}
}
The variable is recognized, but the value is always false since it doesn't seem to be overridden by the condition set in _overrides.scss.
I think that the problem is that Sass variables can't be changed in the runtime, it'll be compiled to plain CSS and all vars will be replaced. Although as I see your condition depends on runtime events.
Check this for references: Dynamic Sass Variables

Referencing class names between SASS modules

I'm confused how to reference my SASS module class name in another module. I have a Contact page module and a PhoneNumber module. In certain context on this page, I'd like to change the button inside the PhoneNumber. Using simplified example for clarity:
// src/pages/contact/index.module.scss
#use 'src/components/PhoneNumber/index.module' as phoneNumber;
.page {
phoneNumber.container {
:global(.button) {
background-color: pink;
}
}
}
It doesn't work, I'm not sure how to reference .container class name of PhoneNumber component.
There are 2 issues:
(1) If you include variables imported by directive #use ... as namespace you have to call them using the added namespace with character $ as well. So you have to call the variable with phoneNumber.$container, not: 'phoneNumber.container'.
(2) If you use a variable as element/id/class-name you have to wrap variables in #{$hereMyVariable} at all.
So try following code:
.page {
.#{phoneNumbers.$container}{
:global(.button) {
background-color: pink;
}
}
}
FURTHER EXPLANATION
Rule #use loads mixins, functions, and variables from other Sass stylesheets so you can use them. To separate same variables/mixins/functions names from different stylesheets you can separate them to different namespaces by #use scssFileName as myNamespace.
Now you can call i.e. variables with same name namespace1.$container and namespace2.$container from different stlesheetswith different values. Mixins works the same way just without $.
So: if you load #use filepath/filename as phoneNumber to your stylesheet and call phoneNumber.container SASS is looking to the other stylesheet for a function or mixin named container. From the context of you simplified example I assume you want to call a variable with the name of the class. Than you need to use $ and the brackets.
(Notice: But if you want just to name your class phoneNumber.container you should write .phoneNumber.containerso SASS will interpret it as classname and not as reference to the namespace of the other stylesheet ...)
Wider explanations how to use #use can be find in the excellent official docs: https://sass-lang.com/documentation/at-rules/use

Get Less variable value from string

I am trying to create a function to switch themes. When I transpile my Less to CSS, the CSS file shows the literal string interpolation rather than the variable value. The variables file looks something like:
#beach-font-color: #3d3d3d;
#ocean-font-color: #d3d3d3;
#theme: "beach";
#symbol: "#";
#currentTheme-font-color: ~"#{symbol}#{theme}-font-color";
In the stylesheet:
body { color: #currentTheme-font-color; }
The generated css produces:
body { color: #beach-font-color; }
instead of:
body { color: #3d3d3d; }
One thing I thought might be required is:
#font: ~"#{symbol}#{theme}-font-color";
#currentTheme-font-color: ~"#{font}";
But this just produces the same results.
Why does the Less compiler use the string literal instead of the variable value?
How would I be able to get the value of the variable, going along these lines?
(I cannot skip this to not provide an alt. answer since such "namespace emulation via variable name concatenation" is my most favourite bloody wrong Less pattern (unfortunately it is also the most widely spread one :().
Instead of emulating namespaces via using looong global variable names (doh#1 in most of programming languages and paradigms global variables are considered harmful since 1970's... :) and then assembling those variable names via the ugly concatenation syntax (doh#2, ~"#{#{#foo}}-#{bar}-seriously}?"), one can use normal namespaces with much more clean syntax:
.theme(beach) {
#font-color: #3d3d3d;
// other beach variables
}
.theme(ocean) {
#font-color: #d3d3d3;
// other ocean variables
}
#theme: beach;
.theme(#theme);
body {color: #font-color}
This is just one of possible variations (for more examples see:
Dynamic Variable Names with Less -> gist
How to thematize in lesscss
etc.)

LESS: mixin with non-class ruleset

In LESS, I am trying to define the button.c2 ruleset, to be applied to <button class="c2">...</button> elements. This ruleset is mainly based on the button.c1 ruleset. However, the following code produces a ParseError:
button.c2 {
button.c1;// Trying to import a ruleset
... // additional rules, such as font-size: 120%;
}
It seems to me that the ParseError is caused by the fact that the imported ruleset does not refer to a class or ID ("button.c1" does not start with a "." or a "#"). From the LESS documentation:
Any CSS class or id ruleset can be mixed-in that way.
Why is there such a limitation? Is there any way around it?
The limitation might just be ease of parsing, since . or # don't show up as the first character of a normal style rule the parser automatically knows that those should be mixed in.
You could get around it by defining .c1 as a mixin and using it for both buttons:
.c1() {
// c1 rules
}
button.c1 {
.c1;
}
button.c2 {
.c1;
// additional rules
}
However, coming up in LESS 1.4.0 are :extend selectors, which will allow you to do what you want. The syntax would be:
button.c2:extend(button.c1) {
// additional rules
}

Resources