Button-Color based on className in scss - css

Is there a neat, simple and beautiful way to implement something like this in scss:
&.red{
--color:red;
}
&.green{
--color:green;
}
&.blue{
--color:blue;
}
&.yellow{
--color:yellow;
}
[...]
background-color:var(--color);
i was thinking something like this:
&.$color{
--color:$color
}
im really new into scss and wanted to know if this is possible in some way.

It sounds like you want to loop through a list of colors to generate new classes, which is definitely doable with proper interpolation:
.button {
$colors: red, green, blue, yellow; // create a list of colors
#each $color in $colors { // loop through each value of a list
&.#{$color} { // need interpolation to use a sass variable in a css selector
--color: #{$color}; // need interpolation for css variable assignment
color: $color; // do not need interpolation for other css properties.
}
}
}
That will output the following compound selectors, based on the $colors list.
.button.red {
--color: red;
color: red;
}
.button.green {
--color: green;
color: green;
}
.button.blue {
--color: blue;
color: blue;
}
.button.yellow {
--color: yellow;
color: yellow;
}
However, since you're already generating classes from a list, I'd suggest creating new class names rather than compound selectors, since these increase specificity. By changing &.#{$color} to &--#{$color} in the loop above, you can get the following BEM-style classes:
.button--red {
--color: red;
color: red;
}
.button--green {
--color: green;
color: green;
}
/* etc. */

Related

Exclude elements from group in SCSS

Imagine a set of rules like the ones shown below:
span, div { color: red; }
span { background: white; }
div { background: black; }
Is it possible to wrap them under 1 SCSS rule? Something in the form of:
span, div {
& { color: red; }
&:not(div) { background: white;}
&:not(span) { background: black; }
}
Unfortunately an approach like this could very easily get quite large. So I'm hoping for an SCSS implementation of the code shown at the top but without the use of :not(<every other selector>).
Preferably something looking like (invalid code):
span, div {
& { color: red; }
&(span) { background: white;}
&(span) { background: black; }
}
I don't think that it is possible to do what you want this way (but I may be wrong).
The code below achieve the result you are looking for but uses a map, a #mixin and #extend instead of a single selector. Maybe it's a bit too complex for want you want to achieve but I hope it can help:
#mixin setSelectors($elements) {
%commonProperties {
#content;
}
#each $selector, $properties in $elements {
#{$selector} {
#extend %commonProperties;
#each $property, $value in $properties {
#{$property}: #{$value};
}
}
}
}
#include setSelectors((
span: (background: white),
div: (background: black)
)) {
color: red; // Common properties
}
Will return:
div, span { color: red; }
span { background: white; }
div { background: black; }
The first argument is a map containing all your selectors and their specific properties. The #content of the #mixin contains shared properties.
If you need to add a selector that doesn't have any specific property, you can add it to the map with null as key. Such as:
#include setSelectors((
span: (background: white),
div: (background: black),
i: null
)) {
color: red;
}
However, this solution doesn't allow nested selectors so I believe that separating the selectors is the best way to go.

Set a SASS variable depending

