Scss array branding stylesheet - css

I wanted to use a Scss solution to easily modify styles on a website with multiple branded sub sites. My aim was to get arrays for elements (classes, ids, tags) which share styling properties and just have those arrays to be edited when adjusting brand styles.
I came up with the following solution which works fine, however... Couldn't the following be done in a smarter way? i.e. less chunks of "$array... + $all.. + #each"? Hope you know what I mean when you see my current syntax:
// All elements with background colors
$arrayElementsWithBackgroundColor: (
'.example-1',
'.example-2' // etc.
);
$allElementsWithBackgroundColor: ();
#each $item in $arrayElementsWithBackgroundColor {
$allElementsWithBackgroundColor: append($allElementsWithBackgroundColor, unquote('#{$item}'), 'comma');
};
// All elements with background gradients
$arrayElementsWithBackgroundColorGradient: (
'.example-3',
'.example-4' // etc.
);
$allElementsWithBackgroundColorGradient: ();
#each $item in $arrayElementsWithBackgroundColorGradient {
$allElementsWithBackgroundColorGradient: append($allElementsWithBackgroundColorGradient, unquote('#{$item}'), 'comma');
};
// All elements with semi-transparent background colors
$arrayElementsWithBackgroundColorSemiTransparent: (
'.example-5',
'.example-6' // etc.
);
$allElementsWithBackgroundColorSemiTransparent: ();
#each $item in $arrayElementsWithBackgroundColorSemiTransparent {
$allElementsWithBackgroundColorSemiTransparent: append($allElementsWithBackgroundColorSemiTransparent, unquote('#{$item}'), 'comma');
};
// All elements with border colors
$arrayElementsWithBorderColor: (
'.example-7',
'.example-8' // etc.
);
$allElementsWithBorderColor: ();
#each $item in $arrayElementsWithBorderColor {
$allElementsWithBorderColor: append($allElementsWithBorderColor, unquote('#{$item}'), 'comma');
};
// All elements with text colors
$arrayElementsWithTextColor: (
'.example-9',
'.example-10' // etc.
);
$allElementsWithTextColor: ();
#each $item in $arrayElementsWithTextColor {
$allElementsWithTextColor: append($allElementsWithTextColor, unquote('#{$item}'), 'comma');
};
Those chunks are where I collect my arrays of elements to be branded. Afterwards I'm using those like this:
body {
#at-root #{&}.brand-1 {
#{$allElementsWithBackgroundColor} { background: $brand-1; }
#{$allElementsWithBackgroundColorGradient} { background: $brand-1-gradient; }
#{$allElementsWithBackgroundColorSemiTransparent} { background: rgba($brand-1,0.8); }
#{$allElementsWithBorderColor} { border-color: $brand-1; }
#{$allElementsWithTextColor} { color: $brand-1; }
}
#at-root #{&}.brand-2 {
#{$allElementsWithBackgroundColor} { background: $$brand-2; }
#{$allElementsWithBackgroundColorGradient} { background: $$brand-2-gradient; }
#{$allElementsWithBackgroundColorSemiTransparent} { background: rgba($$brand-2,0.8); }
#{$allElementsWithBorderColor} { border-color: $$brand-2; }
#{$allElementsWithTextColor} { color: $$brand-2; }
}
#at-root #{&}.brand-3 {
#{$allElementsWithBackgroundColor} { background: $brand-3; }
#{$allElementsWithBackgroundColorGradient} { background: $brand-3-gradient; }
#{$allElementsWithBackgroundColorSemiTransparent} { background: rgba($brand-3,0.8); }
#{$allElementsWithBorderColor} { border-color: $brand-3; }
#{$allElementsWithTextColor} { color: $brand-3; }
}
#at-root #{&}.brand-4 {
#{$allElementsWithBackgroundColor} { background: $brand-4; }
#{$allElementsWithBackgroundColorGradient} { background: $brand-4-gradient; }
#{$allElementsWithBackgroundColorSemiTransparent} { background: rgba($brand-4-alt,0.8); }
#{$allElementsWithBorderColor} { border-color: $brand-4; }
#{$allElementsWithTextColor} { color: $brand-4; }
}
}

