38

I have defined a color in my root:

:root {
--purple: hsl(266, 35%, 70%);
}

And I'm trying to use it in a SASS function to give it transparency:

.purple {
  background: transparentize(#{"var(--primary-color)"}, 0.7)
}

Does anyone know of a way to get this to work? Or is it just not possible?

TylerH
  • 20,799
  • 66
  • 75
  • 101
sammiepls
  • 1,340
  • 2
  • 13
  • 19
  • 5
    Give a read : https://codepen.io/jakealbaugh/post/css4-variables-and-sass – Aravind S Aug 17 '18 at 04:40
  • 5
    Good link! OP: not possible in general because CSS Variables are dynamic: their values are calculated each time they're used. No way Sass can know for sure which value they may have at compile time. Sass variables have values known at compile time. If you're defining constants in Sass and using them to define CSS Variables in :root and not playing with latter in children node, then you can use them in both. – FelipeAls Aug 17 '18 at 13:55

8 Answers8

17

Global variables can be defined outside of an element in a :root pseudo-class:

:root {
  --color-primary: #FFFFFF;
}

you can define a function like this:

@function color($color-name) {
  @return var(--color-#{$color-name});
}

and call it into your sass:

body { 
  color: color(primary); 
}

compiled sass code is:

body { 
  color: var(--color-primary); 
}
maxshuty
  • 9,708
  • 13
  • 64
  • 77
Keivan Sina
  • 608
  • 5
  • 21
11
#{var(--variablename)}

This is how you use CSS variables in SCSS

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Shubham Gaikwad
  • 553
  • 5
  • 4
  • 6
    It should not. You can use CSS variables in SCSS like that, but not in SCSS functions. And the question is specifically about functions. `transparentize(#{var(--someColor)}, 0.7)` will not work (for the reasons mentioned in the comments and other answers) – Romalex Nov 01 '22 at 23:56
8

If you are looking for a way to adjust colors using sass color functions (I feel like this is the main reason why people get here), here is a solution for you. The idea is the same as in Juanete Montoya's answer, but it's pure sass/scss (no php):

First, we need to split a color into hsla values and save each one in a separate custom property.

@mixin define-color($title, $color) {
    --#{$title}-h: #{hue($color)};
    --#{$title}-l: #{lightness($color)};
    --#{$title}-s: #{saturation($color)};
    --#{$title}-a: #{alpha($color)};
}

Now we can put it back together, making some adjustments on the way.

@function color($title, $hue: 0deg, $lightness: 0%, $saturation: 0%, $alpha: 0) {
    @return hsla(
        calc(var(--#{$title}-h) + #{$hue}), 
        calc(var(--#{$title}-s) + #{$saturation}),
        calc(var(--#{$title}-l) + #{$lightness}),
        calc(var(--#{$title}-a) + #{$alpha}),
    );
}

Now we are ready to define some color variables...

:root {
    @include define-color("primary", #696969);
    @include define-color("secondary", blue);
}

use and adjust them!

.example-class {
    color: color("primary");
    background: color("secondary", $lightness: +20%, $alpha: -0.3);
    border: 1px solid color("primary", $hue: -30deg, $saturation: 5%);
}
Romalex
  • 1,582
  • 11
  • 13
6

Unfortunately, this is impossible. CSS variables are subsituted in for their values at runtime. The SASS compiler is just that -- a compiler, making it unable to read the value of CSS variables, since CSS variables can be defined in other files.

Harry Allen
  • 385
  • 4
  • 13
3

SASS transpiling is executed first, so it is better to set the SASS variable first and then connect it with the --css_variables.

To achieve that you need to @use "sass:meta"; like in this example:

@use "sass:meta";


//sass variable
$primarySassVariable: #4252af;


//css variable
:root {
  --primary: #{meta.inspect($primarySassVariable)};
}
Juanma Menendez
  • 17,253
  • 7
  • 59
  • 56
  • this seems like the right way to go, but your example also does not include the original questions intent: to use the variable in the `transparentize` function. It probably works since its just a SCSS variable now? But would be good to put it in the answer. But yes, if setting the css variable from SCSS is an option for people then this seems to bypass all the need to transform the variable. – Flion Mar 30 '23 at 06:45
  • Also, just thinking this through, if you go this route and you want to use the CSS variables in your components (again like in the OPs question) so that you can change it through JS... then the code that sets the CSS variable should be executed _after_ any SCSS code that may want to change the SCSS variables (when importing a theme for example and changing one value), but _before_ any SCSS code for your actual UI if that UI uses the CSS variables. Correct? Because once the bit that sets CSS variable is read & parsed, changing the SCSS variable after that would have no effect – Flion Mar 30 '23 at 07:03
3

Simple solution, needs two variables instead of one

Similar to Romalex's answer there is an elegant solution explained in this article: How to use CSS variables with Sass Mixins

You can define your color variable with RGB 3-number list, then you can manipulate that with Sass transparentize and other color functions.

:root {
  --color-primary: #16498a;
  --color-primary-rgb: 22, 73, 138;
}

h1 {
  color: var(--color-primary);
}

h2 {
  color: rgba(var(--color-primary-rgb), 0.4);
}

You can write your own Sass functions to deal with the doubling of the variables, like in the following example.

Complete solution with custom Sass functions

In this article you have such an example:

How to combine SASS color functions and CSS Variables

Example use:

:root {
  @include defineColorHSL(--color-primary, 220, 90%, 56%);
}

.component {
  color: alpha(var(--color-primary), 0.2); // it works 
}

Needed resources:

// return css color variable with different opacity value
@function alpha($color, $opacity){
  $color: str-replace($color, 'var(');
  $color: str-replace($color, ')');
  $color-h: var(#{$color+'-h'});
  $color-s: var(#{$color+'-s'});
  $color-l: var(#{$color+'-l'});
  @return hsla($color-h, $color-s, $color-l, $opacity);
}

// replace substring with another string
// credits: https://css-tricks.com/snippets/sass/str-replace-function/
@function str-replace($string, $search, $replace: '') {
  $index: str-index($string, $search);
  @if $index {
    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
  }
  @return $string;
}


@mixin defineColorHSL($color, $hue, $saturation, $lightness){
  #{$color}: unquote("hsl(#{$hue}, #{$saturation}, #{$lightness})");#{$color}-h: #{$hue};#{$color}-s: #{$saturation};#{$color}-l: #{$lightness};
}
prkos
  • 449
  • 4
  • 12
0

I realize that this isn't quite what the original question is asking, but I had a similar issue when trying to use a SASS variable to define a CSS variable. Based on initial research, I tried transparentize(#{$purple}, 0.7), which failed. Instead, the #{ } needed to surround the function:

$purple: #abc123;

:root {
  --purple: #{transparentize($purple, 0.7)};
}

Just wanted to share this given that I have yet to find any examples of CSS variables defined using functions and SASS variables in this way. For a moment, the answers here made me think it was not possible, but I misunderstood the question.

dannypernik
  • 172
  • 2
  • 11
  • SCSS compiles styles to CSS, so whatever is in the SCSS file, will be coded in the final file. Any SASS functions are calculated and applied during that compilation, so when it finishes, that's it. CSS variables are dynamically applied through CSS itself. This is why you cannot combine it in this manner, from your example. – Vladimir Jovanović May 22 '23 at 13:40
-3

My solution was transform HEX code color to HSL. First i've created a PHP Function.

function hexToHsl($hex) {
    $hex = str_split(ltrim($hex, '#'), 2);
    // convert the rgb values to the range 0-1
    $rgb = array_map(function($part) {
        return hexdec($part) / 255;
    }, $hex);

    // find the minimum and maximum values of r, g and b
    $min = min($rgb);
    $max = max($rgb);

    // calculate the luminace value by adding the max and min values and divide by 2
    $l = ($min + $max) / 2;
    if ($max === $min) {
        $h = $s = 0;
    } else {
        if ($l < 0.5) {
            $s = ($max - $min) / ($max + $min);
        } elseif ($l > 0.5) {
            $s = ($max - $min) / (2 - $max - $min);
        }
        if ($max === $rgb[0]) {
            $h = ($rgb[1]- $rgb[2]) / ($max -$min);
        } elseif ($max === $rgb[1]) {
            $h = 2 + ($rgb[2]- $rgb[0]) / ($max -$min);
        } elseif ($max === $rgb[2]) {
            $h = 4 + ($rgb[0]- $rgb[1]) / ($max -$min);
        }
        $h = $h * 60;
        if ($h < 0) {
            $h = $h + 360;
        }
    }

    return array($h, $s*100, $l*100);
}

it will receive the hex code color por example #cc66cc and will return an array of 3 values for H, S and L.

$color_main_hsl = hexToHsl($color_main);
$color_main_h = $color_main_hsl[0];
$color_main_s = $color_main_hsl[1] . '%';
$color_main_l = $color_main_hsl[2] . '%';

And then assign to a variable CSS.

<style>
    :root {
        --color_1: <?php echo $color_main; ?>;
        --color_1_h: <?php echo $color_main_h; ?>;
        --color_1_s: <?php echo $color_main_s; ?>;
        --color_1_l: <?php echo $color_main_l; ?>;
    }
</style>

Then i create 2 functions in SASSS, 1 for darken and other for lighten:

// hsl css variable darken
@function hsl_d($color_num, $percentage) {
    $color_h: var(--color_#{$color_num}_h);
    $color_s: var(--color_#{$color_num}_s);
    $color_l: var(--color_#{$color_num}_l);
    @return hsl($color_h, $color_s, calc(#{$color_l} - #{$percentage}#{'%'}));
}

// hsl css variable lighten
@function hsl_l($color_num, $percentage) {
    $color_h: var(--color_#{$color_num}_h);
    $color_s: var(--color_#{$color_num}_s);
    $color_l: var(--color_#{$color_num}_l);
    @return hsl($color_h, $color_s, calc(#{$color_l} + #{$percentage}#{'%'}));
}

And finally I use my sass function:

.element-to-dark {
    background-color: hsl_d(1, 12);
}

.element-to-light {
    background-color: hsl_d(1, 22);
}

The number "1" is because my variables ara called --color_1, --color_1_h, --color_1_s, --color_1_l and my function interpolate this number. The second parameter is the percentage for light or darken.

I hope I have contributed :)