Sass local compile doesn't work, codepen works - css

I'm trying to compile this little piece of .scss code:
$main-color:#06c;
:root{
--main-color-05: adjust-color($main-color, $lightness: +51%);}
When i compile i get this results:
local (sass -v: Sass 3.5.2 (Bleeding Edge))
:root {
--main-color-05: adjust-color($main-color, $lightness: +51%); }
Codepen (here's the code)
:root {
--main-color-05: #d1e8ff;}
Local compiler seems to ignore adjust-color function in custom properties, which works in class declarations:
$main-color:#06c;
:root{
--main-color-05: adjust-color($main-color, $lightness: +51%);}
.main-color-05{
background-color: adjust-color($main-color, $lightness: +51%);}
(compiled)
:root {
--main-color-05: adjust-color($main-color, $lightness: +51%); }
.main-color-05 {
background-color: #d1e8ff; }
Thanks in advance

You can use some sass mixin to set custom properties. Please have a look
https://codepen.io/malyw/pen/aNwKKv
// SCSS
$rootVars: ();
#mixin setVarSass($varName, $value){
$rootVars: map-merge($rootVars, (
#{$varName}: $value
)) !global;
}
#function getVarSass($varName){
#return map-get($rootVars, #{$varName});
}
#include setVarSass("color", #f00);
// CSS
#mixin setVarCss($varName, $value){
#supports ( (--a: 0)) {
// Custom properties are supported in the browser
$varName: unquote($varName);
:root{
#{--$varName}: $value;
}
}
}
#function getVarCss($varName){
$varName: unquote($varName);
#return #{var(--$varName)};
}
// Common
#mixin setVar($varName, $value){
#include setVarSass($varName, $value);
#include setVarCss($varName, $value);
}
#mixin setPropFromVar($propName, $varName){
#supports ( (--a: 0)) {
// Custom properties are supported in the browser
#{$propName}: getVarCss($varName);
}
#supports ( not (--a: 0)) {
// Custom properties are NOT supported in the browser
#{$propName}: getVarSass($varName);
}
}
// SET/GET
#include setVar('main-color', #f00);
body {
#include setPropFromVar('color', 'main-color');
}
or use npm module like
https://github.com/malyw/css-vars
https://www.npmjs.com/package/custom-properties-sass

Related

Sass mixin to generate CSS properties from a map of maps

I have a map of maps:
$colors: (
'yellow': (
'10': #fcf4d6,
'20': #fddc69,
'30': #f1c21b,
'40': #d2a106,
'50': #b28600,
'60': #8e6a00,
'70': #684e00,
'80': #483700,
'90': #302400,
'100': #1c1500,
'hover': (
'10': #f8e6a0,
'20': #fccd27,
'30': #ddb00e,
'40': #bc9005,
'50': #9e7700,
'60': #755800,
'70': #806000,
'80': #5c4600,
'90': #3d2e00,
'100': #332600
)
)
...
My goal is to use a mixin that translates this to CSS properties, recursively.
I feel like in any other language, this would be easy (maybe I don't know enough Sass).
For example,
html {
#include my-mixin($colors);
}
would translate to:
html {
--yellow-10: #fcf4d6;
--yellow-20: #fddc69;
...
--yellow-hover-10: #f8e6a0;
...
}
I have gotten as far as to build the property name only:
#mixin define-css-properties($map, $name: "") {
#each $key, $value in $map {
#if is-map($value) {
#include define-css-properties($value, $name + "-" + $key);
} #else {
--#{$name + "-" + $key}: red;
}
}
}
I solved it.
#mixin define-css-properties($map, $name: "", $valueAccum: null) {
#each $key, $value in $map {
#if is-map($value) {
#include define-css-properties($value, $name + "-" + $key, map.get($value, $key));
} #else {
--#{$name + "-" + $key}: #{$value};
}
}
}
Would love to hear feedback or see alternatives.

Angular Material themes toggle not switching themes