You could consider using maps – like:
// _brands.scss
$brand-1: red;
$brand-2: green;
$brand-3: orange;
$brand-4: blue;
$brand-1-gradient: linear-gradient(to top, red, red);
$brand-2-gradient: linear-gradient(to top, green, green);
$brand-3-gradient: linear-gradient(to top, orange, orange);
$brand-4-gradient: linear-gradient(to top, blue, blue);
$brands: (
brand-1 : (
// note! you can add more properties to each style map
'.example-1, .example-2': (background: $brand-1, color: magenta ),
'.example-3, .example-4': (background: $brand-1-gradient ),
'.example-5, .example-6': (background: rgba($brand-1, 0.8) ),
'.example-7, .example-8': (border-color: $brand-1 ),
'.example-9, .example-10': (color: $brand-1 )
),
brand-2 : (
'.example-1, .example-2': (background: $brand-2 ),
'.example-3, .example-4': (background: $brand-2-gradient ),
'.example-5, .example-6': (background: rgba($brand-2, 0.8) ),
'.example-7, .example-8': (border-color: $brand-2 ),
'.example-9, .example-10': (color: $brand-2 )
),
brand-3 : (
'.example-1, .example-2': (background: $brand-3 ),
'.example-3, .example-4': (background: $brand-4-gradient ),
'.example-5, .example-6': (background: rgba($brand-3, 0.8) ),
'.example-7, .example-8': (border-color: $brand-3 ),
'.example-9, .example-10': (color: $brand-3 )
),
brand-4 : (
'.example-1, .example-2': (background: $brand-4 ),
'.example-3, .example-4': (background: $brand-4-gradient ),
'.example-5, .example-6': (background: rgba($brand-4, 0.8) ),
'.example-7, .example-8': (border-color: $brand-4 ),
'.example-9, .example-10': (color: $brand-4 )
)
);
// brands.scss
#import '_brands.scss'
body {
#each $brand, $selectors in $brands {
#at-root #{&}.#{$brand} {
#each $selector, $style in $selectors {
#{$selector}{
#each $property, $value in $style {
#{$property}: $value;
}
}
}
}
}
}
You could also chose to split each brand into individual stylesheets using a mixin
// add to _brand.scss
#mixin brand($brand-name) {
body {
#at-root #{&}.#{$brand-name} {
#each $selector, $style in map-get($brands, $brand-name) {
#{$selector}{
#each $property, $value in $style {
#{$property}: $value;
}
}
}
}
}
}
// brand1.scss
#import '_brands.scss';
#include brand(brand-1);
// brand2.scss
#import '_brands.scss';
#include brand(brand-2);
// brand3.scss
#import '_brands.scss';
#include brand(brand-3);
// brand4.scss
#import '_brands.scss';
#include brand(brand-4);

Related

How to change sass import file when body class is changing

Is any way to change scss file with color variables when class in body is changing?
I mean when I have in html:
<body class="custom"> ... </body>
I want to load in style.scss
#import 'custom';
and when I have
<body class="dark-mode"> ... </body>
I want to load in style.scss
#import 'dark-mode';
You can not make an #import depending on a condition, but there is a ton of possible other approaches to this. Here is a small framework I wrote back then.
#function keyFilter($iKey, $fKey, $rKey) {
#if ($iKey == $fKey) {
#return $rKey;
}
#return $iKey;
}
#function v($key) {
#return var(--#{$key});
}
//
$modes: (
"&": (
"color": #000,
),
"dark": (
"color": #fff,
),
);
//
#each $key, $map in $modes {
body#{keyFilter("[#{$key}]", "[&]", null)} {
#each $key, $value in $map {
--#{$key}: #{$value};
}
}
}
To "register" a new mode just nest another map in the $modes-map, you can add as many modes as you want. Keep in mind the "&"-mode represents the default-mode.
$modes: (
"&": (
//...
),
"dark": (
//...
),
//...
);
To register a new mode-depending variable just simply enter key and value to the respective mode.
$modes: (
"&": (
"color": #000,
"bgc": #fff,
"bgc-contrast": #eee,
//...
),
"dark": (
"color": #fff,
"bgc": #000,
"bgc-contrast": #424242,
//...
),
);
To call a variable just use the v($key) function.
body {
color: v(color);
background-color: v(bgc);
}
div.contrasted {
background-color: v(bgc-contrast);
}
This compiling to:
body {
--color: #000;
--bgc: #fff;
--bgc-contrast: #eee;
}
body[dark] {
--color: #fff;
--bgc: #000;
--bgc-contrast: #424242;
}
body {
color: var(--color);
background-color: var(--bgc);
}
div.contrasted {
background-color: var(--bgc-contrast);
}
Note: you do not need to declare each variable for each mode. If a variable wasn't found for the current mode, this won't throw an error.For Example: This...
$modes: (
"&": (
//...
),
"dark": (
"color": #fff,
"bgc": #000,
"bgc-contrast": #424242,
//...
),
);
//...
body {
color: v(color);
background-color: v(bgc);
}
div.contrasted {
background-color: v(bgc-contrast);
}
... is totally fine.

SCSS - How to loop nested maps within map-merge?