I'm searching a way to use a particular color depending on a class on the body tag.
I have a main scss file like this
// variables.scss
$bg-main: white;
$color-first: red;
$color-second: green;
And in my other files, I use the colors
// content.scss
.content {
.some-selector: {
// some styles
color: $color-second;
}
a:hover {
// some styles
color: $color-second;
}
}
// and same goes for menu.scss etc.
Now I have a dynamic class on the body, that changes depending on the current selected menu. I would like $color-second to be different for each body classes, and I don't know how to do that. The only solution I found was to move all the $color-second from each files into one single file, like this:
.body-1 {
.content a:hover, .content .some-selector {
color: green;
}
}
.body-2 {
.content a:hover, .content .some-selector {
color: blue;
}
}
.body-1 {
.content a:hover, .content .some-selector {
color: black;
}
}
So I don't need to write the color in each files. This works well, but if I need to set this $color-second to some other selector, I need to put that in this big file.
Is this possible to do this an other way?
I already checked these answers, but it didn't helped me much:
SASS set variable depending on CSS class
Creating or referencing variables dynamically in Sass
Merge string and variable to a variable with SASS
There are multiple ways to do this. The most obvious two which come to mind are mixins and loops:
Mixins
Just put everything you want into a single mixin, and then use it for every body class:
#mixin colored-content($color) {
.content a:hover, .content .some-selector {
color: $color;
}
/* Any other rules which use $color here */
}
.body-1 {
#include colored-content(green);
}
.body-2 {
#include colored-content('#FF0000');
}
.body-3 {
#include colored-content(darken(red, 20));
}
You can extend this example with any number of arguments (for example, $textColor and $bgColor), conditions or rules.
With this approach you will not have SCSS code repetitions, and any updates will be introduced easily.
Loop
Another way is to use a simple loop:
$body_themes: (
"body-1": green,
"body-2": #FF0000,
"body-3": darken(red, 2)
);
#each $body_class, $color in $body_themes {
.#{$body_class} {
.content a:hover, .content .some-selector {
color: $color;
}
/* Any other rules which use $color here */
}
}
It is even shorter, but imho it is less readable.
P.S. It is possible to combine mixins and loops, by the way :)

Set a variable in Sass depending on the selector

