sass & mixins: appending a class name with no spaces - css

Hello lovely stackoverflow community. I'm a new coder, and brand new to sass & intermediate css topics. I'm working on a site that has themes and I'm trying to customize the design of a 3rd party survey widget we are using.
Our themes are controlled via a mixin that looks like this:
#mixin themify() {
// Iterate over the themes
#each $theme-name, $theme in $themes {
$current-theme: $theme !global;
#if $theme-name == ‘abc’ {
#content;
} #else {
.theme-#{$theme-name} & {
#content;
}
}
}
}
The theme-name is on the body tag & inherited by every element. So can be used to target all children with ease. However, for the survey, I need to style it using a selector targeting a specific data-attribute on the body. For ex, this selector successfully allows me to target a survey element:
Body[data-survey-number='77777'] div#a .b {
// styles successfully applied!
}
But when I want to use the themify mixin:
Body[data-survey-number='77777'] div#a .b {
#include themify {
background-color:theme-get(secondary-color);
}
it breaks. I'm seeing that the sass compiles to:
theme-name Body[data-survey-number='77777'] div#a .b {
// never applies to anything :(
}
However what works is when a selector is compiled to this (appended at the end with no space):
Body[data-survey-number='77777']theme-name div#a .b {
// this works! :)
}
I'm wondering how I can get this. Perhaps creating a new mixin?
I appreciate all / any thoughts.

Related

How to check in SCSS if a variable exists and ha a value?

I am new in SCSS so bear with me :)
I have a use case where a SCSS variable --my-variable can exist and can have a value depending on some settings from the backend. So, if --my-variable exists and has a value I should override some styling. If not I shouldn't override anything.
Example:
In file1 I have:
.my-div {
color: red;
}
In file2 I should have something like this:
.my-div {
#include customize(color, --my-variable);
}
#mixin customize($property, $variable) {
#if $variable and (var($variable)) {
#{$property}: var($variable);
}
}
The problem is that the if condition inside the mixin customize() is always true even if my document has no CSS variable called --my-variable. What am I doing wrong?
Thank you
Sass has a function that check if the variable exists.
variable-exists()
$colorVariable: crimson;
#if variable-exists($colorVariable) {
// Do some styling if the variable exists
}

Unable to use CSS Custom Properties (aka CSS Variables) with SASS #if Statement

I'm trying to pass some CSS Custom Properties to a SASS Mixin. I'm able use the variables when applied directly in the styling I want. But when I try to use a variable in an If statement, it doesn't work.
Mixin Example:
#mixin bg-color($hue, $status) {
background: hsl($hue, 50%, 50%); // $hue works as expected
#if $status == 'danger' { // doesn't work!
color: 'red';
} #else if $status == 'warning' { // doesn't work!
color: 'orange';
} #else { // always enters the else branch
color: 'black';
}
}
CSS:
:root {
--hue: 195;
--status: 'default';
}
.demo {
#include bg-color(var(---hue), var(---status));
}
If I manually add the status value to the mixin, it works:
.demo {
#include bg-color(var(---hue), 'danger');
}
Any idea what might be the issue?
UPDATE: As #temani-afif mentioned, this approach isn't possible because SASS files are compiled before CSS variables are used.
If you have some file, where you import all SCSS files, it depends which is imported first and which are imported after.
Make sure that one that you need to be Read by VS is first.
For example i needed to read first my variables, so it have to be first, other way, my code read mixin, and doesnt know yet what is '$blue'.

Access body lang in SCSS in Angular 6

I am using #ngx/translate core for lang change.
I want to define sass on the basic of
<body lang="en">
or
<body lang="fr">
by if else condition.
Inside my component sass i want to do something like this
#if( bodylang = en)
/these rules
else
// these rules
How can i achieve this?
As it is SCSS you are able to use CSS attribute selectors for this, for example:
a[target="_blank"] {
background-color: yellow;
}
Which in your case would be:
body[lang="en"] {
// Your code
}
body[lang="fr"] {
// Your code
}
You can also nest them like such:
body {
background-color: orange;
&[lang="en"] {
background-color: blue;
}
}
This selector is more specific than just referring to the body tag itself. This means that it will apply the regular body CSS if none of the matching langcode tags are found. You can find more attribute selectors here: https://www.w3schools.com/css/css_attribute_selectors.asp
If you want to do it in your component SCSS file you have to do something like bellow code:
:host-context([lang="en"]){
// Your code
}
:host-context([lang="fa"]){
// Your code
}
If you are doing it in your styles.scss file you can achieve your goal with following code:
[lang="en"] {
// Your code
}
[lang="fr"] {
// Your code
}
rsangin's answer is the correct one.
If you want to select every language that is not en, you can just use :not([lang="en"])

sass, custom selectors based on parent