Scenario
We followed this tutorial to provide our users a dark and a light theme.
Problem
The browser loads the expected css-File (dark or light). Nevertheless, the styles are not applied to our components.
This happens in <head></head>:
<link rel="stylesheet" type="text/css" href="theme-dark.css">
or
<link rel="stylesheet" type="text/css" href="theme-light.css">
As you can see, the browser correctly switches the two themes.
Code
angular.json
"styles": [
"projects/menu/src/styles.scss",
{
"input": "projects/menu/src/theming/theme-dark.scss",
"bundleName": "theme-dark",
"inject": false
},
{
"input": "projects/menu/src/theming/theme-light.scss",
"bundleName": "theme-light",
"inject": false
}
],
theme-service.ts
private _mainTheme$: BehaviorSubject<string> = new BehaviorSubject(
'theme-light'
);
private _darkMode$: BehaviorSubject<boolean> = new BehaviorSubject(false);
darkMode$: Observable<boolean> = this._darkMode$.asObservable();
private _renderer: Renderer2;
private head: HTMLElement;
private themeLinks: HTMLElement[] = [];
theme$: Observable<[string, boolean]>;
constructor(
rendererFactory: RendererFactory2,
#Inject(DOCUMENT) document: Document
) {
this.head = document.head;
this._renderer = rendererFactory.createRenderer(null, null);
this.theme$ = combineLatest([this._mainTheme$, this._darkMode$]);
this.theme$.subscribe(async ([mainTheme, darkMode]) => {
const cssExt = '.css';
const cssFilename = darkMode
? 'theme-dark' + cssExt
: mainTheme + cssExt;
await this.loadCss(cssFilename);
if (this.themeLinks.length == 2)
this._renderer.removeChild(this.head, this.themeLinks.shift());
});
}
setMainTheme(name: string) {
this._mainTheme$.next(name);
}
setDarkMode(value: boolean) {
this._darkMode$.next(value);
}
private async loadCss(filename: string) {
return new Promise((resolve) => {
const linkEl: HTMLElement = this._renderer.createElement('link');
this._renderer.setAttribute(linkEl, 'rel', 'stylesheet');
this._renderer.setAttribute(linkEl, 'type', 'text/css');
this._renderer.setAttribute(linkEl, 'href', filename);
this._renderer.setProperty(linkEl, 'onload', resolve);
this._renderer.appendChild(this.head, linkEl);
this.themeLinks = [...this.themeLinks, linkEl];
});
}
theme-base.scss
#import '~#angular/material/theming';
#include mat-core();
#mixin theming($theme) {
#include angular-material-theme($theme);
}
theme-dark.scss
#import '~#angular/material/theming';
#import './theme-base';
#include mat-core();
// color palette definitions
$primary: mat-palette($dark-md-mcgpaletteblack);
$accent: mat-palette($md-mcgpalettewhite);
$warn: mat-palette($dark-md-mcgpalettered);
$background:mat-palette($dark-md-mcgpalettebackground);
$confirm: mat-palette($dark-md-mcgpaletteorange);
$theme-dark: mat-dark-theme((color: (primary: $primary,
accent: $accent,
warn: $warn,
background: $background,
confirm: $confirm)));
theme-dark.scss - looks the same
In style.scss we import both themes:
styles.scss
#import 'projects/menu/src/theming/theme-dark.scss';
#import 'projects/menu/src/theming/theme-light.scss';
.theme-dark {
#include angular-material-theme($theme-dark)
}
.theme-light {
#include angular-material-theme($theme-light)
}
and then we use those variables:
styles.scss
body {
background: mat-color($background, default);
}
h1 {
color: mat-color($accent, default);
}
But currently we also import them in our components. I think this is one thing, that is wrong.
A dialog would look like this:
#import 'projects/menu/src/theming/theme-dark.scss';
#import 'projects/menu/src/theming/theme-light.scss';
.theme-dark {
#include angular-material-theme($theme-dark)
}
.theme-light{
#include angular-material-theme($theme-light)
}
.mat-button {
color: mat-color($accent, default);
background-color: mat-color($confirm, default);
}
What I tried:
Removing the import of both themes in every stylesheet. Colors are still influenced by the order of the theme import in style.scss. Dark theme last -> dark theme will be used and the other way round.
Using the same variables for the themes as in the tutorial. But then we had to change of all of the background-color: mat-color($confirm, default);
And so on. Spent lots of time...
We can't figure out how to put the missing parts together. Do we need to use mixins? Or are we missing setting the class dark-theme on the root component?
Thank you very much in advance.
We finally got it working.
Our mistakes:
#include mat-core(); must only be imported once (in theme-base.scss)
#import 'projects/menu/src/theming/theme-dark.scss'; and #import 'projects/menu/src/theming/theme-light.scss'; must only be imported once (in styles.scss). Importing them in all of the components lets bundle size explode.
Set theme-dark class directly in theme-dark.scss
Do not set classes theme-dark and theme-light in styles.scss or any other component theme
Create two wrapper elements for whole application: <div [ngClass]="{ 'theme-dark': darkMode$ | async }"> <div class="mat-app-background"> <router-outlet></router-outlet></div></div>
If components need to access the theme, use mixins (example shown below)
In theme-service.ts, use OverlayContainer to apply styles to material dialogs as well (as described here)
Mixin example:
settings-dialog.component.scss
#import '~#angular/material/theming';
#mixin settings-dialog-theme($theme) {
.settings-dialog {
#include mat-elevation(2);
.mat-button {
background-color: mat-color($confirm);
}
}
}
settings-dialog.component.html
<div mat-dialog-content class="settings-dialog"> Put your content here </div>
Create a component theme as described here
component-themes.scss
#import 'your-path/settings-dialog.component';
#mixin component-themes($theme) {
#include settings-dialog-theme($theme);
}
Import it into your base theme file
theme-base.scss
#import '~#angular/material/theming';
#import './component-themes.scss';
#include mat-core();
#mixin theming($theme) {
#include angular-material-theme($theme);
#include component-themes($theme);
}