I have a nicely working SCSS function with which I can call any color like so:
color: clr(milk);
Now I would like to make some kind of loop within the map-merge so that when, for example I add a new nested map called 'tertiary' with some other colors, the colors automatically become available without having to add
map-get(colors, 'tertiary')
to the map-merge. Does anyone know how to do this? Below is my current function:
$colors: (
primary: (
milk: #fff,
cola: #000,
mine-shaft: #232323,
),
secondary: (
pampas: #f4f1ef,
pearl-brush: #e9e2dd,
alto: #ddd,
),
);
// Color generation
#function clr($color) {
$color: map-get(map-merge(map-get($colors, 'primary'), map-get($colors, 'secondary')), $color);
#return $color;
}
Nice project! map-merge() can be indeed used to achieve what you want, however it adds an unnecessary step. I'd recommend to simply use a nested loop coupled with an #if statement.
#function clr($find) {
#each $colorCategoryName, $colorCategory in $colors {
#each $colorName, $color in $colorCategory {
#if $find == $colorName {
#return $color;
}
}
}
}
For example: This...
$colors: (
"primary": (
"milk": #fff,
"cola": #000,
"mine-shaft": #232323,
),
"secondary": (
"pampas": #f4f1ef,
"pearl-brush": #e9e2dd,
"alto": #ddd,
),
);
#function clr($find) {
#each $colorCategoryName, $colorCategory in $colors {
#each $colorName, $color in $colorCategory {
#if $find == $colorName {
#return $color;
}
}
}
}
body {
color: clr(cola);
background-color: clr(pampas);
}
Will output:
body {
color: #000;
background-color: #f4f1ef;
}
Let me know if this isn't what you were looking for!

scss interpolation help for passing the values

I need to create dynamic classes for which I am creating a scss code to create the classes for all the possible values. Below is my code:-
$colors: (
"black": "0,0,0",
"white": "255,255,255",
"red" : "255,0,0"
);
$opacity:9;
#for $i from 0 through $opacity {
$j:$i/10;
#each $color, $rgb in $colors {
$rgba: "#{$rgb},#{$j}";
.background-#{$color}-#{$i} {
background: #{$rgba};
}
}
}
I want it to give out put as :-
.background-black-0 {
background: rgba(0,0,0,0);
}
.background-white-0 {
background: rgba(255,255,255,0);
}
.background-red-0 {
background: rgba(255,0,0,0);
}
.background-black-1 {
background: rgba(0,0,0,0.1);
}
.background-white-1 {
background: rgba(255,255,255,0.1);
}
.background-red-1 {
background: rgba(255,0,0,0.1);
}
struggling with the interpolation for rgba(). Otherwise its getting the exact values I want. If you check my code in https://www.sassmeister.com/ you will see it.
You could use directly your colors as rgb color in your map and then add opacity in your #for loop:
$colors: (
"black": rgb(0,0,0),
"white": rgb(255,255,255),
"red": rgb(255,0,0)
);
$opacity:9;
#for $i from 0 through $opacity {
$j:$i/10;
#each $color, $rgb in $colors {
.background-#{$color}-#{$i} {
background: rgba($rgb, $j);
}
}
}

How to Change High Contrast Background Images using SASS