I’ve got a website that’s using a few different ‘main’ colors. The general HTML layout stays the same, only the colors change depending on the content.
I was wondering if I could set a color variable depending on the CSS selector. This way I can theme my website with a few variables and let Sass fill in the colors.
For example:
$color-1: #444;
$color-2: #555;
$color-3: #666;
$color-4: #777;
body.class-1 {
color-default: $color-1;
color-main: $color-2;
}
body.class-2 {
color-default: $color-3;
color-main: $color-4;
}
/* content CSS */
.content {
background: $color-default;
color: $color-main;
}
I was thinking of using a mixin for this, but I was wondering if there’s a better way to do this—with a function maybe? I’m not that great with Sass, so any help would be appreciated.
I think a mixin is the answer. (As I wrote, variables won’t work.)
#mixin content($color-default, $color-main) {
background: $color-default;
color: $color-main;
}
body.class-1 {
#include content(#444, #555);
}
body.class-2 {
#include content(#666, #777);
}
That SCSS compiles to this CSS:
body.class-1 {
background: #444444;
color: #555555; }
body.class-2 {
background: #666666;
color: #777777; }
If you wanted to group the color values together in your SCSS file, you could use variables in conjunction with the mixin:
$color-1: #444;
$color-2: #555;
$color-3: #666;
$color-4: #777;
body.class-1 {
#include content($color-1, $color-2);
}
body.class-2 {
#include content($color-3, $color-4);
}
as sass documentation explain nicely (https://sass-lang.com/documentation/variables):
Sass variables are all compiled away by Sass. CSS variables are included in the CSS output.
CSS variables can have different values for different elements, but Sass variables only have one value at a time.
Sass variables are imperative, which means if you use a variable and then change its value, the earlier use will stay the same. CSS variables are declarative, which means if you change the value, it’ll affect both earlier uses and later uses.
We may take advantage of that using a combination of sass and css variables to achieve what you want:
//theme colors
$red-cosmo: #e01019;
$green-cosmo: #00c398;
$primary-color: var(--primary-color);
body{
--primary-color: #{$red-cosmo};
}
body.univers-ride{
--primary-color: #{$green-cosmo};
}
So when I call my sass variable $primary-color, it will print as my css variable "var(--primary-color)" that will expand as $green-cosmo only if my body has the "univers-ride" class else it will be $red-cosmo the default color.
If you really want to get hacky you could also define your different color schemes in a single variable like $scheme1: class1 #333 #444, where the first value is always the name, and that is followed by all the colors in that scheme.
You can then use #each:
// Define your schemes with a name and colors
$scheme1: class1 #444 #555;
$scheme2: class2 #666 #777;
$scheme3: class4 #888 #999;
// Here are your color schemes
$schemes: $scheme1 $scheme2 $scheme3;
#each $scheme in $schemes {
// Here are the rules specific to the colors in the theme
body.#{nth($scheme, 1)} .content {
background-color: nth($scheme, 2);
color: nth($scheme, 3);
}
}
This will compile to:
body.class1 .content {
background-color: #444444;
color: #555555; }
body.class2 .content {
background-color: #666666;
color: #777777; }
body.class4 .content {
background-color: #888888;
color: #999999; }
Obviously if you don't want to combine body.class1 and .content in your selectors, you could just specify a mixin content($main, $default) and call it inside the #each using nth just like in the above code, but the point is you don't have to write out a rule for each of your classes.
EDIT There are lots of interesting answers on Creating or referencing variables dynamically in Sass and Merge string and variable to a variable with SASS.
You can also create a mixing that use the ampersand parent selector. http://codepen.io/juhov/pen/gbmbWJ
#mixin color {
body.blue & {
background: blue;
}
body.yellow & {
background: yellow;
}
}
UPDATE: its 2017 and variables does works!
#mixin word_font($page) {
#font-face {
font-family: p#{$page};
src: url('../../static/fonts/ttf/#{$page}.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
.p#{$page} {
font-family: p#{$page};
}
}
// Loop and define css classes
#for $i from 1 through 604 {
#include word_font($i);
}
If you don't want to use a variable for each color, you can use one variable for all kinds of colors. In the mixin you can choose the right color with nth. For instance, if you write the index of the color as 1, then you get the first color in the color variable.
$colors: #444, #555, #666, #777;
#mixin content($color-default-num, $color-main-num) {
background: nth($colors, $color-default-num);
color: nth($colors, $color-main-num);
}
body.class-1 {
#include content(1, 2);
}
For me the definite answer to my problem was creating a map of maps and loopig through them as follows:
$pallettes: (
light-theme: (
container-color: red,
inner-color: blue,
),
dark-theme: (
container-color: black,
inner-color: gray,
),
);
#each $pallette, $content in $pallettes {
.main.#{$pallette} {
background-color: map-get($content, container-color);
.inner-div {
background-color: map-get($content, inner-color);
}
}
}
You can simply override your scss variables inside of the class wrapper:
$color1: red;
$color2: yellow;
header { background: $color1; }
.override-class {
$color1: green;
header { background: $color1; }
}
Seems to work for me.

Variable scoping in SASS

Suppose I'd like to define a global value for a variable, then override it for specific selectors. According to the SASS documentation this should be possible.
Variables are only available within the level of nested selectors where they’re defined. If they’re defined outside of any nested selectors, they’re available everywhere.
Logic would then state that "green" and "red" as values for $text-color would only be available within their respective nested selectors, and that $text-color would take a value of "blue" when called anywhere else, such as within .foo.
$text-color: blue;
.green {
$text-color: green;
color: $text-color;
}
.red {
$text-color: red;
color: $text-color;
}
.foo {
color: $text-color;
}
This is not the case, however, and the above SASS compiles to:
.green {
color: green;
}
.red {
color: red;
}
.foo {
color: red;
}
Any help understanding this would be, well, helpful.
This is an old question, but for the record, as of version 3.4.0, variables are now block-scoped unless marked with the !global flag.
From the changelog:
All variable assignments not at the top level of the document are
now local by default. If there's a global variable with the same
name, it won't be overwritten unless the !global flag is used.
For example, $var: value !global will assign to $var globally.
This behavior can be detected using
feature-exists(global-variable-shadowing).
This means that this scss:
$color: red;
.sel {
$color: green;
color: $color;
}
.sel2 {
color: $color;
}
Will now produce the following css:
.sel {
color: green;
}
.sel2 {
color: red;
}
This is because once you assign a variable to be global, all further assignments to that variable are done globally too. If you want to make it local after that, you could do $local-text-color: $text-color; and then color: $local-text-color;

