Set a variable in Sass depending on the selector

50,219

Solution 1

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);
}

Solution 2

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.

Solution 3

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.

Solution 4

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;
  }
}

Solution 5

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);
}
Share:
50,219
Jrn
Author by

Jrn

Front-end developer & Drupal themer

Updated on July 09, 2022

Comments

  • Jrn
    Jrn almost 2 years

    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.

  • Jrn
    Jrn over 10 years
    Allright, I was afraid I had to do it like this. Thanks for the help, appreciated.
  • cimmanon
    cimmanon over 10 years
    What developer is going to remember what order their colors are in? This is an extremely impractical solution.
  • Jrn
    Jrn over 10 years
    Thanks for your answer and the links, both very intresting read. Appreciated.
  • Tim Hartmann
    Tim Hartmann about 10 years
    @cimmanon Right. But if you think forward you can now use a for-loop to create classes for each color :) I wanted to show only one way which is possible
  • iamdash
    iamdash about 10 years
    Thank you very much. I had the daunting task of applying eight different colour schemes to the same page, and has saved me a lot of time. Hacky, but it works. Cheers
  • fearis
    fearis over 8 years
    Is there a chance to change 'global' variable by class ? $color: #666; body.class-1 { $color : #fff; } p { $color; }
  • Koen
    Koen over 7 years
    This just overrides $color1 unconditionally from that point. If it works for you, I guess it is coincidental.
  • Sato
    Sato over 7 years
    this is about colors and not about fonts. So not relevant for this question.
  • Naveed
    Naveed over 7 years
    @Sato question is about using variable to set values of css properties, color or fonts or whatever.
  • Sato
    Sato over 7 years
    Valid, but still some more documentation about your suggested answer would be welcome
  • Samuel Willems
    Samuel Willems about 6 years
    Actually the question is about using variables outside of their scope. Which I presume would be possible using the !global flag (added March 2014). Edit: Nope, it is not.
  • Thony
    Thony over 3 years
    2020: Sass variables still won't work alone, but you may put a css variable inside a sass variable and have it change depending of body classes, thus achieving OP's needs. see my answer : stackoverflow.com/a/62873203/1589851
  • Rvervuurt
    Rvervuurt almost 3 years
    This is definitely the best answer. Mixins works fine, but this is exactly as OP wanted it.
  • Leon Vuković
    Leon Vuković almost 3 years
    Awesome! Thanks, +1
  • danday74
    danday74 over 2 years
    excellent answer - ignore everything else and use this!
  • Alexandre Martini
    Alexandre Martini over 2 years
    The only problem with this approach is that you can't use something like background-color: darken($primary-color, 10%), because technically $primary-color is not a color.
  • Thony
    Thony over 2 years
    @AlexandreMartini I guess you could use another css trick to circumvent that, like to simulate darken => filter: brightness(0.x); or you could darken it before assigning the color to a css variable
  • Mo1
    Mo1 about 2 years
    Thanks, @Thony. This a great solution IMO.