I am trying to make a mixin for standard background images and high contrasted background images, not sure how do i do it. can someone pl. shade some light
I have a mixin for text color and background color :
$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)
}
output :
.className {
background-color: #2aace2;
}
body.contrasted .className {
background-color: #177eab;
}
Thanks !!
EDITED AS PER ANSWER : Edited with extra color / transparent variable, but throws error Edited with extra color / transparent variable, but throws error
$images:(
standard: (
pdp-more-icon-mob: white + $img_file_path + 'product-detail-page/Product-surface-display/more-icon/More-Mobile.png',
pdp-more-icon-mob-retina : #f00 + $img_file_path + 'product-detail-page/Product-surface-display/more-icon/More-Mobile__2x.png'
),
contrasted: (
pdp-more-icon-mob: green + $img_file_path + 'product-detail-page/Product-surface-display/more-icon/More-Desktop-High-Contrast.png',
pdp-more-icon-mob-retina : $green + $img_file_path + 'product-detail-page/Product-surface-display/more-icon/More-Desktop-High-Contrast__2x.png'
)
);
#function get-images($key, $type: 'standard'){
#each $name, $image in map-get($images, $type){
#if($key == $name){
#return $image
}
}
}
#mixin get-images($property, $color, $image, $retina:false){
#{$property}: get-images($color + $image);
#at-root body.contrasted & {
#{$property}: get-images($color + $image, contrasted);
}
#if($retina){
#media (-webkit-min-device-pixel-ratio: 2){
#{$property}: get-images($color + $image#{-retina});
#at-root body.contrasted & {
#{$property}: get-images($color + $image#{-retina}, contrasted);
}
}
}
}
.className{
#include get-images(background, color, image, true);
}
Without refactoring the mixin is possible to use it this way—by adding your images properly in your map:
$colors:(
standard: (
primary-color-blue: #2aace2,
mid-blue-color:#2695d2,
dark-blue-color:#124b62,
image: 'img.png',
image-retina: 'img-retina.png'
),
contrasted: (
primary-color-blue: #177eab,
mid-blue-color:#1c6f9b,
dark-blue-color:#124b62,
image: 'img2.png',
image-retina: 'img2-retina.png'
)
);
#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-image, image);
#media (-webkit-min-device-pixel-ratio: 2){
#include get-color(background-image, image-retina)
}
}
But you can also adapt this mixin for images and do so:
$images:(
standard: (
image: 'img.png',
image-retina: 'img-retina.png'
),
contrasted: (
image: 'img2.png',
image-retina: 'img2-retina.png'
)
);
#function get-images($key, $type: 'standard'){
#each $name, $image in map-get($images, $type){
#if($key == $name){
#return $image
}
}
}
#mixin get-images($property,$image, $retina:false){
#{$property}: get-images($image);
#at-root body.contrasted & {
#{$property}: get-images($image, contrasted);
}
#if($retina){
#media (-webkit-min-device-pixel-ratio: 2){
#{$property}: get-images($image#{-retina});
#at-root body.contrasted & {
#{$property}: get-images($image#{-retina}, contrasted);
}
}
}
}
.className{
#include get-images(background-image, image, true);
}
Where the true in 'get-images' is calling to image + '-retina' in the $images map, and outputs:
.className {
background-image: "img.png";
}
body.contrasted .className {
background-image: "img2.png";
}
#media (-webkit-min-device-pixel-ratio: 2) {
.className {
background-image: "img-retina.png";
}
body.contrasted .className {
background-image: "img2-retina.png";
}
}

SASS – looping through map via mixin doesn’t compile CSS

what I have is a simple SASS color map:
$brand_col: (
def: blue,
mus: red,
ser: yellow
);
The following:
#each $brand, $col in $brand_col {
body.#{$brand} {
background: $col;
}
}
leads to expected output:
body.def { background: blue; }
body.mus { background: red; }
body.ser { background: yellow; }
When I try to put the same thing into a mixin like so:
$color: null;
#mixin branding {
#each $brand, $col in $brand_col {
&.#{$brand} {
$color: $col;
#content;
}
}
}
.body { #include branding { background: $color; } }
I would expect the same output, but nothing is getting compiled at all. I copied the mixin from a sass specific site and don’t fully understand the whole process. Any hints what I'm doing wrong?
Thanks
Ralf
To achive the same result as in your first example, have two options:
Option 1
Make a simple non-reusable mixin:
$brand_col: (
def: blue,
mus: red,
ser: yellow
);
#mixin branding {
#each $brand, $col in $brand_col {
&.#{$brand} {
background: $col;
}
}
}
.body {
#include branding;
}
This will compile to:
.body.def {
background: blue;
}
.body.mus {
background: red;
}
.body.ser {
background: yellow;
}
Option 2
Make a reusable mixin, so you can pass the color map to apply:
$brand_colors: (
def: blue,
mus: red,
ser: yellow
);
#mixin branding($colors) {
#each $class, $color in $colors {
&.#{$class} {
background: $color;
}
}
}
.body {
#include branding($brand_colors);
}
// Latter you can use it to apply the same 'branding' for any other element
div {
#include branding($brand_colors);
}
Will compile to:
.body.def {
background: blue;
}
.body.mus {
background: red;
}
.body.ser {
background: yellow;
}
div.def {
background: blue;
}
div.mus {
background: red;
}
div.ser {
background: yellow;
}
You could even implement a second parameter to the mixin to specify which css property you want to apply, with background as a default:
#mixin branding($colors, $property: background) {
#each $class, $color in $colors {
&.#{$class} {
#{$property}: $color;
}
}
}
// Latter you can use it to apply the same 'branding' for any other element and property
h1 {
#include branding($brand_colors, color);
}
Will compile to:
h1.def {
color: blue;
}
h1.mus {
color: red;
}
h1.ser {
color: yellow;
}
You can find out more about mixins here.
Hope it helps!
What do you mean by $color: $col;? no such property like "null" in CSS, because when you set $color: null at top and then trying to set a property $color: $col; you actually trying to set like that null: blue; this is nothing mean anything to compiler.
I think you no need #content directive use here. You should try just following way:
$brand_col: (
def: blue,
mus: red,
ser: yellow
);
#mixin branding {
#each $brand, $col in $brand_col {
&.#{$brand} {
background: $col;
}
}
}
.body { #include branding(); }

Resources