Scoping sass variable

Is there a way to add scope to sass variables?
I want to be able to attach a class to my body element. The class will refer to a set of colours that the rest of the stylesheets can access.
I have tried:
#mixin theme_one{
$color: #000;
}
.theme_one{
#include theme_one;
}
and
.theme_one{
$color: #000;
}
I've just come across the same issue myself. I wanted to have different colour themes for different sections of my site.
Using a mixin seems like the best way to go. It's nicely DRY, and easy to use. The trick is not setting your colours in your main styles blocks, but rather using only the mixin for this.
I've set up the theme colours as variables at the top so they can be edited nicely, and I've set them as lists so that multiple values can be passed without hordes of variable being defined.
So:
// Variable Definitions
$defaultColor: black white grey;
$color2: blue green brown;
$color3: red white blue;
#mixin colorSet($color: $defaultColor) {
$link: nth($color, 1);
$border: nth($color, 2);
$background: nth($color, 3);
border-color: $border;
background-color: $background;
.column {
border-color: lighten($border, 10%);
}
a {
color: $link;
&:hover {
color: darken($link, 15%);
}
}
}
// Default colours
body {
#include colorSet();
}
// Scoped colours
.my-theme-3 {
#include colorSet($color3);
}
.my-theme-2 {
#include colorSet($color2);
}
Will produce something like this:
body {
border-color: white;
background-color: grey; }
body .column {
border-color: white; }
body a {
color: black; }
body a:hover {
color: black; }
.my-theme-3 {
border-color: white;
background-color: blue; }
.my-theme-3 .column {
border-color: white; }
.my-theme-3 a {
color: red; }
.my-theme-3 a:hover {
color: #b30000; }
.my-theme-2 {
border-color: green;
background-color: brown; }
.my-theme-2 .column {
border-color: #00b300; }
.my-theme-2 a {
color: blue; }
.my-theme-2 a:hover {
color: #0000b3; }
Edit: Updated to use default mixin values.
In your case no need to use mixin, If you have set of many styles then use mixin,
ie. if you have
#mixin theme_one{
$color: #000;
height: 50px;
}
then use Mixin
otherwise for single property use only variable
$color: #fff;
.some_class01{
color: $color;
background: $color;
}
.some_class22{
border-color: $color;
}
IMP: Variable should assign at the top of your code, it means don't use it after/below where you assigned it :)
Not sure if this is what you are looking for. It looks like you may have tried something similar to this,
which should probably work. (it may just be a matter of using !default)
Your body tag with a class on it..
<body class="theme_one">
</body>
Sass variables defined in stylesheet..
//THEME ONE VARIABLES
.theme_one{
$borderColor:#333 !default;
$fontColor:#999 !default;
}
//THEME TWO VARIABLES
.theme_two{
$borderColor:#CCC !default;
$fontColor:#000 !default;
}
Pre-existing CSS which will be overwritten depending on which class is used on the body tag.
h1.someheader {
color:$fontColor;
border-bottom:1px solid;
border-color:$borderColor;
}
Otherwise you could maybe try something like this. It looks like you may have tried something similar, however there seems to be an error with your mixin ... see note below.
//mixin used to set variables for properties
#mixin themeOne($fontColor,$borderColor) {
color:$fontColor;
border-color:$borderColor;
}
#include themeOne(#000,#CCC);
Pre-existing CSS
h1.someheader {
color:$fontColor
border-color:$borderColor;
border-bottom:1px solid;
}
Also note in your mixin example you are using $color:#000; ... This won't be interpreited properly as it should be color:#000; You can't use variables as selectors
unless you do something like #{$color}:#000;
I haven't quite tested this yet, so some things might need to be adjusted. If this doesn't solve your problem I hope it at least gives you some ideas.

Resources