I have the following mixins to make easy work with BEM syntax, sass 3.3.2 code:
=b($name)
.#{$name}
#content
=e($name)
&__#{$name}
#content
=m($name)
&--#{$name}
#content
+b(menu)
+e(item)
color: grey
+e(item)
+m(alert)
color: red
This gives me the desired result:
.menu__item {color: grey;}
.menu__item--alert {color: red;}
So this works pretty nice for element level modifiers, however when i want to have block level modifiers the problem begins:
+b(menu)
+m(theme-1)
+e(item)
color: blue
css output:
.menu--theme-1__item {color: blue;}
when the thing i really want is this:
.menu--theme-1 .menu__item {color: blue;}
So i need a way to check what the context of an element is, when the context is a block there is no problem but when is a modifier the syntax fails. I tried inside e mixin to take the parent selector as string, so when e parent is b it will not have the -- syntax, in the other way when his parent is m it will have the -- syntax, with that i could decide what syntax use for both context.
I didn't find a way to take the parent selector as a string and i think is not possible, is there a way to make this works?
Update
I found a not very straightforward solution with it works fine, it uses a context argument in the element mixin:
=e($name, $context:null)
#if $context
&
+b($context)
&__#{$name}
#content
#else
&__#{$name}
#content
Now i can call the mixin as follows:
+b(menu)
+m(theme-1)
+e(item, nav)
color: blue
getting:
.menu--theme-1 .menu__item {color: blue;}
Answering a 2 year old question - long shot :) But hopefully could help someone else as well.
So a more robust way would be to improve your Element mixin to check if the parent selector has a modifier.
So you would need 2 functions:
one to check if a selector contains a modifier
one to get the block name from that selector
#function _bem-selector-has-modifier($selector) {
$selector: _bem-selector-to-string($selector);
#if str-index($selector, $bem-modifier-separator) or str-index($selector, ':') {
#return true;
} #else {
#return false;
}
}
#function _bem-get-block-name($selector) {
$selector: _bem-selector-to-string($selector);
$modifier-separator: '--';
$modifier-start: str-index($selector, $modifier-separator) - 1;
#return str-slice($selector, 0, $modifier-start);
}
And then you just need to apply the check in your element mixin
$bem-element-separator: '__';
#mixin element($element) {
$selector: &;
#if _bem-selector-has-modifier($selector) {
$block: _bem-get-block-name($selector);
#at-root {
#{$selector} {
.#{$block + '__' + $element} {
#content;
}
}
}
} #else {
#at-root {
#{$selector +'__' + $element} {
#content;
}
}
}
}
So calling element('item') inside a modifier('with-modifier') should render a .block--with-modifier .block__item selector in your compiled CSS and you won't have to manually pass the context.
The code posted is SCSS, but the idea should be the same with SASS.

Declare a global CSS property ? Is this possible?

I have a very wierd question, I dont know wether if its possible in css or not
Suppose I have say 3 different css classes as shown below, as you can see I have a common property of all these classes, I want to declare this color somewhere else and pass a reference to it here, so if next time I want to change the color I can simply change at one place rather than changing in all the 5 classes.
I know that you can use body{}, or a wrapper for this but that would affect the colors of the entire site right ? Is there a way to do this ?
Is this even possible ?
.abc {
color:red;
}
.abc2 {
color:red;
}
.abc3 {
color:red;
}
.abc4 {
color:red;
}
.abc5 {
color:red;
}
The bad news: you can't do it in CSS.
The good news: you can write in a meta-CSS language like LESS, which then processes a LESS file to pure CSS. This is called a "mixin".
In LESS:
#errorColor: red;
.error-color {
color: #errorColor;
}
#error-1 {
.error-color;
}
.all-errors {
.error-color;
}
More info: http://lesscss.org/#-mixins
if you want to declare all of them at a time, you can use:
.abc, .abc2, .abc3, .abc4, .abc5 {
color:red;
}
Or you can declare an additional class & add to all the .abc, .abc2.... & make its color:red;.
This can not be done with CSS, but that is still a very popular thing to do by using a CSS preprocessor such as LESS, SASS, SCSS, or Stylus.
A preprocessor will let you define a variable (say $red = #F00). It will replace the variable in your CSS document with the variable value for you, allowing you to write very DRY and module CSS.
This functionality is referred to as "CSS variables", which is part of the future spec, but not yet implemented on any browsers.
For now, the best way to do this in pure CSS is to declare an additional class for the desired "global", and then add that class to all relevant items.
.abc_global { color: red; }
.abc1 { /* additional styling */ }
.abc2 { /* additional styling */ }
<div class="abc1 abc_global"></div>
<div class="abc2 abc_global"></div>
With LESS
You are able to define that red color once:
.myRedColor {
color:red;
}
Now you can call that red on any CSS styles. Even NESTED styles! It's a wicked tool!
.abc1 {
.myRedColor;
}
.abc2 {
.myRedColor;
}
.abc3 {
.myRedColor;
}
.abc4 {
.myRedColor;
}
NESTED EXAMPLE:
.abc {
.itsEasyAsOneTwoThree{
.myRedColor;
}
}
Now all of our "itsEasyAsOneTwoThree" classes that are properly nested inside of an "abc" class will be assigned the red style. No more remembering those long #867530 color codes :) How cool is that?!
You can also use PostCSS with the plugin postcss-preset-env and support custom properties/variables, then use the :root selector to add global css variables.
:root {
--color-gray: #333333;
--color-white: #ffffff;
--color-black: #000000;
}

Resources