How to separate scss function returned values

I have two SCSS maps call $my-map-1 and $my-map-2. Each map has the keys with their hex value. I wrote a function to return the key and the hex values ($key, $value) of each map separately.
After that, I wrote a #if condition with my function to check the map. I pass my map name to the function. If map there, check is the $key equal to the given name. If that true, pass the $valu of that $key to my color mixin. This is my code.
$my-map-1: (
map-1-color-1: #506c89,
map-1-color-2: #737373,
map-1-color-3: #2a3949,
map-1-color-4: #182028,
);
$my-map-2: (
map-2-color-1: #fff,
map-2-color-2: #000,
map-2-color-3: #ddd,
map-2-color-4: #ccc,
);
//the function to read te map and return the key and the value.
#function color-map($color-map) {
#each $key, $value in $color-map {
#return ($key, $value);
}
}
//mixin
#mixin color-mix($color){
color: $color;
}
//css classes from here
#if color-map($my-map-1) {
if($key == map-1-color-1) {
.my-class{
#include color-mix($color:$value);
}
}
}
You can use map-get method, it is very useful: http://sass-lang.com/documentation/Sass/Script/Functions.html#map_get-instance_method
This is an example of mixin. I pass as argument also your key: maybe it is better because you can check also others key names, if you need it:
$my-map-1: (
map-1-color-1: #506c89,
map-1-color-2: #737373,
map-1-color-3: #2a3949,
map-1-color-4: #182028
);
$my-map-2: (
map-2-color-1: #fff,
map-2-color-2: #000,
map-2-color-3: #ddd,
map-2-color-4: #ccc
);
#mixin color-map($color-map, $key-map) {
#each $key, $value in $color-map {
#if($key == $key-map) {
.my-class{
color: map-get($color-map, $key);
}
}
}
}
#include color-map($my-map-1, map-1-color-1);
Your output will be:
.my-class {
color: #506c89;
}

SCSS Variable Name Interpolation

I'm trying to create a bunch of buttons using a loop and a map in SCSS, but I don't know if the following is possible. I couldn't find a direct answer when I searched for this.
Does anyone know if I can do this? And if I can, what in my syntax is wrong?
$button-variants:(
primary: 'primary',
secondary: 'secondary',
positive: 'positive',
negative: 'negative',
warning: 'warning'
);
#each $key,$val in $button-variants {
.c-button--#{$key} {
#include button($color-#{$key}, $color-#{$key}-highlight, true);
}
}
I'm receiving an error:
Error: Undefined variable: "$color-".
on line 54 of source/css/scss/04-components/_button.scss
>> #include button($color-#{$key}, $color-#{$key}-highlight, true);
Try something analogous to this:
/* Define the Sassy Map called $icons */
$icons: (
checkmark: a,
plus: b,
minus: c
);
/* For each key in the map, create a class */
#each $name, $value in $icons {
.icon--#{$name} {
content: $value;
}
}
And you'll get the following result:
/* For each key in the map, created a class */
.icon--checkmark {
content: "a";
}
.icon--plus {
content: "b";
}
.icon--minus {
content: "c";
}

Change Variable colors if else in scss

I am trying to change color on click of change contrast (adding change-contrast class to body) in scss.
my static colors would be :
$primary-color-blue: #2aace2;
$mid-blue-color: #2695d2;
$dark-blue-color: #124b62;
on change contrast :
$primary-color-blue: #177eab;
$mid-blue-color: #1c6f9b;
$dark-blue-color: #124b62;
should be something like in sass
if(change-contrast) {
// console.log (get High Constrast Colors)
}
else {
// console.log (Static Colors)
}
Iam trying to do like below, but if i do like below i have to add to each and every class.
#mixin branding {
#each $brand in $brand_clr {
&.#{nth($brand,1)} {
$primary-color-blue: #177eab;
#content;
}
}
}
test {
background: $primary-color-blue;
#include branding {
background: $primary-color-blue;
}
}
Appreciate your help on this !!
Thanks
I think the solution comes across a well structured color map. You need to group colors by types (eg. static, contrasted). Then, use a mixin to avoid duplicating youself.
$colors:(
standard: (
primary-color-blue: #2aace2,
mid-blue-color:#2695d2,
dark-blue-color:#124b62
),
contrasted: (
primary-color-blue: #177eab,
mid-blue-color:#1c6f9b,
dark-blue-color:#124b62
)
);
#function get-color($key, $type: 'standard'){
#each $name, $color in map-get($colors, $type){
#if($key == $name){
#return $color
}
}
}
#mixin get-color($property,$color){
#{$property}: get-color($color);
#at-root body.contrasted & {
#{$property}: get-color($color, contrasted);
}
}
.className{
#include get-color(background-color, primary-color-blue)
}
This will output:
.className {
background-color: #2aace2;
}
body.contrasted .className {
background-color: #177eab;
}

Resources